void JavaNativeModule::invoke(unsigned int reactMethodId, folly::dynamic&& params, int callId) { messageQueueThread_->runOnQueue([this, reactMethodId, params=std::move(params), callId] { static auto invokeMethod = wrapper_->getClass()->getMethod<void(jint, ReadableNativeArray::javaobject)>("invoke"); #ifdef WITH_FBSYSTRACE if (callId != -1) { fbsystrace_end_async_flow(TRACE_TAG_REACT_APPS, "native", callId); } #endif invokeMethod( wrapper_, static_cast<jint>(reactMethodId), ReadableNativeArray::newObjectCxxArgs(std::move(params)).get()); }); }
void NewJavaNativeModule::invoke(unsigned int reactMethodId, folly::dynamic&& params, int callId) { if (reactMethodId >= methods_.size()) { throw std::invalid_argument( folly::to<std::string>("methodId ", reactMethodId, " out of range [0..", methods_.size(), "]")); } CHECK(!methods_[reactMethodId].isSyncHook()) << "Trying to invoke a synchronous hook asynchronously"; messageQueueThread_->runOnQueue([this, reactMethodId, params=std::move(params), callId] () mutable { #ifdef WITH_FBSYSTRACE if (callId != -1) { fbsystrace_end_async_flow(TRACE_TAG_REACT_APPS, "native", callId); } #endif invokeInner(reactMethodId, std::move(params)); }); }
void ModuleRegistry::callNativeMethod(ExecutorToken token, unsigned int moduleId, unsigned int methodId, folly::dynamic&& params, int callId) { if (moduleId >= modules_.size()) { throw std::runtime_error( folly::to<std::string>("moduleId ", moduleId, " out of range [0..", modules_.size(), ")")); } #ifdef WITH_FBSYSTRACE if (callId != -1) { fbsystrace_end_async_flow(TRACE_TAG_REACT_APPS, "native", callId); } #endif // TODO mhorowitz: systrace std::string what; try { modules_[moduleId]->invoke(token, methodId, std::move(params)); return; } catch (const std::exception& e) { what = e.what(); // fall through; } catch (...) { // fall through; } std::string moduleName = normalizeName(modules_[moduleId]->getName()); auto descs = modules_[moduleId]->getMethods(); std::string methodName; if (methodId < descs.size()) { methodName = descs[methodId].name; } else { methodName = folly::to<std::string>("id ", methodId, " (out of range [0..", descs.size(), "))"); } if (what.empty()) { throw std::runtime_error( folly::to<std::string>("Unknown native exception in module '", moduleName, "' method '", methodName, "'")); } else { throw std::runtime_error( folly::to<std::string>("Native exception in module '", moduleName, "' method '", methodName, "': ", what)); } }
void CxxNativeModule::invoke(unsigned int ReactABI20_0_0MethodId, folly::dynamic&& params, int callId) { if (ReactABI20_0_0MethodId >= methods_.size()) { throw std::invalid_argument(folly::to<std::string>("methodId ", ReactABI20_0_0MethodId, " out of range [0..", methods_.size(), "]")); } if (!params.isArray()) { throw std::invalid_argument( folly::to<std::string>("method parameters should be array, but are ", params.typeName())); } CxxModule::Callback first; CxxModule::Callback second; const auto& method = methods_[ReactABI20_0_0MethodId]; if (!method.func) { throw std::runtime_error(folly::to<std::string>("Method ", method.name, " is synchronous but invoked asynchronously")); } if (params.size() < method.callbacks) { throw std::invalid_argument(folly::to<std::string>("Expected ", method.callbacks, " callbacks, but only ", params.size(), " parameters provided")); } if (method.callbacks == 1) { first = convertCallback(makeCallback(instance_, params[params.size() - 1])); } else if (method.callbacks == 2) { first = convertCallback(makeCallback(instance_, params[params.size() - 2])); second = convertCallback(makeCallback(instance_, params[params.size() - 1])); } params.resize(params.size() - method.callbacks); // I've got a few flawed options here. I can let the C++ exception // propogate, and the registry will log/convert them to java exceptions. // This lets all the java and red box handling work ok, but the only info I // can capture about the C++ exception is the what() string, not the stack. // I can std::terminate() the app. This causes the full, accurate C++ // stack trace to be added to logcat by debuggerd. The java state is lost, // but in practice, the java stack is always the same in this case since // the javascript stack is not visible, and the crash is unfriendly to js // developers, but crucial to C++ developers. The what() value is also // lost. Finally, I can catch, log the java stack, then rethrow the C++ // exception. In this case I get java and C++ stack data, but the C++ // stack is as of the rethrow, not the original throw, both the C++ and // java stacks always look the same. // // I am going with option 2, since that seems like the most useful // choice. It would be nice to be able to get what() and the C++ // stack. I'm told that will be possible in the future. TODO // mhorowitz #7128529: convert C++ exceptions to Java messageQueueThread_->runOnQueue([method, params=std::move(params), first, second, callId] () { #ifdef WITH_FBSYSTRACE if (callId != -1) { fbsystrace_end_async_flow(TRACE_TAG_REACT_APPS, "native", callId); } #endif SystraceSection s(method.name.c_str()); try { method.func(std::move(params), first, second); } catch (const facebook::xplat::JsArgumentException& ex) { throw; } catch (...) { // This means some C++ code is buggy. As above, we fail hard so the C++ // developer can debug and fix it. std::terminate(); } }); }