Commit Graph
74 Commits
Author SHA1 Message Date
Peter Argany e94f2c3625 Adhere to main queue requirements of native modules
Summary:
Certain turbomodules set `requiresMainQueueSetup` to true. This is b/c they use some fancy APIs in setup that need main queue.

TurboModuleManager mostly adhered to this restriction, the only case it didn't is when setting bridge. There is possibility that this happens on JS thread, which would crash the app for these certain TM. This diff fixes that.

Reviewed By: RSNara

Differential Revision: D16921644

fbshipit-source-id: 69b2410550360d3ccb03c0b71fb7dfccb889eda4
2019-08-21 14:31:02 -07:00
Ramanpreet Nara c237794236 Format code in ReactCommon/turbomodule/core
Summary: Just ran `arc f ReactCommon/turbomodule/core/**/*`.

Reviewed By: ejanzer

Differential Revision: D16691807

fbshipit-source-id: 3f499ffeffaae47bda550c0071c93cd7f48e2a23
2019-08-08 10:52:26 -07:00
Kevin Gozali d2e18a1c5c iOS: Revert RCT->RN prefix renaming to avoid confusion
Summary: The previous rename from RCT->RN prefix ended up causing some confusions on which prefix to use for which files and under what circumstances. To avoid further confusion before we're done with the re-architecture project, let's keep them as RCT.

Reviewed By: mdvacca

Differential Revision: D16705566

fbshipit-source-id: 395bff771c84e5ded6b2261a84c7549df1e6c5e5
2019-08-08 07:21:25 -07:00
Ramanpreet Nara bc7c85f153 Delete jsi::Functions before jsi::Runtime gets deleted
Summary:
## The Problem
1. `CatalystInstanceImpl` indirectly holds on to the `jsi::Runtime`. When you destroy `CatalystInstanceImpl`, you destroy the `jsi::Runtime`. As a part of reloading React Native, we destroy and re-create `CatalystInstanceImpl`, which destroys and re-creates the `jsi::Runtime`.
2. When JS passes in a callback to a TurboModule method, we take that callback (a `jsi::Function`) and wrap it in a Java `Callback` (implemented by `JCxxCallbackImpl`). This Java `Callback`, when executed, schedules the `jsi::Function` to be invoked on a Java thread at a later point in time. **Note:** The Java NativeModule can hold on to the Java `Callback` (and, by transitivity, the `jsi::Function`) for potentially forever.
3. It is a requirement of `jsi::Runtime` that all objects associated with the Runtime (ex: `jsi::Function`) must be destroyed before the Runtime itself is destroyed. See: https://fburl.com/m3mqk6wt

### jsi.h
```
/// .................................................... In addition, to
/// make shutdown safe, destruction of objects associated with the Runtime
/// must be destroyed before the Runtime is destroyed, or from the
/// destructor of a managed HostObject or HostFunction.  Informally, this
/// means that the main source of unsafe behavior is to hold a jsi object
/// in a non-Runtime-managed object, and not clean it up before the Runtime
/// is shut down.  If your lifecycle is such that avoiding this is hard,
/// you will probably need to do use your own locks.
class Runtime {
 public:
  virtual ~Runtime();
```

Therefore, when you delete `CatalystInstanceImpl`, you could end up with a situation where the `jsi::Runtime` is destroyed before all `jsi::Function`s are destroyed. In dev, this leads the program to crash when you reload the app after having used a TurboModule method that uses callbacks.

## The Solution
If the only reference to a `HostObject` or a `HostFunction` is in the JS Heap, then the `HostObject` and `HostFunction` destructors can destroy JSI objects. The TurboModule cache is the only thing, aside from the JS Heap, that holds a reference to all C++ TurboModules. But that cache (and the entire native side of `TurboModuleManager`) is destroyed when we call `mHybridData.resetNative()` in `TurboModuleManager.onCatalystInstanceDestroy()` in D16552730. (I verified this by commenting out `mHybridData.resetNative()` and placing a breakpoint in the destructor of `JavaTurboModule`). So, when we're cleaning up `TurboModuleManager`, the only reference to a Java TurboModule is the JS Heap. Therefore, it's safe and correct for us to destroy all `jsi::Function`s created by the Java TurboModule in `~JavaTurboModule`. So, in this diff, I keep a set of all `CallbackWrappers`, and explicitly call `destroy()` on them in the `JavaTurboModule` destructor. Note that since `~JavaTurboModule` accesses `callbackWrappers_`, it must be executed on the JS Thread, since `createJavaCallbackFromJSIFunction` also accesses `callbackWrappers_` on the JS Thread.

For additional safety, I also eagerly destroyed the `jsi::Function` after it's been invoked once. I'm not yet sure if we only want JS callbacks to only ever be invoked once. So, I've created a Task to document this work: T48128233.

Reviewed By: mdvacca

Differential Revision: D16623340

