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();
    }
  });
}