text: /*
   * Copyright (c) Facebook, Inc. and its affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   */
  
  #include "ABI42_0_0CxxNativeModule.h"
  #include "ABI42_0_0Instance.h"
  
  #include <folly/json.h>
  #include <glog/logging.h>
  #include <iterator>
  
  #include "ABI42_0_0JsArgumentHelpers.h"
  #include "ABI42_0_0MessageQueueThread.h"
  #include "ABI42_0_0SystraceSection.h"
  
  using ABI42_0_0facebook::xplat::module::CxxModule;
  namespace ABI42_0_0facebook {
  namespace ABI42_0_0React {
  
  std::function<void(folly::dynamic)> makeCallback(
      std::weak_ptr<Instance> instance,
      const folly::dynamic &callbackId) {
    if (!callbackId.isNumber()) {
      throw std::invalid_argument("Expected callback(s) as final argument");
    }
  
    auto id = callbackId.asInt();
    return [winstance = std::move(instance), id](folly::dynamic args) {
      if (auto instance = winstance.lock()) {
        instance->callJSCallback(id, std::move(args));
      }
    };
  }
  
  namespace {
  
  /**
   * CxxModule::Callback accepts a vector<dynamic>, makeCallback returns
   * a callback that accepts a dynamic, adapt the second into the first.
   * TODO: Callback types should be made equal (preferably
   * function<void(dynamic)>) to avoid the extra copy and indirect call.
   */
  CxxModule::Callback convertCallback(
      std::function<void(folly::dynamic)> callback) {
    return [callback = std::move(callback)](std::vector<folly::dynamic> args) {
      callback(folly::dynamic(
          std::make_move_iterator(args.begin()),
          std::make_move_iterator(args.end())));
    };
  }
  
  } // namespace
  
  std::string CxxNativeModule::getName() {
    return name_;
  }
  
  std::vector<MethodDescriptor> CxxNativeModule::getMethods() {
    lazyInit();
  
    std::vector<MethodDescriptor> descs;
    for (auto &method : methods_) {
      descs.emplace_back(method.name, method.getType());
    }
    return descs;
  }
  
  folly::dynamic CxxNativeModule::getConstants() {
    lazyInit();
  
    if (!module_) {
      return nullptr;
    }
  
    folly::dynamic constants = folly::dynamic::object();
    for (auto &pair : module_->getConstants()) {
      constants.insert(std::move(pair.first), std::move(pair.second));
    }
    return constants;
  }
  
  void CxxNativeModule::invoke(
      unsigned int ABI42_0_0ReactMethodId,
      folly::dynamic &&params,
      int callId) {
    if (ABI42_0_0ReactMethodId >= methods_.size()) {
      throw std::invalid_argument(folly::to<std::string>(
          "methodId ",
          ABI42_0_0ReactMethodId,
          " 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_[ABI42_0_0ReactMethodId];
  
    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
    // propagate, 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);
          }
  #else
          (void)(callId);
  #endif
          SystraceSection s(method.name.c_str());
          try {
            method.func(std::move(params), first, second);
          } catch (const ABI42_0_0facebook::xplat::JsArgumentException &ex) {
            throw;
          } catch (std::exception &e) {
            LOG(ERROR) << "std::exception. Method call " << method.name.c_str()
                       << " failed: " << e.what();
            std::terminate();
          } catch (std::string &error) {
            LOG(ERROR) << "std::string. Method call " << method.name.c_str()
                       << " failed: " << error.c_str();
            std::terminate();
          } catch (...) {
            LOG(ERROR) << "Method call " << method.name.c_str()
                       << " failed. unknown error";
            std::terminate();
          }
        });
  }
  
  MethodCallResult CxxNativeModule::callSerializableNativeHook(
      unsigned int hookId,
      folly::dynamic &&args) {
    if (hookId >= methods_.size()) {
      throw std::invalid_argument(folly::to<std::string>(
          "methodId ", hookId, " out of range [0..", methods_.size(), "]"));
    }
  
    const auto &method = methods_[hookId];
  
    if (!method.syncFunc) {
      throw std::runtime_error(folly::to<std::string>(
          "Method ", method.name, " is asynchronous but invoked synchronously"));
    }
  
    return method.syncFunc(std::move(args));
  }
  
  void CxxNativeModule::lazyInit() {
    if (module_ || !provider_) {
      return;
    }
  
    // TODO 17216751: providers should never return null modules
    module_ = provider_();
    provider_ = nullptr;
    if (module_) {
      methods_ = module_->getMethods();
      module_->setInstance(instance_);
    }
  }
  
  } // namespace ABI42_0_0React
  } // namespace ABI42_0_0facebook
  