fbshipit-source-id: 3a4c3efc70b9b3c8d329f19fdf4b4423c489695b
2019-08-02 17:08:19 -07:00
Ramanpreet Nara 82e4d28e25 Move ownership of TurboModule jni ref to JavaTurboModule
Summary: When you create a TurboModule from the JS side, we instantiate its Java class and simply make this `javaobject` a `jni::global_ref` in C++. But the reason why we need to make this a global ref is because `JavaTurboModule` needs it to be a global reference for method calls. Making this a `jni::global_ref` from the perspective to TurboModuleManager doesn't really make any sense. So, this diff refactors that bit of code.

Differential Revision: D16622133

fbshipit-source-id: 6a5c20bb405b945c06378a3423d5e7eb38ef244c
2019-08-02 10:08:40 -07:00
Ramanpreet Nara 7ab517b5fe Make TurboModules long-lived on Android
Summary: On iOS, calling the `__turboModuleProxy` function with the same name returns the same instance of the TurboModule. Adding this behaviour to Andorid as well.

Differential Revision: D16622131

fbshipit-source-id: 472011ac3356e7c30497f848be0c888596c449b1
2019-08-02 10:08:39 -07:00
Eric Williamson 283a9dcc11 Revert D16553363: [RN][TurboModule] Make TurboModules long-lived on Android
Differential Revision:
D16553363

Original commit changeset: c95e150d6967

fbshipit-source-id: d035724ce131b560abc70e94410d727951a83241
2019-08-01 21:59:45 -07:00
Eric Williamson af4862ce32 Revert D16555673: [RN][TurboModule] Move ownership of TurboModule jni ref to JavaTurboModule
Differential Revision:
D16555673

Original commit changeset: 2778fc5a372c

fbshipit-source-id: fac3a6ea185acaa750f58e19d24c194668749636
2019-08-01 21:59:45 -07:00
Eric Williamson 6bc0c108eb Revert D16589168: [RN][TurboModule] Delete jsi::Functions before jsi::Runtime gets deleted
Differential Revision:
D16589168

Original commit changeset: a1c0786999c2

fbshipit-source-id: 8048d62e958c0b58aface00dae8447b8c2d5d2dc
2019-08-01 21:59:45 -07:00
Kevin Gozali 9420de6860 iOS: codemod react-native-github: RCT->RN prefix for Fabric
Summary: Fabric ObjC(++) files will be prefixed by RN* for the time being, this codemod is a simple rename. This includes `interface` and `protocol` definition

Reviewed By: PeteTheHeat, yungsters

Differential Revision: D16611524

fbshipit-source-id: 868d2571ea2414dde4cbb3b75b1334b779b5d832
2019-08-01 20:06:04 -07:00
Ramanpreet Nara 2a8c188701 Delete jsi::Functions before jsi::Runtime gets deleted
Summary:
## The Problem
1. `CatalystInstanceImpl` indirectly holds on to the `jsi::Runtime`. When you destroy `CatalystInstanceImpl`, you destroy the `jsi::Runtime`. As a part of reloading React Native, we destroy and re-create `CatalystInstanceImpl`, which destroys and re-creates the `jsi::Runtime`.
2. When JS passes in a callback to a TurboModule method, we take that callback (a `jsi::Function`) and wrap it in a Java `Callback` (implemented by `JCxxCallbackImpl`). This Java `Callback`, when executed, schedules the `jsi::Function` to be invoked on a Java thread at a later point in time. **Note:** The Java NativeModule can hold on to the Java `Callback` (and, by transitivity, the `jsi::Function`) for potentially forever.
3. It is a requirement of `jsi::Runtime` that all objects associated with the Runtime (ex: `jsi::Function`) must be destroyed before the Runtime itself is destroyed. See: https://fburl.com/m3mqk6wt

### jsi.h
```
/// .................................................... In addition, to
/// make shutdown safe, destruction of objects associated with the Runtime
/// must be destroyed before the Runtime is destroyed, or from the
/// destructor of a managed HostObject or HostFunction.  Informally, this
/// means that the main source of unsafe behavior is to hold a jsi object
/// in a non-Runtime-managed object, and not clean it up before the Runtime
/// is shut down.  If your lifecycle is such that avoiding this is hard,
/// you will probably need to do use your own locks.
class Runtime {
 public:
  virtual ~Runtime();
```

Therefore, when you delete `CatalystInstanceImpl`, you could end up with a situation where the `jsi::Runtime` is destroyed before all `jsi::Function`s are destroyed. In dev, this leads the program to crash when you reload the app after having used a TurboModule method that uses callbacks.

