hunk.code_window:       final FragmentActivity activity = (FragmentActivity) getCurrentActivity();
         mClient = new GoogleApiClient.Builder(getReactApplicationContext())
             .addApi(Fitness.HISTORY_API)
             .addApi(Fitness.SENSORS_API)
             .addScope(new Scope(Scopes.FITNESS_ACTIVITY_READ))
             .addConnectionCallbacks(
                 new GoogleApiClient.ConnectionCallbacks() {
  
hunk.labels: keep keep keep add keep keep keep
hunk.after_edit:           .addApi(Fitness.RECORDING_API)
  
hunk.file_path: android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/PedometerModule.java
hunk.type: add
file: // Copyright 2004-present Facebook. All Rights Reserved.
  
  #include "ABI14_0_0JSCExecutor.h"
  
  #include <algorithm>
  #include <condition_variable>
  #include <mutex>
  #include <sstream>
  #include <string>
  #include <glog/logging.h>
  #include <folly/json.h>
  #include <folly/Exception.h>
  #include <folly/Memory.h>
  #include <folly/String.h>
  #include <folly/Conv.h>
  #include <fcntl.h>
  #include <sys/time.h>
  #include <system_error>
  
  #include <ABI14_0_0jschelpers/ABI14_0_0JSCHelpers.h>
  #include <ABI14_0_0jschelpers/ABI14_0_0Value.h>
  
  #ifdef WITH_INSPECTOR
  #include <inspector/Inspector.h>
  #endif
  
  #include "ABI14_0_0Platform.h"
  #include "ABI14_0_0SystraceSection.h"
  #include "ABI14_0_0JSCNativeModules.h"
  #include "ABI14_0_0JSCSamplingProfiler.h"
  #include "ABI14_0_0JSCUtils.h"
  #include "JSModulesUnbundle.h"
  #include "ABI14_0_0ModuleRegistry.h"
  #include "ABI14_0_0RecoverableError.h"
  
  #if defined(WITH_JSC_EXTRA_TRACING) || DEBUG
  #include "ABI14_0_0JSCTracing.h"
  #endif
  
  #ifdef WITH_JSC_EXTRA_TRACING
  #include "ABI14_0_0JSCLegacyProfiler.h"
  #include "ABI14_0_0JSCLegacyTracing.h"
  #include <JavaScriptCore/API/JSProfilerPrivate.h>
  #endif
  
  #ifdef WITH_JSC_MEMORY_PRESSURE
  #include <jsc_memory.h>
  #endif
  
  #ifdef WITH_FB_MEMORY_PROFILING
  #include "ABI14_0_0JSCMemory.h"
  #endif
  
  #if defined(WITH_FB_JSC_TUNING) && defined(__ANDROID__)
  #include <jsc_config_android.h>
  #endif
  
  #ifdef JSC_HAS_PERF_STATS_API
  #include "ABI14_0_0JSCPerfStats.h"
  #endif
  
  namespace facebook {
  namespace ReactABI14_0_0 {
  
  using namespace detail;
  
  namespace {
  
  template<JSValueRef (JSCExecutor::*method)(size_t, const JSValueRef[])>
  inline JSObjectCallAsFunctionCallback exceptionWrapMethod() {
    struct funcWrapper {
      static JSValueRef call(
          JSContextRef ctx,
          JSObjectRef function,
          JSObjectRef thisObject,
          size_t argumentCount,
          const JSValueRef arguments[],
          JSValueRef *exception) {
        try {
          auto executor = Object::getGlobalObject(ctx).getPrivate<JSCExecutor>();
          return (executor->*method)(argumentCount, arguments);
        } catch (...) {
          *exception = translatePendingCppExceptionToJSError(ctx, function);
          return Value::makeUndefined(ctx);
        }
      }
    };
  
    return &funcWrapper::call;
  }
  
  template<JSValueRef (JSCExecutor::*method)(JSObjectRef object, JSStringRef propertyName)>
  inline JSObjectGetPropertyCallback exceptionWrapMethod() {
    struct funcWrapper {
      static JSValueRef call(
           JSContextRef ctx,
           JSObjectRef object,
           JSStringRef propertyName,
           JSValueRef *exception) {
        try {
          auto executor = Object::getGlobalObject(ctx).getPrivate<JSCExecutor>();
          return (executor->*method)(object, propertyName);
        } catch (...) {
          *exception = translatePendingCppExceptionToJSError(ctx, object);
          return Value::makeUndefined(ctx);
        }
      }
    };
  
    return &funcWrapper::call;
  }
  
  }
  
  #if DEBUG
  static JSValueRef nativeInjectHMRUpdate(
      JSContextRef ctx,
      JSObjectRef function,
      JSObjectRef thisObject,
      size_t argumentCount,
      const JSValueRef arguments[],
      JSValueRef *exception) {
    String execJSString = Value(ctx, arguments[0]).toString();
    String jsURL = Value(ctx, arguments[1]).toString();
    evaluateScript(ctx, execJSString, jsURL);
    return Value::makeUndefined(ctx);
  }
  #endif
  
  std::unique_ptr<JSExecutor> JSCExecutorFactory::createJSExecutor(
      std::shared_ptr<ExecutorDelegate> delegate, std::shared_ptr<MessageQueueThread> jsQueue) {
    return std::unique_ptr<JSExecutor>(
      new JSCExecutor(delegate, jsQueue, m_cacheDir, m_jscConfig));
  }
  
  JSCExecutor::JSCExecutor(std::shared_ptr<ExecutorDelegate> delegate,
                           std::shared_ptr<MessageQueueThread> messageQueueThread,
                           const std::string& cacheDir,
                           const folly::dynamic& jscConfig) throw(JSException) :
      m_delegate(delegate),
      m_deviceCacheDir(cacheDir),
      m_messageQueueThread(messageQueueThread),
      m_nativeModules(delegate ? delegate->getModuleRegistry() : nullptr),
      m_jscConfig(jscConfig) {
    initOnJSVMThread();
  
    {
      SystraceSection s("nativeModuleProxy object");
      installGlobalProxy(m_context, "nativeModuleProxy",
                         exceptionWrapMethod<&JSCExecutor::getNativeModule>());
    }
  }
  
  JSCExecutor::JSCExecutor(
      std::shared_ptr<ExecutorDelegate> delegate,
      std::shared_ptr<MessageQueueThread> messageQueueThread,
      int workerId,
      JSCExecutor *owner,
      std::string scriptURL,
      std::unordered_map<std::string, std::string> globalObjAsJSON,
      const folly::dynamic& jscConfig) :
      m_delegate(delegate),
      m_workerId(workerId),
      m_owner(owner),
      m_deviceCacheDir(owner->m_deviceCacheDir),
      m_messageQueueThread(messageQueueThread),
      m_nativeModules(delegate->getModuleRegistry()),
      m_jscConfig(jscConfig) {
    // We post initOnJSVMThread here so that the owner doesn't have to wait for
    // initialization on its own thread
    m_messageQueueThread->runOnQueue([this, scriptURL,
                                      globalObjAsJSON=std::move(globalObjAsJSON)] () {
      initOnJSVMThread();
  
      installNativeHook<&JSCExecutor::nativePostMessage>("postMessage");
  
      for (auto& it : globalObjAsJSON) {
        setGlobalVariable(std::move(it.first),
                          folly::make_unique<JSBigStdString>(std::move(it.second)));
      }
  
      // Try to load the script from the network if script is a URL
      // NB: For security, this will only work in debug builds
      std::unique_ptr<const JSBigString> script;
      if (scriptURL.find("http://") == 0 || scriptURL.find("https://") == 0) {
        std::stringstream outfileBuilder;
        outfileBuilder << m_deviceCacheDir << "/workerScript" << m_workerId << ".js";
        script = folly::make_unique<JSBigStdString>(
          WebWorkerUtil::loadScriptFromNetworkSync(scriptURL, outfileBuilder.str()));
      } else {
        // TODO(9604438): Protect against script does not exist
        script = WebWorkerUtil::loadScriptFromAssets(scriptURL);
      }
  
      // TODO(9994180): Throw on error
      loadApplicationScript(std::move(script), std::move(scriptURL));
    });
  }
  
  JSCExecutor::~JSCExecutor() {
    CHECK(*m_isDestroyed) << "JSCExecutor::destroy() must be called before its destructor!";
  }
  
  void JSCExecutor::destroy() {
    *m_isDestroyed = true;
    if (m_messageQueueThread.get()) {
      m_messageQueueThread->runOnQueueSync([this] () {
        terminateOnJSVMThread();
      });
    } else {
      terminateOnJSVMThread();
    }
  }
  
  void JSCExecutor::setContextName(const std::string& name) {
    String jsName = String(m_context, name.c_str());
    JSC_JSGlobalContextSetName(m_context, jsName);
  }
  
  void JSCExecutor::initOnJSVMThread() throw(JSException) {
    SystraceSection s("JSCExecutor.initOnJSVMThread");
  
    #if defined(__APPLE__)
    const bool useCustomJSC = m_jscConfig.getDefault("UseCustomJSC", false).getBool();
    if (useCustomJSC) {
      JSC_configureJSCForIOS(true);
    }
    #else
    const bool useCustomJSC = false;
    #endif
  
    #if defined(WITH_FB_JSC_TUNING) && defined(__ANDROID__)
    configureJSCForAndroid(m_jscConfig);
    #endif
  
    // Create a custom global class, so we can store data in it later using JSObjectSetPrivate
    JSClassRef globalClass = nullptr;
    {
      SystraceSection s("JSClassCreate");
      globalClass = JSC_JSClassCreate(useCustomJSC, &kJSClassDefinitionEmpty);
    }
    {
      SystraceSection s("JSGlobalContextCreateInGroup");
      m_context = JSC_JSGlobalContextCreateInGroup(useCustomJSC, nullptr, globalClass);
    }
    JSC_JSClassRelease(useCustomJSC, globalClass);
  
    // Add a pointer to ourselves so we can retrieve it later in our hooks
    Object::getGlobalObject(m_context).setPrivate(this);
  
    #ifdef WITH_INSPECTOR
    Inspector::instance().registerGlobalContext("main", m_context);
    #endif
  
    installNativeHook<&JSCExecutor::nativeFlushQueueImmediate>("nativeFlushQueueImmediate");
    installNativeHook<&JSCExecutor::nativeCallSyncHook>("nativeCallSyncHook");
  
    // Webworker support
    installNativeHook<&JSCExecutor::nativeStartWorker>("nativeStartWorker");
    installNativeHook<&JSCExecutor::nativePostMessageToWorker>("nativePostMessageToWorker");
    installNativeHook<&JSCExecutor::nativeTerminateWorker>("nativeTerminateWorker");
    installNativeHook<&JSCExecutor::nativeCallSyncHook>("nativeCallSyncHook");
  
    installGlobalFunction(m_context, "nativeLoggingHook", JSNativeHooks::loggingHook);
    installGlobalFunction(m_context, "nativePerformanceNow", JSNativeHooks::nowHook);
  
    #if DEBUG
    installGlobalFunction(m_context, "nativeInjectHMRUpdate", nativeInjectHMRUpdate);
    #endif
  
    #if defined(WITH_JSC_EXTRA_TRACING) || DEBUG
    addNativeTracingHooks(m_context);
    #endif
  
    #ifdef WITH_JSC_EXTRA_TRACING
    addNativeProfilingHooks(m_context);
    addNativeTracingLegacyHooks(m_context);
    PerfLogging::installNativeHooks(m_context);
  
    initSamplingProfilerOnMainJSCThread(m_context);
    #endif
  
    #ifdef WITH_FB_MEMORY_PROFILING
    addNativeMemoryHooks(m_context);
    #endif
  
    #ifdef JSC_HAS_PERF_STATS_API
    addJSCPerfStatsHooks(m_context);
    #endif
  
    #if defined(WITH_FB_JSC_TUNING) && defined(__ANDROID__)
    configureJSContextForAndroid(m_context, m_jscConfig, m_deviceCacheDir);
    #endif
  }
  
  void JSCExecutor::terminateOnJSVMThread() {
    // terminateOwnedWebWorker mutates m_ownedWorkers so collect all the workers
    // to terminate first
    std::vector<int> workerIds;
    for (auto& it : m_ownedWorkers) {
      workerIds.push_back(it.first);
    }
    for (int workerId : workerIds) {
      terminateOwnedWebWorker(workerId);
    }
  
    m_nativeModules.reset();
  
    #ifdef WITH_INSPECTOR
    Inspector::instance().unregisterGlobalContext(m_context);
    #endif
  
    JSC_JSGlobalContextRelease(m_context);
    m_context = nullptr;
  }
  
  #ifdef WITH_FBJSCEXTENSIONS
  static const char* explainLoadSourceError(JSLoadSourceError err) {
    switch (err) {
    case JSLoadSourceErrorNone:
      return "No error encountered during source load";
  
    case JSLoadSourceErrorOnRead:
      return "Error reading source";
  
    case JSLoadSourceErrorNotCompiled:
      return "Source is not compiled";
  
    case JSLoadSourceErrorVersionMismatch:
      return "Source version not supported";
  
    case JSLoadSourceErrorUnknown:
      return "Unknown error occurred when loading source";
  
    default:
      return "Bad error code";
    }
  }
  #endif
  
  #ifdef WITH_FBJSCEXTENSIONS
  void JSCExecutor::loadApplicationScript(
      std::string bundlePath,
      std::string sourceURL,
      int flags) {
    SystraceSection s("JSCExecutor::loadApplicationScript",
                      "sourceURL", sourceURL);
  
    if (!(flags & (UNPACKED_JS_SOURCE | UNPACKED_BYTECODE))) {
      throw RecoverableError("Optimized bundle with no unpacked source or bytecode");
    }
  
    String jsSourceURL(m_context, sourceURL.c_str());
    JSSourceCodeRef sourceCode = nullptr;
    SCOPE_EXIT {
      if (sourceCode) {
        JSReleaseSourceCode(sourceCode);
      }
    };
  
    if (flags & UNPACKED_BYTECODE) {
      int fd = open((bundlePath + UNPACKED_BYTECODE_SUFFIX).c_str(), O_RDONLY);
      RecoverableError::runRethrowingAsRecoverable<std::system_error>([fd]() {
          folly::checkUnixError(fd, "Couldn't open compiled bundle");
        });
      SCOPE_EXIT { close(fd); };
  
      JSLoadSourceError jsError;
      sourceCode = JSCreateCompiledSourceCode(fd, jsSourceURL, &jsError);
  
      if (!sourceCode) {
        throw RecoverableError(explainLoadSourceError(jsError));
      }
    } else {
      auto jsScriptBigString = JSBigOptimizedBundleString::fromOptimizedBundle(bundlePath);
      if (!jsScriptBigString->isAscii()) {
        LOG(WARNING) << "Bundle is not ASCII encoded - falling back to the slow path";
        return loadApplicationScript(std::move(jsScriptBigString), sourceURL);
      }
  
      sourceCode = JSCreateSourceCode(
        jsScriptBigString->fd(),
        jsSourceURL,
        jsScriptBigString->hash(),
        true);
    }
  
    ReactABI14_0_0Marker::logMarker("RUN_JS_BUNDLE_START");
  
    evaluateSourceCode(m_context, sourceCode, jsSourceURL);
  
    bindBridge();
  
    flush();
    ReactABI14_0_0Marker::logMarker("CREATE_REACT_CONTEXT_END");
    ReactABI14_0_0Marker::logMarker("RUN_JS_BUNDLE_END");
  }
  #endif
  
  #ifdef WITH_FBJSCEXTENSIONS
  void JSCExecutor::loadApplicationScript(
      int fd,
      std::string sourceURL)
  {
    String jsSourceURL(m_context, sourceURL.c_str());
  
    JSLoadSourceError jsError;
    auto bcSourceCode = JSCreateCompiledSourceCode(fd, jsSourceURL, &jsError);
  
    switch (jsError) {
    case JSLoadSourceErrorOnRead:
    case JSLoadSourceErrorNotCompiled:
      // Not bytecode, fall through.
      return JSExecutor::loadApplicationScript(fd, sourceURL);
  
    case JSLoadSourceErrorNone:
      if (!bcSourceCode) {
        throw std::runtime_error("Unexpected error opening compiled bundle");
      }
      break;
  
    case JSLoadSourceErrorVersionMismatch:
    case JSLoadSourceErrorUnknown:
      throw RecoverableError(explainLoadSourceError(jsError));
    }
  
    ReactABI14_0_0Marker::logMarker("RUN_JS_BUNDLE_START");
  
    evaluateSourceCode(m_context, bcSourceCode, jsSourceURL);
  
    // TODO(luk): t13903306 Remove this check once we make native modules
    // working for java2js
    if (m_delegate) {
      bindBridge();
      flush();
    }
  
    ReactABI14_0_0Marker::logMarker("CREATE_REACT_CONTEXT_END");
    ReactABI14_0_0Marker::logMarker("RUN_JS_BUNDLE_END");
  }
  #endif
  
  void JSCExecutor::loadApplicationScript(std::unique_ptr<const JSBigString> script, std::string sourceURL) throw(JSException) {
    SystraceSection s("JSCExecutor::loadApplicationScript",
                      "sourceURL", sourceURL);
  
    #ifdef WITH_FBSYSTRACE
    fbsystrace_begin_section(
      TRACE_TAG_REACT_CXX_BRIDGE,
      "JSCExecutor::loadApplicationScript-createExpectingAscii");
    #endif
  
    ReactABI14_0_0Marker::logMarker("RUN_JS_BUNDLE_START");
  
    ReactABI14_0_0Marker::logMarker("loadApplicationScript_startStringConvert");
    String jsScript = jsStringFromBigString(m_context, *script);
    ReactABI14_0_0Marker::logMarker("loadApplicationScript_endStringConvert");
  
    #ifdef WITH_FBSYSTRACE
    fbsystrace_end_section(TRACE_TAG_REACT_CXX_BRIDGE);
    #endif
  
    String jsSourceURL(m_context, sourceURL.c_str());
    evaluateScript(m_context, jsScript, jsSourceURL);
  
    // TODO(luk): t13903306 Remove this check once we make native modules working for java2js
    if (m_delegate) {
      bindBridge();
      flush();
    }
    ReactABI14_0_0Marker::logMarker("CREATE_REACT_CONTEXT_END");
    ReactABI14_0_0Marker::logMarker("RUN_JS_BUNDLE_END");
  }
  
  void JSCExecutor::setJSModulesUnbundle(std::unique_ptr<JSModulesUnbundle> unbundle) {
    if (!m_unbundle) {
      installNativeHook<&JSCExecutor::nativeRequire>("nativeRequire");
    }
    m_unbundle = std::move(unbundle);
  }
  
  void JSCExecutor::bindBridge() throw(JSException) {
    SystraceSection s("JSCExecutor::bindBridge");
    auto global = Object::getGlobalObject(m_context);
    auto batchedBridgeValue = global.getProperty("__fbBatchedBridge");
    if (batchedBridgeValue.isUndefined()) {
      throwJSExecutionException("Could not get BatchedBridge, make sure your bundle is packaged correctly");
    }
  
    auto batchedBridge = batchedBridgeValue.asObject();
    m_callFunctionReturnFlushedQueueJS = batchedBridge.getProperty("callFunctionReturnFlushedQueue").asObject();
    m_invokeCallbackAndReturnFlushedQueueJS = batchedBridge.getProperty("invokeCallbackAndReturnFlushedQueue").asObject();
    m_flushedQueueJS = batchedBridge.getProperty("flushedQueue").asObject();
    m_callFunctionReturnResultAndFlushedQueueJS = batchedBridge.getProperty("callFunctionReturnResultAndFlushedQueue").asObject();
  }
  
  void JSCExecutor::callNativeModules(Value&& value) {
    SystraceSection s("JSCExecutor::callNativeModules");
    try {
      auto calls = value.toJSONString();
      m_delegate->callNativeModules(*this, folly::parseJson(calls), true);
    } catch (...) {
      std::string message = "Error in callNativeModules()";
      try {
        message += ":" + value.toString().str();
      } catch (...) {
        // ignored
      }
      std::throw_with_nested(std::runtime_error(message));
    }
  }
  
  void JSCExecutor::flush() {
    SystraceSection s("JSCExecutor::flush");
    callNativeModules(m_flushedQueueJS->callAsFunction({}));
  }
  
  void JSCExecutor::callFunction(const std::string& moduleId, const std::string& methodId, const folly::dynamic& arguments) {
    SystraceSection s("JSCExecutor::callFunction");
    // This weird pattern is because Value is not default constructible.
    // The lambda is inlined, so there's no overhead.
  
    auto result = [&] {
      try {
        return m_callFunctionReturnFlushedQueueJS->callAsFunction({
          Value(m_context, String::createExpectingAscii(m_context, moduleId)),
          Value(m_context, String::createExpectingAscii(m_context, methodId)),
          Value::fromDynamic(m_context, std::move(arguments))
        });
      } catch (...) {
        std::throw_with_nested(
          std::runtime_error("Error calling function: " + moduleId + ":" + methodId));
      }
    }();
  
    callNativeModules(std::move(result));
  }
  
  void JSCExecutor::invokeCallback(const double callbackId, const folly::dynamic& arguments) {
    SystraceSection s("JSCExecutor::invokeCallback");
    auto result = [&] {
      try {
        return m_invokeCallbackAndReturnFlushedQueueJS->callAsFunction({
          Value::makeNumber(m_context, callbackId),
          Value::fromDynamic(m_context, std::move(arguments))
        });
      } catch (...) {
        std::throw_with_nested(
          std::runtime_error(folly::to<std::string>("Error invoking callback.", callbackId)));
      }
    }();
  
    callNativeModules(std::move(result));
  }
  
  Value JSCExecutor::callFunctionSyncWithValue(
      const std::string& module, const std::string& method, Value args) {
    SystraceSection s("JSCExecutor::callFunction");
  
    Object result = m_callFunctionReturnResultAndFlushedQueueJS->callAsFunction({
      Value(m_context, String::createExpectingAscii(m_context, module)),
      Value(m_context, String::createExpectingAscii(m_context, method)),
      std::move(args),
    }).asObject();
  
    Value length = result.getProperty("length");
  
    if (!length.isNumber() || length.asInteger() != 2) {
      std::runtime_error("Return value of a callFunction must be an array of size 2");
    }
  
    callNativeModules(result.getPropertyAtIndex(1));
    return result.getPropertyAtIndex(0);
  }
  
  void JSCExecutor::setGlobalVariable(std::string propName, std::unique_ptr<const JSBigString> jsonValue) {
    try {
      SystraceSection s("JSCExecutor.setGlobalVariable",
                        "propName", propName);
  
      auto valueToInject = Value::fromJSON(m_context, jsStringFromBigString(m_context, *jsonValue));
      Object::getGlobalObject(m_context).setProperty(propName.c_str(), valueToInject);
    } catch (...) {
      std::throw_with_nested(std::runtime_error("Error setting global variable: " + propName));
    }
  }
  
  void* JSCExecutor::getJavaScriptContext() {
    return m_context;
  }
  
  bool JSCExecutor::supportsProfiling() {
    #ifdef WITH_FBSYSTRACE
    return true;
    #else
    return false;
    #endif
  }
  
  void JSCExecutor::startProfiler(const std::string &titleString) {
    #ifdef WITH_JSC_EXTRA_TRACING
    String title(m_context, titleString.c_str());
    #if WITH_REACT_INTERNAL_SETTINGS
    JSStartProfiling(m_context, title, false);
    #else
    JSStartProfiling(m_context, title);
    #endif
    #endif
  }
  
  void JSCExecutor::stopProfiler(const std::string &titleString, const std::string& filename) {
    #ifdef WITH_JSC_EXTRA_TRACING
    String title(m_context, titleString.c_str());
    facebook::ReactABI14_0_0::stopAndOutputProfilingFile(m_context, title, filename.c_str());
    #endif
  }
  
  void JSCExecutor::handleMemoryPressureUiHidden() {
    #ifdef WITH_JSC_MEMORY_PRESSURE
    JSHandleMemoryPressure(this, m_context, JSMemoryPressure::UI_HIDDEN);
    #endif
  }
  
  void JSCExecutor::handleMemoryPressureModerate() {
    #ifdef WITH_JSC_MEMORY_PRESSURE
    JSHandleMemoryPressure(this, m_context, JSMemoryPressure::MODERATE);
    #endif
  }
  
  void JSCExecutor::handleMemoryPressureCritical() {
    #ifdef WITH_JSC_MEMORY_PRESSURE
    JSHandleMemoryPressure(this, m_context, JSMemoryPressure::CRITICAL);
    #endif
  }
  
  void JSCExecutor::flushQueueImmediate(Value&& queue) {
    auto queueStr = queue.toJSONString();
    m_delegate->callNativeModules(*this, folly::parseJson(queueStr), false);
  }
  
  void JSCExecutor::loadModule(uint32_t moduleId) {
    auto module = m_unbundle->getModule(moduleId);
    auto sourceUrl = String::createExpectingAscii(m_context, module.name);
    auto source = String::createExpectingAscii(m_context, module.code);
    evaluateScript(m_context, source, sourceUrl);
  }
  
  int JSCExecutor::addWebWorker(
      std::string scriptURL,
      JSValueRef workerRef,
      JSValueRef globalObjRef) {
    static std::atomic_int nextWorkerId(1);
    int workerId = nextWorkerId++;
  
    Object globalObj = Value(m_context, globalObjRef).asObject();
  
    auto workerJscConfig = m_jscConfig;
    workerJscConfig["isWebWorker"] = true;
  
    std::shared_ptr<MessageQueueThread> workerMQT =
      WebWorkerUtil::createWebWorkerThread(workerId, m_messageQueueThread.get());
    std::unique_ptr<JSCExecutor> worker;
    workerMQT->runOnQueueSync([this, &worker, &workerMQT, &scriptURL, &globalObj, workerId, &workerJscConfig] () {
      worker.reset(new JSCExecutor(m_delegate, workerMQT, workerId, this, std::move(scriptURL),
                                   globalObj.toJSONMap(), workerJscConfig));
    });
  
    Object workerObj = Value(m_context, workerRef).asObject();
    workerObj.makeProtected();
  
    JSCExecutor *workerPtr = worker.get();
    std::shared_ptr<MessageQueueThread> sharedMessageQueueThread = worker->m_messageQueueThread;
    m_delegate->registerExecutor(
        std::move(worker),
        std::move(sharedMessageQueueThread));
  
    m_ownedWorkers.emplace(
        std::piecewise_construct,
        std::forward_as_tuple(workerId),
        std::forward_as_tuple(workerPtr, std::move(workerObj)));
  
    return workerId;
  }
  
  void JSCExecutor::postMessageToOwnedWebWorker(int workerId, JSValueRef message) {
    auto worker = m_ownedWorkers.at(workerId).executor;
    std::string msgString = Value(m_context, message).toJSONString();
  
    std::shared_ptr<bool> isWorkerDestroyed = worker->m_isDestroyed;
    worker->m_messageQueueThread->runOnQueue([isWorkerDestroyed, worker, msgString] () {
      if (*isWorkerDestroyed) {
        return;
      }
      worker->receiveMessageFromOwner(msgString);
    });
  }
  
  void JSCExecutor::postMessageToOwner(JSValueRef msg) {
    std::string msgString = Value(m_context, msg).toJSONString();
    std::shared_ptr<bool> ownerIsDestroyed = m_owner->m_isDestroyed;
    m_owner->m_messageQueueThread->runOnQueue([workerId=m_workerId, owner=m_owner, ownerIsDestroyed, msgString] () {
      if (*ownerIsDestroyed) {
        return;
      }
      owner->receiveMessageFromOwnedWebWorker(workerId, msgString);
    });
  }
  
  void JSCExecutor::receiveMessageFromOwnedWebWorker(int workerId, const std::string& json) {
    Object* workerObj;
    try {
      workerObj = &m_ownedWorkers.at(workerId).jsObj;
    } catch (std::out_of_range& e) {
      // Worker was already terminated
      return;
    }
  
    Value onmessageValue = workerObj->getProperty("onmessage");
    if (onmessageValue.isUndefined()) {
      return;
    }
  
    JSValueRef args[] = { createMessageObject(json) };
    onmessageValue.asObject().callAsFunction(1, args);
  
    flush();
  }
  
  void JSCExecutor::receiveMessageFromOwner(const std::string& msgString) {
    CHECK(m_owner) << "Received message in a Executor that doesn't have an owner!";
  
    JSValueRef args[] = { createMessageObject(msgString) };
    Value onmessageValue = Object::getGlobalObject(m_context).getProperty("onmessage");
    onmessageValue.asObject().callAsFunction(1, args);
  }
  
  void JSCExecutor::terminateOwnedWebWorker(int workerId) {
    auto& workerRegistration = m_ownedWorkers.at(workerId);
    std::shared_ptr<MessageQueueThread> workerMQT = workerRegistration.executor->m_messageQueueThread;
    m_ownedWorkers.erase(workerId);
  
    workerMQT->runOnQueueSync([this, &workerMQT] {
      workerMQT->quitSynchronous();
      std::unique_ptr<JSExecutor> worker = m_delegate->unregisterExecutor(*this);
      worker->destroy();
      worker.reset();
    });
  }
  
  Object JSCExecutor::createMessageObject(const std::string& msgJson) {
    Value rebornJSMsg = Value::fromJSON(m_context, String(m_context, msgJson.c_str()));
    Object messageObject = Object::create(m_context);
    messageObject.setProperty("data", rebornJSMsg);
    return messageObject;
  }
  
  // Native JS hooks
  template<JSValueRef (JSCExecutor::*method)(size_t, const JSValueRef[])>
  void JSCExecutor::installNativeHook(const char* name) {
    installGlobalFunction(m_context, name, exceptionWrapMethod<method>());
  }
  
  JSValueRef JSCExecutor::getNativeModule(JSObjectRef object, JSStringRef propertyName) {
    if (JSC_JSStringIsEqualToUTF8CString(m_context, propertyName, "name")) {
      return Value(m_context, String(m_context, "NativeModules"));
    }
  
    return m_nativeModules.getModule(m_context, propertyName);
  }
  
  JSValueRef JSCExecutor::nativePostMessage(
      size_t argumentCount,
      const JSValueRef arguments[]) {
    if (argumentCount != 1) {
      throw std::invalid_argument("Got wrong number of args");
    }
    JSValueRef msg = arguments[0];
    postMessageToOwner(msg);
  
    return Value::makeUndefined(m_context);
  }
  
  JSValueRef JSCExecutor::nativeRequire(
    size_t argumentCount,
    const JSValueRef arguments[]) {
  
    if (argumentCount != 1) {
      throw std::invalid_argument("Got wrong number of args");
    }
  
    double moduleId = Value(m_context, arguments[0]).asNumber();
    if (moduleId <= 0) {
      throw std::invalid_argument(folly::to<std::string>("Received invalid module ID: ",
        Value(m_context, arguments[0]).toString().str()));
    }
  
    loadModule(moduleId);
    return Value::makeUndefined(m_context);
  }
  
  JSValueRef JSCExecutor::nativeFlushQueueImmediate(
      size_t argumentCount,
      const JSValueRef arguments[]) {
    if (argumentCount != 1) {
      throw std::invalid_argument("Got wrong number of args");
    }
  
    flushQueueImmediate(Value(m_context, arguments[0]));
    return Value::makeUndefined(m_context);
  }
  
  JSValueRef JSCExecutor::nativeStartWorker(
      size_t argumentCount,
      const JSValueRef arguments[]) {
    if (argumentCount != 3) {
      throw std::invalid_argument("Got wrong number of args");
    }
  
    std::string scriptFile = Value(m_context, arguments[0]).toString().str();
  
    JSValueRef worker = arguments[1];
    JSValueRef globalObj = arguments[2];
  
    int workerId = addWebWorker(scriptFile, worker, globalObj);
  
    return Value::makeNumber(m_context, workerId);
  }
  
  JSValueRef JSCExecutor::nativePostMessageToWorker(
      size_t argumentCount,
      const JSValueRef arguments[]) {
    if (argumentCount != 2) {
      throw std::invalid_argument("Got wrong number of args");
    }
  
    double workerDouble = Value(m_context, arguments[0]).asNumber();
    if (workerDouble != workerDouble) {
      throw std::invalid_argument("Got invalid worker id");
    }
  
    postMessageToOwnedWebWorker((int) workerDouble, arguments[1]);
  
    return Value::makeUndefined(m_context);
  }
  
  JSValueRef JSCExecutor::nativeTerminateWorker(
      size_t argumentCount,
      const JSValueRef arguments[]) {
    if (argumentCount != 1) {
      throw std::invalid_argument("Got wrong number of args");
    }
  
    double workerDouble = Value(m_context, arguments[0]).asNumber();
    if (workerDouble != workerDouble) {
      std::invalid_argument("Got invalid worker id");
    }
  
    terminateOwnedWebWorker((int) workerDouble);
  
    return Value::makeUndefined(m_context);
  }
  
  JSValueRef JSCExecutor::nativeCallSyncHook(
      size_t argumentCount,
      const JSValueRef arguments[]) {
    if (argumentCount != 3) {
      throw std::invalid_argument("Got wrong number of args");
    }
  
    unsigned int moduleId = Value(m_context, arguments[0]).asUnsignedInteger();
    unsigned int methodId = Value(m_context, arguments[1]).asUnsignedInteger();
    std::string argsJson = Value(m_context, arguments[2]).toJSONString();
  
    MethodCallResult result = m_delegate->callSerializableNativeHook(
        *this,
        moduleId,
        methodId,
        argsJson);
    if (result.isUndefined) {
      return Value::makeUndefined(m_context);
    }
    return Value::fromDynamic(m_context, result.result);
  }
  
  } }
  