## The Solution
If the only reference to a `HostObject` or a `HostFunction` is in the JS Heap, then the `HostObject` and `HostFunction` destructors can destroy JSI objects. The TurboModule cache is the only thing, aside from the JS Heap, that holds a reference to all C++ TurboModules. But that cache (and the entire native side of `TurboModuleManager`) is destroyed when we call `mHybridData.resetNative()` in `TurboModuleManager.onCatalystInstanceDestroy()` in D16552730. (I verified this by commenting out `mHybridData.resetNative()` and placing a breakpoint in the destructor of `JavaTurboModule`). So, when we're cleaning up `TurboModuleManager`, the only reference to a Java TurboModule is the JS Heap. Therefore, it's safe and correct for us to destroy all `jsi::Function`s created by the Java TurboModule in `~JavaTurboModule`. So, in this diff, I keep a set of all `CallbackWrappers`, and explicitly call `destroy()` on them in the `JavaTurboModule` destructor. Note that since `~JavaTurboModule` accesses `callbackWrappers_`, it must be executed on the JS Thread, since `createJavaCallbackFromJSIFunction` also accesses `callbackWrappers_` on the JS Thread.

For additional safety, I also eagerly destroyed the `jsi::Function` after it's been invoked once. I'm not yet sure if we only want JS callbacks to only ever be invoked once. So, I've created a Task to document this work: T48128233.

Reviewed By: mhorowitz

Differential Revision: D16589168

fbshipit-source-id: a1c0786999c22bef55d416beb0fc40261447a807
2019-08-01 16:36:47 -07:00
Ramanpreet Nara 88e03fa4db Move ownership of TurboModule jni ref to JavaTurboModule
Summary: When you create a TurboModule from the JS side, we instantiate its Java class and simply make this `javaobject` a `jni::global_ref` in C++. But the reason why we need to make this a global ref is because `JavaTurboModule` needs it to be a global reference for method calls. Making this a `jni::global_ref` from the perspective to TurboModuleManager doesn't really make any sense. So, this diff refactors that bit of code.

Reviewed By: mdvacca

Differential Revision: D16555673

fbshipit-source-id: 2778fc5a372c41847e8296c2e22bb9a8826fcc52
2019-08-01 16:36:47 -07:00
Ramanpreet Nara e876cc68e7 Make TurboModules long-lived on Android
Summary: On iOS, calling the `__turboModuleProxy` function with the same name returns the same instance of the TurboModule. Adding this behaviour to Andorid as well.

Reviewed By: mdvacca

Differential Revision: D16553363

fbshipit-source-id: c95e150d6967604a808cfb49877b7a633e33d729
2019-08-01 16:36:46 -07:00
Championrunner 9115b61083 Update RCTTurboModule.mm (#25831)
Summary:
## Changelog

[CATEGORY] [TYPE] - Typo Error
Pull Request resolved: https://github.com/facebook/react-native/pull/25831

Differential Revision: D16514794

Pulled By: cpojer

fbshipit-source-id: 36a267fe53f78b93de418d1f0b04f04353bf2195
2019-07-26 01:36:32 -07:00
Min ho Kim 84f5ebe4f9 Fix typos (#25770)
Summary:
Fix typos mostly in comments and some string literals.

## Changelog

[General] [Fixed] - Fix typos
Pull Request resolved: https://github.com/facebook/react-native/pull/25770

Differential Revision: D16437857

Pulled By: cpojer

fbshipit-source-id: ffeb4d6b175e341381352091134f7c97d78c679f
2019-07-23 03:23:11 -07:00
Peter Argany 97d3d59077 Comments & category cleanup
Summary: Been reading a lot of code comments getting familiar with Fabric & TM, just fixing a few typos and removing an unused bridge category method.

Reviewed By: shergin

Differential Revision: D16371581

fbshipit-source-id: bf0cc9c873c60e37124dc715c92d7f105e54e42f
2019-07-20 10:41:46 -07:00
Ramanpreet Nara ff9323a0b0 Type-check JS args when converting them to Java args
Summary:
When you call a TurboModule method with JS arguments, we necessarily convert these JS args to Java objects/primitives before passing them to the Java implementation of the method. The problem is that we let the type of the JS arg dictate the type of the Java object/primitive to convert said arg to. This means that if a JS developer passes in an `number` to a function that expects an `Array`, we'll convert the number to a `double` and try to call the Java method with that `double`, when it actually expects a `ReadableArray`. This will trigger a JNI error, and crash the program. Ideally, we should be able to catch these type mismatches early on.

In this diff, on every TurboModule method call, I parse the method signature to determine the Java type of each JS argument. Then, for any argument, if the JS arg and the Java arg types aren't compatible, I raise an exception, which gets displayed as a RedBox in development. This diff also implements support for `?number` and `?boolean` argument and return types in TurboModules.

Reviewed By: mdvacca

Differential Revision: D16214814

fbshipit-source-id: 4399bb88c5344cff50aa8fe8d54eb2000990a852
2019-07-12 22:54:55 -07:00
Kevin Gozali c1b0f398e6 TM iOS: move jscallinvoker under ReactCommon podspec
Summary:
This essentially changes the header namespace to `<ReactCommon/`
Relevant efforts:
https://github.com/facebook/react-native/pull/25619
https://github.com/facebook/react-native/pull/25393

Reviewed By: PeteTheHeat

Differential Revision: D16233125

fbshipit-source-id: 83eda4cc50ebb01efd1ce3eb18f47c97a049cffa
2019-07-12 22:44:20 -07:00
Kevin Gozali 6e7ce9c082 TM iOS: refactor header dir for TM
Summary:
For better cocoapods compatibility, refactored TM podspec to be a subspec of ReactCommon, then use `<ReactCommon/` header prefix for all TM files.

Relevant efforts:
https://github.com/facebook/react-native/pull/25619
https://github.com/facebook/react-native/pull/25393

Reviewed By: hramos

Differential Revision: D16231697

fbshipit-source-id: 38d3418b19978ff54aa0c61b064ac45ac0e1c36c
2019-07-12 22:44:20 -07:00
James Treanor 6ef91061e8 CocoaPods frameworks compatibility: Step 1 (#25496)
Summary:
This is the first step towards fixing https://github.com/facebook/react-native/issues/25349. These are the changes to the podspec to correctly update dependencies and build config that will cause any breaking change for users or libraries.

I am breaking these changes out from https://github.com/facebook/react-native/pull/25393 as suggested by fkgozali in https://github.com/facebook/react-native/pull/25393#issuecomment-508322884.

These are the changes:

- Made C++ headers in `React-Core` private by default so that ObjC files can import the module without failures.
- Reduced the number of `yoga` headers that are exposed for the same reason as above. As far as I can see this doesn't cause issues but we can find another solution if it does.
- Adding some missing dependencies to fix undefined symbols errors.
- Added `DoubleConversion` to `HEADER_SEARCH_PATHS` where it was missing.

## Changelog

[iOS] [Fixed] - Updated podspecs for improved compatibility with different install types.
Pull Request resolved: https://github.com/facebook/react-native/pull/25496

Test Plan:
Everything should work exactly as before. I have a branch on my [sample project](https://github.com/jtreanor/react-native-cocoapods-frameworks) here which points at this branch to show that it is still working `Podfile` to demonstrate this is fixed.

You can see that it works with these steps:

1. `git clone git@github.com:jtreanor/react-native-cocoapods-frameworks.git`
2. `git checkout podspec-updates`
3. `cd ios && pod install`
4. `cd .. && react-native run-ios`

The sample app will build and run successfully.

Reviewed By: mmmulani

Differential Revision: D16167346

Pulled By: fkgozali

fbshipit-source-id: 1917b2f8779cb172362a457fb3fce686c55056d3
2019-07-10 10:21:38 -07:00
Kevin Gozali 01a42bc368 TM iOS: Prevent module cache invalidation race
Summary: There are still race condition during bridge invalidation. Some modules may be accessing other modules during invalidation, but it's racing with the TM manager clearing the cache.

Reviewed By: JoshuaGross

Differential Revision: D15947488

fbshipit-source-id: 3bd51382264f538a03ca565b0f099da40c3daadf
2019-06-21 15:54:01 -07:00
zhongwuzw af59323d81 TM: Backward compatibility of calling invalidate on module's method queue (#25264)
Summary:
Backward compatibility for `invalidate` method, because for the old module system, we ensure call all methods of modules on `methodQueue`, we also need to keep this rule to avoid some race condition in module.

## Changelog

[iOS] [Fixed] - Backward compatibility of calling invalidate on module's method queue
Pull Request resolved: https://github.com/facebook/react-native/pull/25264

Test Plan: Ensure `invalidate` method of `module` be called on `methodQueue`.

Reviewed By: PeteTheHeat, JoshuaGross

Differential Revision: D15944532

Pulled By: fkgozali

fbshipit-source-id: e260792bc6b86a48cdf376282063cbabccbf26f0
2019-06-21 15:54:01 -07:00
Kevin Gozali 72e276a0f0 TM iOS: Keep LongLivedObject longer past invalidation
Summary:
There can be a race condition between bridge invalidation (hence TM binding invalidation) with pending Promise reject/resolve invocation. If invalidation happens first, invoking the resolve/reject from ObjC class might end up accessing destroyed PromiseWrapper, causing hard crash randomly.

The proper fix is to switch the objc promise resolve/reject block (objc block) to use C++ PromiseWrapper directly, such that the lifecycle of the shared_ptr<> can be correct.

Reviewed By: RSNara

Differential Revision: D15801403

fbshipit-source-id: 9b0c7d323b18d94e920ea3eafc3a6916dadba247
2019-06-13 09:13:51 -07:00
Ramanpreet Nara e908f7a3fe Fix TurboCxxModules::get
Summary: Make `TurboCxxModule::get` return `undefined` when trying to access methods that don't exist. This is what `TurboModule::get` does. So, it makes sense that this behaviour is preserved in TurboCxxModule.

Reviewed By: mdvacca

Differential Revision: D15780044

fbshipit-source-id: 13aeae081655735ef634f1dc09c0dc70a3a3a893
2019-06-13 09:07:24 -07:00
Kevin Gozali c0bf53b716 TM iOS: attempt to convert number args using RCTConvert as well
Summary: Some native modules methods expects number-based args like `NSDate`. For backward compatibility, the incoming numbers should be converted using RCTConvert, just like object args.

Reviewed By: mdvacca

Differential Revision: D15748968

fbshipit-source-id: 4db2cb0c41eda1bbe8cde7b0365d9c3d675f5fb5
2019-06-10 18:41:22 -07:00
Kevin Gozali 7225daf98d TM iOS: Support @synthesize methodQueue auto queue creation
Summary: Some native modules defined `synthesize methodQueue` which will ask the bridge to create a new serial queue for them. TM system needs to preserve this behavior for backward compatibility. In the future though, this magic shall be removed.

Reviewed By: mdvacca

Differential Revision: D15748198

fbshipit-source-id: 66a4b60a2769ac967a8d3bb00c4c635a68daebbc
2019-06-10 18:41:22 -07:00
Kevin Gozali 360e999937 TM iOS: reduce the scope of cache access lock
Summary: We just need to protect access to the cache, we don't need to protect the entire module lookup, because a module initialization may try to lookup another module, causing deadlocks.

Reviewed By: RSNara

Differential Revision: D15690645

fbshipit-source-id: cbb780db8699a94f2c9a2e121b35ddad2b125b65
2019-06-06 09:40:21 -07:00
Ramanpreet Nara a44ab2957c Protect access to RCTTurboModuleCache
Summary: The `_rctTurboModuleCache` `std::unordered_map` can be accessed by multiple threads at the same time via the `provideRCTTurboModule` method. Since `provideRCTTurboModule` both reads and writes to `_rctTurboModuleCache`, this is really bad because we could end up reading from `_rctTurboModuleCache` while it's in an invalid state. Therefore, in this diff, I'm making it so that only one thread at a time can enter `provideRCTTurboModule`.

Reviewed By: fkgozali

Differential Revision: D15609987

fbshipit-source-id: e24e1f5cc2351d8cbb820b7a97074aacd06eec9d
2019-06-03 16:16:09 -07:00
zhongwuzw a352ecfabd TM iOS: Remove retainArguments && do retain by us explicitly (#24849)
Summary:
This is a TODO, we met crash if we don't call `retainArguments` when return type is like `NSDictionary`, the reason is `getReturnValue` don't retain the return value, so we need to using `__bridge` to transfer ownership to OC type.
Also add `resolveBlock` and `rejectBlock` to `retainedObjectsForInvocation`.

cc. cpojer

## Changelog

[iOS] [Fixed] - Remove retainArguments && do retain by us explicitly
Pull Request resolved: https://github.com/facebook/react-native/pull/24849

Differential Revision: D15369209

Pulled By: fkgozali

fbshipit-source-id: 8431d03705d8476f38c8b5d29630489a545d373a
2019-05-15 21:50:40 -07:00
Kevin Gozali 8662d9d3b0 TM iOS: Added backward-compatible ObjC invalidation logic
Summary:
Some ObjC NativeModules conform to `RCTInvalidating` protocol and implements `invalidate` method. This is typically used to clean things up during bridge teardown or reload. In TurboModule system the invalidate method can be replaced by pure destructors, but for backward compatibility, we call existing invalidate method on each module during teardown.

This also cleans up all existing LongLivedObject's.

Reviewed By: mdvacca, RSNara

Differential Revision: D15365655

fbshipit-source-id: 802844b39b5b7adb54970ea541f4d744bbf9e480
2019-05-15 17:36:05 -07:00
RCiesielczuk cd2f8c567b Introduce TurboModule Setup Metric (#24732)
Summary:
With the introduction of TurboModules, it would be beneficial to measure the setup time of these modules, as we currently have it in place for NativeModules.

The instantiation of the TMs occurs in the `RCTTurboModuleManager`. In order to successfully measure the time it took to setup the module, we need to ensure that we don't take into account cached modules. As such, we need to:

1. Check if module is in `_turboModuleCache`
  a. Start mark for `RCTPLTurboModuleSetup` tag if not found
2. Get the TM via `[self provideTurboModule:]`
3. Check if module is in `_turboModuleCache`
   a. Stop mark for `RCTPLTurboModuleSetup` if we did not find module in cache prior to **step 2** and if it's now present in the cache.
   b. Notify about setup time if the above is true.
4. Return TM

## Changelog

[iOS] [Added] - Gain insights on the the turbo module setup times by observing `RCTDidSetupModuleNotification`. The userInfo dictionary will contain the module name and setup time in milliseconds. These values can be extracted via `RCTDidSetupModuleNotificationModuleNameKey` and `RCTDidSetupModuleNotificationSetupTimeKey`.
Pull Request resolved: https://github.com/facebook/react-native/pull/24732

Differential Revision: D15362088

Pulled By: RSNara

fbshipit-source-id: e6a8044e4aba5a12ae63e9c7dbf707a17ec00180
2019-05-15 15:28:10 -07:00
zhongwuzw 36f9a4aca7 TM iOS: Consolidate RCTDidInitializeModuleNotification sender to batchedBridge (#24866)
Summary:
Consolidate sender of notification to `batchedBridge` for TM and old module system, old module implementation can see https://github.com/facebook/react-native/blob/dc893756b82051d48d6a776aeef1186c31cf310d/React/Base/RCTModuleData.mm#L191-L193.

## Changelog

[iOS] [Fixed] - Conlidate RCTDidInitializeModuleNotification sender to batchedBridge
Pull Request resolved: https://github.com/facebook/react-native/pull/24866

Differential Revision: D15357566

Pulled By: RSNara

fbshipit-source-id: 479575c1e9781d8aea9fa3aea9e0283d24df7e81
2019-05-15 12:31:30 -07:00
Emily Janzer c75f062a77 Change JSCallInvoker to be an abstract class
Summary: The existing implementation of JSCallInvoker holds a reference to Instance (aka the bridge). This diff makes JSCallInvoker an abstract base class that's extended by BridgeJSCallInvoker, which is what's returned by CatalystInstance to initialize TurboModules. This will allow us to add another class that derives from JSCallInvoker that doesn't rely on the bridge.

Reviewed By: shergin

Differential Revision: D15252375

fbshipit-source-id: 75eee2ca149235a63e2a2cd8cdd361b163d1d1ab
2019-05-13 12:04:33 -07:00
Kevin Gozali 0436f81595 TM iOS: Fixed mixed-up module lookup from native side
Summary:
There are 2 issues:
* RCTTurboModuleManager may be initialized too late, so the module lookup from native via `[bridge moduleForClass:]` may end up going to the old system incorrectly
* In the case where JS is asking for a nativemodule, we may be incorrectly registering modules that are marked as RCTTurboModule - they should be ignored instead

Reviewed By: mmmulani, RSNara

Differential Revision: D15247632

fbshipit-source-id: 4e0fae33923810c74966b38276b206ac00723012
2019-05-07 19:29:16 -07:00
Ramanpreet Nara 913ff9f5ae Fix OSS build
Summary: iOS TurboModules work in OSS. I pulled out JSCallInvoker from `React-turbomodule-core`, as a part of D15055511. In this diff, I'm adding it back in.

Reviewed By: mdvacca

Differential Revision: D15195007

fbshipit-source-id: 64ee294a68c0a63ba400b8a829864c6619d3311a
2019-05-03 13:31:21 -07:00
Ramanpreet Nara ef4955fefe Make async calls work
Summary:
JSCallInvoker requires a `std::weak_ptr<Instance>` to create. In our C++, `CatalystInstance` is responsible for creating this `Instance` object. This `CatalystInstance` C++ initialization is separate from the `TurboModuleManager` C++ initialization. Therefore, in this diff, I made `CatalystInstance` responsible for creating the `JSCallInvoker`. It then exposes the `JSCallInvoker` using a hybrid class called `JSCallInvokerHolder`, which contains a `std::shared_ptr<JSCallInvoker>` member variable. Using `CatalystInstance.getJSCallInvokerHolder()` in TurboModuleManager.java, we get a handle to this hybrid container. Then, we pass it this hybrid object to `TurboModuleManager::initHybrid`, which retrieves the `std::shared_ptr<JSCallInvoker>` from the `JavaJSCallInvokerHandler`.

There were a few cyclic dependencies, so I had to break down the buck targets:
- `CatalystInstanceImpl.java` depends on `JSCallInvokerHolderImpl.java`, and `TurboModuleManager.java` depends on classes that are packaged with `CatalystInstanceImpl.java`. So, I had to put `JSCallInvokerHolderImpl.java` in its own buck target.
- `CatalystInstance.cpp` depends on `JavaJSCallInvokerHolder.cpp`, and `TurboModuleManager.cpp` depends on classes that are build with `CatalystInstance.cpp`. So, I had to put `JavaJSCallInvokerHolder.cpp` in its own buck target. To make things simpler, I also moved `JSCallInvoker.{cpp,h}` files into the same buck target as `JavaJSCallInvokerHolder.{cpp,h}`.

I think these steps should be enough to create the TurboModuleManager without needing a bridge:
1. Make `JSCallInvoker` an abstract base class.
2. On Android, create another derived class of `JSCallInvoker` that doesn't depend on Instance.
3. Create `JavaJSCallInvokerHolder` using an instance of this new class somewhere in C++.
4. Pass this instance of `JavaJSCallInvokerHolder` to Java and use it to create/instatiate `TurboModuleManager`.

Regarding steps 1 and 2, we can also make JSCallInvoker accept a lambda.

Reviewed By: mdvacca

Differential Revision: D15055511

fbshipit-source-id: 0ad72a86599819ec35d421dbee7e140959a26ab6
2019-05-03 13:31:20 -07:00
Ramanpreet Nara 7a8de7db46 Replace throwIfJNIReportsPendingException
Summary: Looks like FBJNI exports a C Macro that does exactly what `throwIfJNIReportsPendingException` does. Therefore, I'm replacing `throwIfJNIReportsPendingException` with calls to `FACEBOOK_JNI_THROW_PENDING_EXCEPTION()`.

Reviewed By: mdvacca

Differential Revision: D15174820

fbshipit-source-id: 9dfb519352cbd5f37527675323cbabad05e31d4a
2019-05-03 12:00:19 -07:00
Ramanpreet Nara a9650709e3 Fix multiple invocations of getConstants
Summary:
`jclass` in `JNI` is just a regular local reference. Therefore, it's unsafe to keep a static reference to it. Link: http://journals.ecs.soton.ac.uk/java/tutorial/native1.1/implementing/refs.html.

This bug made it so that when you clicked on `getConstants` twice in the TurboModule playground, the app would crash.

Reviewed By: mdvacca

Differential Revision: D15174821

fbshipit-source-id: 13b2b8726473acc9b07306558044d26bed0db92d
2019-05-03 12:00:19 -07:00
Ramanpreet Nara c9006ce5fb Refactor getConstants implementation
Summary: Previously, we'd override the `TurboModule::get` method inside the `JavaTurboModule` class to return a special `jsi::Function` in the case that the property being accessed was "getConstants". We really don't need to do this because we can simply special-case the invocation of the `getConstants` method inside the `JavaTurboModule::invokeJavaMethod` method.

Reviewed By: mdvacca

Differential Revision: D15174822

fbshipit-source-id: 0ee705be841757d3870c908da911c3872b977a9f
2019-05-03 12:00:18 -07:00
Kevin Gozali 0708e28caf TM: removed unused virtual invokeMethod() and invalidate()
Summary:
* invokeMethod() ends up not useful because each platform has its own way of invoking the platform methods
* invalidate() is not necessary because there's already the destructor of each C++ class

Reviewed By: mdvacca

Differential Revision: D15187833

fbshipit-source-id: 9478ed1e6288da30c67179e03a7bc7da6043280b
2019-05-02 14:12:34 -07:00
Kevin Gozali 694bf7104c TM iOS optimization: codegen @selector for each method call.
Summary: This adds support for specifying the exact selector name for each exposed ObjC method. This allows us to avoid dynamic method lookup every time a method is called from JS.

Reviewed By: RSNara

Differential Revision: D15141611

fbshipit-source-id: ed2820782ab013369e4e1f22dbce31d9838a17bb
2019-04-30 13:58:35 -07:00
Ramanpreet Nara e7a8b26f22 Fix ObjCTurboModule::getArgumentTypeName
Summary: In `ObjCTurboModule::getArgumentTypeName`, I replaced all instances of `':'` with `''` to transform the selector into a TurboModule methodName. This transformation works when the method has 0 or 1 argument, however, it breaks when the method has more than 1 argument. In all cases, we just want to get the substring until the first `':'`.

Reviewed By: fkgozali

Differential Revision: D15056937

fbshipit-source-id: 3a7dce1ce62ca9758e46c0af951b269166d68454
2019-04-23 17:52:21 -07:00
Ramanpreet Nara c9c8f8d5b3 Remove jClassName_ member
Summary: `jClassName_` is unnecessary because you can use `JNIEnv::GetObjectClass` to get the TurboModule's Java class.

Reviewed By: fkgozali

Differential Revision: D14937480

fbshipit-source-id: 2c1c9be53217331152270dbac3d13f372a2ed818
2019-04-22 15:33:06 -07:00
Ramanpreet Nara 129aeacf48 Add exception handling to method invocations
Summary:
If you call into a Java method (from C++ using JNI) that raises an exception, the JNI call won't actually raise a C++ error. Instead, the `JNIEnv` will record the pending Java exception and the C++ will continue executing as normal. This is bad because the next time you call into JNI, the app will actually crash, unless you explicitly cleared the exception using `JNIEnv::ExceptionClear()` before the JNI call.

With respect to TurboModules, we need to make sure that RedBoxes show up whenever a native methods raise an exception. We also don't want the app to crash when a JNI method call fails because of a raised exception. Therefore, in this diff, I raise a C++ exception if `JNIEnv::ExceptionCheck()` is true.

Reviewed By: mdvacca

Differential Revision: D14738540

fbshipit-source-id: 4c3063aa93ae7aef025bd2dab6b45059bb8fb409
2019-04-16 09:18:14 -07:00
Ramanpreet Nara d1c35aaa84 Add support for Promises
Summary:
If the return type of a TurboModule method is `Promise`, the infra should create a `com.facebook.react.bridge.Promise` object and pass it as the final argument of the TurboModule Java method call. The Java TurboModule method can then do some work asynchronously and either resolve or reject the promise at some point in time.

**Note:** I stacked a diff for error handling on top of this one.

Reviewed By: mdvacca

Differential Revision: D13653156

fbshipit-source-id: 4c30c3223ad8f47c6ba7f1236527aaced01c8ae8
2019-04-16 09:18:14 -07:00
Kevin Gozali 4da6e4a042 TM iOS: Install SampleTurboModule in RNTester (pods)
Summary: This sets up RCTSampleTurboModule (and other variants) in RNTester when built with cocoapods. There's no call site yet though. And RNTester.xcodeproj doesn't support it.

Reviewed By: cpojer

Differential Revision: D14932535

fbshipit-source-id: db8eafd6777cbec8f3592dafdccbdd7cf44e38bc
2019-04-15 12:25:48 -07:00
Kevin Gozali a2aba45067 TM iOS: Move SampleTurboModule to OSS
Summary:
This provides various versions of SampleTurboModule, that are:
* compatible with existing NativeModule
* TurboModule compliant

Variants:
* RCTSampleTurboModule (traditional objc module)
* RCTSampleTurboCxxModule (objc++ module using CxxModule)
* SampleTurboModule (pure C++ impl of a TurboModule, no ObjC)

As noted in some files, they need to be codegen'ed based on the `NativeSampleTurboModule.js` (Flow type). The codegen script is not yet usable in OSS (we'll work on it some time in H2 2019). For now, these files need to be manually synced with Flow type.

Reviewed By: cpojer

Differential Revision: D14932539

fbshipit-source-id: fb887192384e5e6e4dff4cac68b4e037a4783cd9
2019-04-15 12:25:47 -07:00
Kevin Gozali e612b9b0f5 TM iOS: Install TurboModule system in RNTester (pod) by default
Summary:
For CocoaPods variant only: install TurboModule binding so that sample modules can start using it. This commit only installs `global.__turboModuleProxy` - no sample module is provided.

Note: RNTester.xcodeproj will NOT have TurboModule enabled, due to complication in the .xcodeproj setup (doable, but maybe for some other time...)

To test:

```
console.error(global.__turboModuleProxy == null ? 'BOO' : 'YAY!');
```

Saw `YAY!` in RNTester pod version.

Reviewed By: cpojer

Differential Revision: D14932536

fbshipit-source-id: 3dc083da9154ec320ce6789ec7f2cef5a08fd6a7
2019-04-15 12:25:47 -07:00
Kevin Gozali 71a8944b39 TM iOS: Use weak_ptr to pass around Instance to avoid unreleased refs
Summary:
There is a timing issue when reloading the bridge (in dev mode) and the tear down of the TurboModules. This causes `Instance` to never get freed, hence the "bridge" isn't cleaning up properly. The side effect can be bogus error saying that it's unable to find a module.

To address this, JSCallInvoker should just take in weak_ptr<Instance>.

Reviewed By: RSNara

Differential Revision: D14739181

fbshipit-source-id: f9f2a55486debaeb28d3d293df3cf1d3f6b9a031
2019-04-02 18:11:18 -07:00
Ramanpreet Nara 46944b7937 Implement auto-conversions from primitives to Objects
Summary:
If a NativeModule method requires an optional boolean argument, our codegen translates those optional booleans into `NSNumber*` instead of `BOOL`. The reason why is probably because this is the closest object analogue to `BOOL` in Objective C. The same boxing occurs with numbers. If the type of a number argument in JavaScript is optional, we'll map it to the `NSNumber*` Objective C type instead of `double`. Our existing TurboModules argument conversion code would not take this behaviour into account. Therefore, we'd try to insert a `BOOL` where the `NSInvocation` would expect a `NSNumber*`. This, in turn, would cause the app to crash. (Why would it crash at the point of NSInvocation retainArguments, I'm still not sure).

Our flow typechecking ensures that if the type of a method argument is a boolean, we pass in a boolean. Therefore, on the Native side, if we detect a boolean, we can check the type of the Native argument to see whether we should box the primitive. If the native argument type is an object, then we know it has to be an `NSNumber*` in both cases, so we simply wrap the `BOOL` or `double` in a `NSNumber*`.

Reviewed By: shergin

Differential Revision: D14679590

fbshipit-source-id: c394a878492aab8e98c71d74ec8740a94fc3a6c5
2019-03-31 02:49:35 -07:00