@ 261103.1

230419.1 · 7234c7dcc614058537fd285b92881fdbba73f379

Implement JSI for Node-API with tests

reading declarations

.clang-format

new file mode 100644@@ -0,0 +1,92 @@+---+AccessModifierOffset: -1+AlignAfterOpenBracket: AlwaysBreak+AlignConsecutiveAssignments: false+AlignConsecutiveDeclarations: false+AlignEscapedNewlinesLeft: true+AlignOperands:   false+AlignTrailingComments: false+AllowAllParametersOfDeclarationOnNextLine: false+AllowShortBlocksOnASingleLine: false+AllowShortCaseLabelsOnASingleLine: false+AllowShortFunctionsOnASingleLine: Empty+AllowShortIfStatementsOnASingleLine: false+AllowShortLoopsOnASingleLine: false+AlwaysBreakAfterReturnType: None+AlwaysBreakBeforeMultilineStrings: true+AlwaysBreakTemplateDeclarations: true+BinPackArguments: false+BinPackParameters: false+BraceWrapping:+  AfterClass:      false+  AfterControlStatement: false+  AfterEnum:       false+  AfterFunction:   false+  AfterNamespace:  false+  AfterObjCDeclaration: false+  AfterStruct:     false+  AfterUnion:      false+  BeforeCatch:     false+  BeforeElse:      false+  IndentBraces:    false+BreakBeforeBinaryOperators: None+BreakBeforeBraces: Attach+BreakBeforeTernaryOperators: true+BreakConstructorInitializersBeforeComma: false+BreakAfterJavaFieldAnnotations: false+BreakStringLiterals: false+ColumnLimit:       120+CommentPragmas:  '^ IWYU pragma:'+ConstructorInitializerAllOnOneLineOrOnePerLine: true+ConstructorInitializerIndentWidth: 4+ContinuationIndentWidth: 4+Cpp11BracedListStyle: true+DerivePointerAlignment: false+DisableFormat:   false+ForEachMacros:   [ FOR_EACH_RANGE, FOR_EACH, TEST_CLASS, TEST_CLASS_EX ]+IncludeBlocks: Preserve+IncludeCategories:+  - Regex:           'pch.h'+    Priority:        -1+  - Regex:           '.*\.g\..*'+    Priority:        1+  - Regex:           '^<.*\.h(pp)?>'+    Priority:        2+  - Regex:           '^<.*'+    Priority:        3+  - Regex:           '.*'+    Priority:        4+IndentCaseLabels: true+IndentWidth:     2+IndentWrappedFunctionNames: false+KeepEmptyLinesAtTheStartOfBlocks: false+MacroBlockBegin: ''+MacroBlockEnd:   ''+MaxEmptyLinesToKeep: 1+NamespaceIndentation: None+ObjCBlockIndentWidth: 2+ObjCSpaceAfterProperty: true+ObjCSpaceBeforeProtocolList: true+PenaltyBreakBeforeFirstCallParameter: 1+PenaltyBreakComment: 300+PenaltyBreakFirstLessLess: 120+PenaltyBreakString: 1000+PenaltyExcessCharacter: 1000000+PenaltyReturnTypeOnItsOwnLine: 200+PointerAlignment: Right+ReflowComments:  true+SortIncludes:    true+SpaceAfterCStyleCast: false+SpaceBeforeAssignmentOperators: true+SpaceBeforeParens: ControlStatements+SpaceInEmptyParentheses: false+SpacesBeforeTrailingComments: 1+SpacesInAngles:  false+SpacesInContainerLiterals: true+SpacesInCStyleCastParentheses: false+SpacesInParentheses: false+SpacesInSquareBrackets: false+Standard:        Cpp11+TabWidth:        8+UseTab:          Never+...

.gitignore

@@ -348,3 +348,7 @@ MigrationBackup/  # Ionide (cross platform F# VS Code tools) working folder .ionide/++.vscode/+build/+out/

CMakeLists.txt

new file mode 100644@@ -0,0 +1,18 @@+# Copyright (c) Microsoft Corporation.+# Licensed under the MIT License.++# set minimum cmake version+cmake_minimum_required(VERSION 3.16 FATAL_ERROR)+cmake_policy(SET CMP0092 NEW)++# project name and language+project(NodeApiJsi+  HOMEPAGE_URL "https://github.com/microsoft/node-api-jsi"+  LANGUAGES CXX)++set(CMAKE_CXX_STANDARD 17)+set(CMAKE_CXX_STANDARD_REQUIRED ON)+set(CMAKE_CXX_EXTENSIONS OFF)++add_subdirectory(external)+add_subdirectory(tests)

README.md

@@ -1,14 +1,15 @@-# Project+# JSI for Node-API -> This repo has been populated by an initial template to help get you started. Please-> make sure to update the content to build a great experience for community-building.+This project implements JSI for Node-API. -As the maintainer of this project, please make a few updates:+[Node-API](https://nodejs.org/api/n-api.html) is an ABI safe API defined by Node.JS project. -- Improving this README.MD file to provide a great experience-- Updating SUPPORT.MD with content about this project's support experience-- Understanding the security reporting process in SECURITY.MD-- Remove this section from the README+[JSI](https://github.com/facebook/hermes/tree/main/API/jsi/jsi) is a C++ API used+by [React Native](https://github.com/facebook/react-native) to abstract access to+JavaScript engines.++The JSI implementation on top of Node-API enables use of JS engines +that implement Node-API with React Native.  ## Contributing 

SUPPORT.md

@@ -1,13 +1,3 @@-# TODO: The maintainer of this repo has not yet edited this file
-
-**REPO OWNER**: Do you want Customer Service & Support (CSS) support for this product/project?
-
-- **No CSS support:** Fill out this template with information about how to file issues and get help.
-- **Yes CSS support:** Fill out an intake form at [aka.ms/onboardsupport](https://aka.ms/onboardsupport). CSS will work with/help you to determine next steps.
-- **Not sure?** Fill out an intake as though the answer were "Yes". CSS will help you decide.
-
-*Then remove this first heading from this SUPPORT.MD file before publishing your repo.*
-
 # Support
 
 ## How to file issues and get help  
@@ -16,10 +6,9 @@ This project uses GitHub Issues to track bugs and feature requests. Please searc issues before filing new issues to avoid duplicates.  For new issues, file your bug or 
 feature request as a new Issue.
 
-For help and questions about using this project, please **REPO MAINTAINER: INSERT INSTRUCTIONS HERE 
-FOR HOW TO ENGAGE REPO OWNERS OR COMMUNITY FOR HELP. COULD BE A STACK OVERFLOW TAG OR OTHER
-CHANNEL. WHERE WILL YOU HELP PEOPLE?**.
+For help and questions about using this project, please refer to the product documentation 
+or use GitHub discussions for this project.
 
 ## Microsoft Support Policy  
 
-Support for this **PROJECT or PRODUCT** is limited to the resources listed above.
+Support for this **JSI for Node-API** is limited to the resources listed above.

external/CMakeLists.txt

new file mode 100644@@ -0,0 +1,4 @@+# Copyright (c) Microsoft Corporation.+# Licensed under the MIT License.++add_subdirectory(gtest)

external/gtest/CMakeLists.txt

new file mode 100644@@ -0,0 +1,45 @@+# Copyright (c) Microsoft Corporation.+# Licensed under the MIT License.++# the following code to fetch googletest+# is inspired by and adapted after:+#   - https://cmake.org/cmake/help/v3.11/module/FetchContent.html+#+# We use version of GTest as in the https://github.com/Microsoft/TestAdapterForGoogleTest+# It allows to run GTests in Visual Studio and ADO.+# Please update the GIT_TAG below after the TAFGT is updated the reference.+include(FetchContent)++FetchContent_Declare(+  googletest+  URL https://github.com/google/googletest/archive/06519cedc3159de8b36a504766ad6b7966555f10.zip+  DOWNLOAD_EXTRACT_TIMESTAMP+)++FetchContent_GetProperties(googletest)++if(NOT googletest_POPULATED)+  FetchContent_Populate(googletest)++  # Prevent GoogleTest from overriding our compiler/linker options+  # when building with Visual Studio+  set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)+  # Prevent GoogleTest from using PThreads+  set(gtest_disable_pthreads ON CACHE BOOL "" FORCE)++  # adds the targets: gtest, gtest_main, gmock, gmock_main+  add_subdirectory(+    ${googletest_SOURCE_DIR}+    ${googletest_BINARY_DIR}+    )++  # Silence std::tr1 warning on MSVC+  if(MSVC)+    foreach(_tgt gtest gtest_main gmock gmock_main)+      target_compile_definitions(${_tgt}+        PRIVATE+          "_SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING"+        )+    endforeach()+  endif()+endif()

jsi/.clang-format

new file mode 100644@@ -0,0 +1,5 @@+---+BasedOnStyle: InheritParentConfig+PointerAlignment: Left+ColumnLimit:      80+...

jsi/CMakeLists.txt

new file mode 100644@@ -0,0 +1,31 @@+# Copyright (c) Meta Platforms, Inc. and affiliates.+#+# This source code is licensed under the MIT license found in the+# LICENSE file in the root directory of this source tree.++set(CMAKE_CXX_STANDARD 14)+set(CMAKE_CXX_STANDARD_REQUIRED ON)++add_library(jsi+        jsi.cpp)++target_include_directories(jsi PUBLIC ..)+++set(jsi_compile_flags "")+if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" OR+    "${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU")+  list(APPEND jsi_compile_flags "-Wno-non-virtual-dtor")+elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "MSVC")+  # Turn on Error Handling in MSVC, otherwise objects are not destructed+  # when they go out of scope due to exceptions.+  list(APPEND jsi_compile_flags "/EHsc")+endif()+if (HERMES_ENABLE_BITCODE)+  list(APPEND jsi_compile_flags "-fembed-bitcode")+endif ()+target_compile_options(jsi PRIVATE ${jsi_compile_flags})++install(DIRECTORY "${PROJECT_SOURCE_DIR}/API/jsi/" DESTINATION include+  FILES_MATCHING PATTERN "*.h"+  PATTERN "test" EXCLUDE)

jsi/JSIDynamic.cpp

new file mode 100644@@ -0,0 +1,207 @@+/*+ * Copyright (c) Meta Platforms, Inc. and affiliates.+ *+ * This source code is licensed under the MIT license found in the+ * LICENSE file in the root directory of this source tree.+ */++#include "JSIDynamic.h"++#include <glog/logging.h>++#include <folly/dynamic.h>+#include <jsi/jsi.h>++using namespace facebook::jsi;++namespace facebook {+namespace jsi {++namespace {++struct FromDynamic {+  FromDynamic(const folly::dynamic* dynArg, Object objArg)+      : dyn(dynArg), obj(std::move(objArg)) {}++  const folly::dynamic* dyn;+  Object obj;+};++// This converts one element.  If it's a collection, it gets pushed onto+// the stack for later processing.+Value valueFromDynamicShallow(+    Runtime& runtime,+    std::vector<FromDynamic>& stack,+    const folly::dynamic& dyn) {+  switch (dyn.type()) {+    case folly::dynamic::NULLT:+      return Value::null();+    case folly::dynamic::ARRAY: {+      Object arr = Array(runtime, dyn.size());+      Value ret = Value(runtime, arr);+      stack.emplace_back(&dyn, std::move(arr));+      return ret;+    }+    case folly::dynamic::BOOL:+      return Value(dyn.getBool());+    case folly::dynamic::DOUBLE:+      return dyn.getDouble();+    case folly::dynamic::INT64:+      return Value((double)dyn.getInt());+    case folly::dynamic::OBJECT: {+      auto obj = Object(runtime);+      Value ret = Value(runtime, obj);+      stack.emplace_back(&dyn, std::move(obj));+      return ret;+    }+    case folly::dynamic::STRING:+      return Value(String::createFromUtf8(runtime, dyn.getString()));+  }+  CHECK(false);+}++} // namespace++Value valueFromDynamic(Runtime& runtime, const folly::dynamic& dynInput) {+  std::vector<FromDynamic> stack;++  Value ret = valueFromDynamicShallow(runtime, stack, dynInput);++  while (!stack.empty()) {+    auto top = std::move(stack.back());+    stack.pop_back();++    switch (top.dyn->type()) {+      case folly::dynamic::ARRAY: {+        Array arr = std::move(top.obj).getArray(runtime);+        for (size_t i = 0; i < top.dyn->size(); ++i) {+          arr.setValueAtIndex(+              runtime,+              i,+              valueFromDynamicShallow(runtime, stack, (*top.dyn)[i]));+        }+        break;+      }+      case folly::dynamic::OBJECT: {+        Object obj = std::move(top.obj);+        for (const auto& element : top.dyn->items()) {+          if (element.first.isNumber() || element.first.isString()) {+            obj.setProperty(+                runtime,+                PropNameID::forUtf8(runtime, element.first.asString()),+                valueFromDynamicShallow(runtime, stack, element.second));+          }+        }+        break;+      }+      default:+        CHECK(false);+    }+  }++  return ret;+}++namespace {++struct FromValue {+  FromValue(folly::dynamic* dynArg, Object objArg)+      : dyn(dynArg), obj(std::move(objArg)) {}++  folly::dynamic* dyn;+  Object obj;+};++// This converts one element.  If it's a collection, it gets pushed+// onto the stack for later processing.  The output is created by+// mutating the output argument, because we need its actual pointer to+// push onto the stack.+void dynamicFromValueShallow(+    Runtime& runtime,+    std::vector<FromValue>& stack,+    const jsi::Value& value,+    folly::dynamic& output) {+  if (value.isUndefined() || value.isNull()) {+    output = nullptr;+  } else if (value.isBool()) {+    output = value.getBool();+  } else if (value.isNumber()) {+    output = value.getNumber();+  } else if (value.isString()) {+    output = value.getString(runtime).utf8(runtime);+  } else if (value.isObject()) {+    Object obj = value.getObject(runtime);+    if (obj.isArray(runtime)) {+      output = folly::dynamic::array();+    } else if (obj.isFunction(runtime)) {+      throw JSError(runtime, "JS Functions are not convertible to dynamic");+    } else {+      output = folly::dynamic::object();+    }+    stack.emplace_back(&output, std::move(obj));+#if JSI_VERSION >= 8+  } else if (value.isBigInt()) {+    throw JSError(runtime, "JS BigInts are not convertible to dynamic");+#endif+  } else if (value.isSymbol()) {+    throw JSError(runtime, "JS Symbols are not convertible to dynamic");+  } else {+    throw JSError(runtime, "Value is not convertible to dynamic");+  }+}++} // namespace++folly::dynamic dynamicFromValue(Runtime& runtime, const Value& valueInput) {+  std::vector<FromValue> stack;+  folly::dynamic ret;++  dynamicFromValueShallow(runtime, stack, valueInput, ret);++  while (!stack.empty()) {+    auto top = std::move(stack.back());+    stack.pop_back();++    if (top.obj.isArray(runtime)) {+      // Inserting into a dyn can invalidate references into it, so we+      // need to insert new elements up front, then push stuff onto+      // the stack.+      Array array = top.obj.getArray(runtime);+      size_t arraySize = array.size(runtime);+      for (size_t i = 0; i < arraySize; ++i) {+        top.dyn->push_back(nullptr);+      }+      for (size_t i = 0; i < arraySize; ++i) {+        dynamicFromValueShallow(+            runtime, stack, array.getValueAtIndex(runtime, i), top.dyn->at(i));+      }+    } else {+      Array names = top.obj.getPropertyNames(runtime);+      std::vector<std::pair<std::string, jsi::Value>> props;+      for (size_t i = 0; i < names.size(runtime); ++i) {+        String name = names.getValueAtIndex(runtime, i).getString(runtime);+        Value prop = top.obj.getProperty(runtime, name);+        if (prop.isUndefined()) {+          continue;+        }+        // The JSC conversion uses JSON.stringify, which substitutes+        // null for a function, so we do the same here.  Just dropping+        // the pair might also work, but would require more testing.+        if (prop.isObject() && prop.getObject(runtime).isFunction(runtime)) {+          prop = Value::null();+        }+        props.emplace_back(name.utf8(runtime), std::move(prop));+        top.dyn->insert(props.back().first, nullptr);+      }+      for (const auto& prop : props) {+        dynamicFromValueShallow(+            runtime, stack, prop.second, (*top.dyn)[prop.first]);+      }+    }+  }++  return ret;+}++} // namespace jsi+} // namespace facebook

jsi/JSIDynamic.h

new file mode 100644@@ -0,0 +1,25 @@+/*+ * Copyright (c) Meta Platforms, Inc. and affiliates.+ *+ * This source code is licensed under the MIT license found in the+ * LICENSE file in the root directory of this source tree.+ */++#pragma once++#include <folly/dynamic.h>+#include <jsi/jsi.h>++namespace facebook {+namespace jsi {++facebook::jsi::Value valueFromDynamic(+    facebook::jsi::Runtime& runtime,+    const folly::dynamic& dyn);++folly::dynamic dynamicFromValue(+    facebook::jsi::Runtime& runtime,+    const facebook::jsi::Value& value);++} // namespace jsi+} // namespace facebook

jsi/README.md

new file mode 100644@@ -0,0 +1,25 @@+# JavaScript Interface ++This folder contains definitions for JSI.+JSI is the public API for Hermes engine.+It is being used by React Native project to work with JS engines.++## JSI versions++JSI has versions associated with the following commit hashes in the +https://github.com/facebook/hermes repo. ++| Version | Commit Hash                              | Commit Description+|--------:|:-----------------------------------------|------------------------------------------------------+|      10 | `b81666598672cb5f8b365fe6548d3273f216322e` | Clarify const-ness of JSI references+|       9 | `e6d887ae96bef5c71032f11ed1a9fb9fecec7b46` | Add external ArrayBuffers to JSI+|       8 | `4d64e61a1f9926eca0afd4eb38d17cea30bdc34c` | Add BigInt JSI API support+|         | `e8cc57311877464478da5421265bcc898098e136` | Add methods for creating and interacting with BigInt+|       7 | `4efad65b8266e3f26e04e3ca9addf92fc4d6ded8` | Add API for setting/getting native state+|       6 | `bc3cfb87fbfc82732936ec4445c9765bf9a5f08a` | Add BigInt skeleton+|       5 | `2b6d408980d7f23f50602fd88169c8a9881592a6` | Add PropNameID::fromSymbol+|       4 | `a5bee55c8301bb8662e408feee28bbc3e2a1fc81` | Introduce drainMicrotasks to JSI+|       3 | `0c9daa5a5eee7649558a53e3e541b80c89048c42` | Change jsi::Runtime::lockWeakObject to take a mutable ref+|       2 | `e0616e77e1ddc3ea5e2ccbca2e20dd0c4049c637` | Make it possible for a Runtime implementation to provide its own JSON parsing+|       1 | `3ba9615f80913764ecb6456779d502e31dde9e5d` | Fix build break in MSVC (#26462)+|       0 | `f22a18f67da3f03db59c1ec715d6ec3776b03fbf` | Initial commit\ No newline at end of file

jsi/decorator.h

new file mode 100644@@ -0,0 +1,828 @@+/*+ * Copyright (c) Meta Platforms, Inc. and affiliates.+ *+ * This source code is licensed under the MIT license found in the+ * LICENSE file in the root directory of this source tree.+ */++#pragma once++#include <tuple>++#include <jsi/instrumentation.h>+#include <jsi/jsi.h>++// This file contains objects to help API users create their own+// runtime adapters, i.e. if you want to compose runtimes to add your+// own behavior.++namespace facebook {+namespace jsi {++// Use this to wrap host functions. It will pass the member runtime as+// the first arg to the callback.  The first argument to the ctor+// should be the decorated runtime, not the plain one.+class DecoratedHostFunction {+ public:+  DecoratedHostFunction(Runtime& drt, HostFunctionType plainHF)+      : drt_(drt), plainHF_(std::move(plainHF)) {}++  Runtime& decoratedRuntime() {+    return drt_;+  }++  Value+  operator()(Runtime&, const Value& thisVal, const Value* args, size_t count) {+    return plainHF_(decoratedRuntime(), thisVal, args, count);+  }++ private:+  template <typename Plain, typename Base>+  friend class RuntimeDecorator;++  Runtime& drt_;+  HostFunctionType plainHF_;+};++// From the perspective of the caller, a plain HostObject is passed to+// the decorated Runtime, and the HostObject methods expect to get+// passed that Runtime.  But the plain Runtime will pass itself to its+// callback, so we need a helper here which curries the decorated+// Runtime, and calls the plain HostObject with it.+//+// If the concrete RuntimeDecorator derives DecoratedHostObject, it+// should call the base class get() and set() to invoke the plain+// HostObject functionality.  The Runtime& it passes does not matter,+// as it is not used.+class DecoratedHostObject : public HostObject {+ public:+  DecoratedHostObject(Runtime& drt, std::shared_ptr<HostObject> plainHO)+      : drt_(drt), plainHO_(plainHO) {}++  // The derived class methods can call this to get a reference to the+  // decorated runtime, since the rt passed to the callback will be+  // the plain runtime.+  Runtime& decoratedRuntime() {+    return drt_;+  }++  Value get(Runtime&, const PropNameID& name) override {+    return plainHO_->get(decoratedRuntime(), name);+  }++  void set(Runtime&, const PropNameID& name, const Value& value) override {+    plainHO_->set(decoratedRuntime(), name, value);+  }++  std::vector<PropNameID> getPropertyNames(Runtime&) override {+    return plainHO_->getPropertyNames(decoratedRuntime());+  }++ private:+  template <typename Plain, typename Base>+  friend class RuntimeDecorator;++  Runtime& drt_;+  std::shared_ptr<HostObject> plainHO_;+};++/// C++ variant on a standard Decorator pattern, using template+/// parameters.  The \c Plain template parameter type is the+/// undecorated Runtime type.  You can usually use \c Runtime here,+/// but if you know the concrete type ahead of time and it's final,+/// the compiler can devirtualize calls to the decorated+/// implementation.  The \c Base template parameter type will be used+/// as the base class of the decorated type.  Here, too, you can+/// usually use \c Runtime, but if you want the decorated type to+/// implement a derived class of Runtime, you can specify that here.+/// For an example, see threadsafe.h.+template <typename Plain = Runtime, typename Base = Runtime>+class RuntimeDecorator : public Base, private jsi::Instrumentation {+ public:+  Plain& plain() {+    static_assert(+        std::is_base_of<Runtime, Plain>::value,+        "RuntimeDecorator's Plain type must derive from jsi::Runtime");+    static_assert(+        std::is_base_of<Runtime, Base>::value,+        "RuntimeDecorator's Base type must derive from jsi::Runtime");+    return plain_;+  }+  const Plain& plain() const {+    return plain_;+  }++  Value evaluateJavaScript(+      const std::shared_ptr<const Buffer>& buffer,+      const std::string& sourceURL) override {+    return plain().evaluateJavaScript(buffer, sourceURL);+  }+  std::shared_ptr<const PreparedJavaScript> prepareJavaScript(+      const std::shared_ptr<const Buffer>& buffer,+      std::string sourceURL) override {+    return plain().prepareJavaScript(buffer, std::move(sourceURL));+  }+  Value evaluatePreparedJavaScript(+      const std::shared_ptr<const PreparedJavaScript>& js) override {+    return plain().evaluatePreparedJavaScript(js);+  }+#if JSI_VERSION >= 4+  bool drainMicrotasks(int maxMicrotasksHint) override {+    return plain().drainMicrotasks(maxMicrotasksHint);+  }+#endif+  Object global() override {+    return plain().global();+  }+  std::string description() override {+    return plain().description();+  };+  bool isInspectable() override {+    return plain().isInspectable();+  };+  Instrumentation& instrumentation() override {+    return *this;+  }++ protected:+  // plain is generally going to be a reference to an object managed+  // by a derived class.  We cache it here so this class can be+  // concrete, and avoid making virtual calls to find the plain+  // Runtime.  Note that the ctor and dtor do not access through the+  // reference, so passing a reference to an object before its+  // lifetime has started is ok.+  RuntimeDecorator(Plain& plain) : plain_(plain) {}++  Runtime::PointerValue* cloneSymbol(const Runtime::PointerValue* pv) override {+    return plain_.cloneSymbol(pv);+  };+#if JSI_VERSION >= 6+  Runtime::PointerValue* cloneBigInt(const Runtime::PointerValue* pv) override {+    return plain_.cloneBigInt(pv);+  };+#endif+  Runtime::PointerValue* cloneString(const Runtime::PointerValue* pv) override {+    return plain_.cloneString(pv);+  };+  Runtime::PointerValue* cloneObject(const Runtime::PointerValue* pv) override {+    return plain_.cloneObject(pv);+  };+  Runtime::PointerValue* clonePropNameID(+      const Runtime::PointerValue* pv) override {+    return plain_.clonePropNameID(pv);+  };++  PropNameID createPropNameIDFromAscii(const char* str, size_t length)+      override {+    return plain_.createPropNameIDFromAscii(str, length);+  };+  PropNameID createPropNameIDFromUtf8(const uint8_t* utf8, size_t length)+      override {+    return plain_.createPropNameIDFromUtf8(utf8, length);+  };+  PropNameID createPropNameIDFromString(const String& str) override {+    return plain_.createPropNameIDFromString(str);+  };+#if JSI_VERSION >= 5+  PropNameID createPropNameIDFromSymbol(const Symbol& sym) override {+    return plain_.createPropNameIDFromSymbol(sym);+  };+#endif+  std::string utf8(const PropNameID& id) override {+    return plain_.utf8(id);+  };+  bool compare(const PropNameID& a, const PropNameID& b) override {+    return plain_.compare(a, b);+  };++  std::string symbolToString(const Symbol& sym) override {+    return plain_.symbolToString(sym);+  }++#if JSI_VERSION >= 8+  BigInt createBigIntFromInt64(int64_t value) override {+    return plain_.createBigIntFromInt64(value);+  }+  BigInt createBigIntFromUint64(uint64_t value) override {+    return plain_.createBigIntFromUint64(value);+  }+  bool bigintIsInt64(const BigInt& b) override {+    return plain_.bigintIsInt64(b);+  }+  bool bigintIsUint64(const BigInt& b) override {+    return plain_.bigintIsUint64(b);+  }+  uint64_t truncate(const BigInt& b) override {+    return plain_.truncate(b);+  }+  String bigintToString(const BigInt& bigint, int radix) override {+    return plain_.bigintToString(bigint, radix);+  }+#endif++  String createStringFromAscii(const char* str, size_t length) override {+    return plain_.createStringFromAscii(str, length);+  };+  String createStringFromUtf8(const uint8_t* utf8, size_t length) override {+    return plain_.createStringFromUtf8(utf8, length);+  };+  std::string utf8(const String& s) override {+    return plain_.utf8(s);+  }++  Object createObject() override {+    return plain_.createObject();+  };++  Object createObject(std::shared_ptr<HostObject> ho) override {+    return plain_.createObject(+        std::make_shared<DecoratedHostObject>(*this, std::move(ho)));+  };+  std::shared_ptr<HostObject> getHostObject(const jsi::Object& o) override {+    std::shared_ptr<HostObject> dho = plain_.getHostObject(o);+    return static_cast<DecoratedHostObject&>(*dho).plainHO_;+  };+  HostFunctionType& getHostFunction(const jsi::Function& f) override {+    HostFunctionType& dhf = plain_.getHostFunction(f);+    // This will fail if a cpp file including this header is not compiled+    // with RTTI.+    return dhf.target<DecoratedHostFunction>()->plainHF_;+  };++#if JSI_VERSION >= 7+  bool hasNativeState(const Object& o) override {+    return plain_.hasNativeState(o);+  }+  std::shared_ptr<NativeState> getNativeState(const Object& o) override {+    return plain_.getNativeState(o);+  }+  void setNativeState(const Object& o, std::shared_ptr<NativeState> state)+      override {+    plain_.setNativeState(o, state);+  }+#endif++  Value getProperty(const Object& o, const PropNameID& name) override {+    return plain_.getProperty(o, name);+  };+  Value getProperty(const Object& o, const String& name) override {+    return plain_.getProperty(o, name);+  };+  bool hasProperty(const Object& o, const PropNameID& name) override {+    return plain_.hasProperty(o, name);+  };+  bool hasProperty(const Object& o, const String& name) override {+    return plain_.hasProperty(o, name);+  };+  void setPropertyValue(+      JSI_CONST_10 Object& o,+      const PropNameID& name,+      const Value& value) override {+    plain_.setPropertyValue(o, name, value);+  };+  void setPropertyValue(+      JSI_CONST_10 Object& o,+      const String& name,+      const Value& value) override {+    plain_.setPropertyValue(o, name, value);+  };++  bool isArray(const Object& o) const override {+    return plain_.isArray(o);+  };+  bool isArrayBuffer(const Object& o) const override {+    return plain_.isArrayBuffer(o);+  };+  bool isFunction(const Object& o) const override {+    return plain_.isFunction(o);+  };+  bool isHostObject(const jsi::Object& o) const override {+    return plain_.isHostObject(o);+  };+  bool isHostFunction(const jsi::Function& f) const override {+    return plain_.isHostFunction(f);+  };+  Array getPropertyNames(const Object& o) override {+    return plain_.getPropertyNames(o);+  };++  WeakObject createWeakObject(const Object& o) override {+    return plain_.createWeakObject(o);+  };+  Value lockWeakObject(JSI_NO_CONST_3 JSI_CONST_10 WeakObject& wo) override {+    return plain_.lockWeakObject(wo);+  };++  Array createArray(size_t length) override {+    return plain_.createArray(length);+  };+#if JSI_VERSION >= 9+  ArrayBuffer createArrayBuffer(+      std::shared_ptr<MutableBuffer> buffer) override {+    return plain_.createArrayBuffer(std::move(buffer));+  };+#endif+  size_t size(const Array& a) override {+    return plain_.size(a);+  };+  size_t size(const ArrayBuffer& ab) override {+    return plain_.size(ab);+  };+  uint8_t* data(const ArrayBuffer& ab) override {+    return plain_.data(ab);+  };+  Value getValueAtIndex(const Array& a, size_t i) override {+    return plain_.getValueAtIndex(a, i);+  };+  void setValueAtIndexImpl(JSI_CONST_10 Array& a, size_t i, const Value& value)+      override {+    plain_.setValueAtIndexImpl(a, i, value);+  };++  Function createFunctionFromHostFunction(+      const PropNameID& name,+      unsigned int paramCount,+      HostFunctionType func) override {+    return plain_.createFunctionFromHostFunction(+        name, paramCount, DecoratedHostFunction(*this, std::move(func)));+  };+  Value call(+      const Function& f,+      const Value& jsThis,+      const Value* args,+      size_t count) override {+    return plain_.call(f, jsThis, args, count);+  };+  Value callAsConstructor(const Function& f, const Value* args, size_t count)+      override {+    return plain_.callAsConstructor(f, args, count);+  };++  // Private data for managing scopes.+  Runtime::ScopeState* pushScope() override {+    return plain_.pushScope();+  }+  void popScope(Runtime::ScopeState* ss) override {+    plain_.popScope(ss);+  }++  bool strictEquals(const Symbol& a, const Symbol& b) const override {+    return plain_.strictEquals(a, b);+  };+#if JSI_VERSION >= 6+  bool strictEquals(const BigInt& a, const BigInt& b) const override {+    return plain_.strictEquals(a, b);+  };+#endif+  bool strictEquals(const String& a, const String& b) const override {+    return plain_.strictEquals(a, b);+  };+  bool strictEquals(const Object& a, const Object& b) const override {+    return plain_.strictEquals(a, b);+  };++  bool instanceOf(const Object& o, const Function& f) override {+    return plain_.instanceOf(o, f);+  };++  // jsi::Instrumentation methods++  std::string getRecordedGCStats() override {+    return plain().instrumentation().getRecordedGCStats();+  }++  std::unordered_map<std::string, int64_t> getHeapInfo(+      bool includeExpensive) override {+    return plain().instrumentation().getHeapInfo(includeExpensive);+  }++  void collectGarbage(std::string cause) override {+    plain().instrumentation().collectGarbage(std::move(cause));+  }++  void startTrackingHeapObjectStackTraces(+      std::function<void(+          uint64_t,+          std::chrono::microseconds,+          std::vector<HeapStatsUpdate>)> callback) override {+    plain().instrumentation().startTrackingHeapObjectStackTraces(+        std::move(callback));+  }++  void stopTrackingHeapObjectStackTraces() override {+    plain().instrumentation().stopTrackingHeapObjectStackTraces();+  }++  void startHeapSampling(size_t samplingInterval) override {+    plain().instrumentation().startHeapSampling(samplingInterval);+  }++  void stopHeapSampling(std::ostream& os) override {+    plain().instrumentation().stopHeapSampling(os);+  }++  void createSnapshotToFile(const std::string& path) override {+    plain().instrumentation().createSnapshotToFile(path);+  }++  void createSnapshotToStream(std::ostream& os) override {+    plain().instrumentation().createSnapshotToStream(os);+  }++  std::string flushAndDisableBridgeTrafficTrace() override {+    return const_cast<Plain&>(plain())+        .instrumentation()+        .flushAndDisableBridgeTrafficTrace();+  }++  void writeBasicBlockProfileTraceToFile(+      const std::string& fileName) const override {+    const_cast<Plain&>(plain())+        .instrumentation()+        .writeBasicBlockProfileTraceToFile(fileName);+  }++  /// Dump external profiler symbols to the given file name.+  void dumpProfilerSymbolsToFile(const std::string& fileName) const override {+    const_cast<Plain&>(plain()).instrumentation().dumpProfilerSymbolsToFile(+        fileName);+  }++ private:+  Plain& plain_;+};++namespace detail {++// This metaprogramming allows the With type's methods to be+// optional.++template <typename T, typename U = void>+struct BeforeCaller {+  static void before(T&) {}+};++template <typename T, typename U = void>+struct AfterCaller {+  static void after(T&) {}+};++// decltype((void)&...) is either SFINAE, or void.+// So, if SFINAE does not happen for T, then this specialization exists+// for BeforeCaller<T, void>, and always applies.  If not, only the+// default above exists, and that is used instead.+template <typename T>+struct BeforeCaller<T, decltype((void)&T::before)> {+  static void before(T& t) {+    t.before();+  }+};++template <typename T>+struct AfterCaller<T, decltype((void)&T::after)> {+  static void after(T& t) {+    t.after();+  }+};++// It's possible to use multiple decorators by nesting+// WithRuntimeDecorator<...>, but this specialization allows use of+// std::tuple of decorator classes instead.  See testlib.cpp for an+// example.+template <typename... T>+struct BeforeCaller<std::tuple<T...>> {+  static void before(std::tuple<T...>& tuple) {+    all_before<0, T...>(tuple);+  }++ private:+  template <size_t N, typename U, typename... Rest>+  static void all_before(std::tuple<T...>& tuple) {+    detail::BeforeCaller<U>::before(std::get<N>(tuple));+    all_before<N + 1, Rest...>(tuple);+  }++  template <size_t N>+  static void all_before(std::tuple<T...>&) {}+};++template <typename... T>+struct AfterCaller<std::tuple<T...>> {+  static void after(std::tuple<T...>& tuple) {+    all_after<0, T...>(tuple);+  }++ private:+  template <size_t N, typename U, typename... Rest>+  static void all_after(std::tuple<T...>& tuple) {+    all_after<N + 1, Rest...>(tuple);+    detail::AfterCaller<U>::after(std::get<N>(tuple));+  }++  template <size_t N>+  static void all_after(std::tuple<T...>&) {}+};++} // namespace detail++// A decorator which implements an around idiom.  A With instance is+// RAII constructed before each call to the undecorated class; the+// ctor is passed a single argument of type WithArg&.  Plain and Base+// are used as in the base class.+template <typename With, typename Plain = Runtime, typename Base = Runtime>+class WithRuntimeDecorator : public RuntimeDecorator<Plain, Base> {+ public:+  using RD = RuntimeDecorator<Plain, Base>;++  // The reference arguments to the ctor are stored, but not used by+  // the ctor, and there is no ctor, so they can be passed members of+  // the derived class.+  WithRuntimeDecorator(Plain& plain, With& with) : RD(plain), with_(with) {}++  Value evaluateJavaScript(+      const std::shared_ptr<const Buffer>& buffer,+      const std::string& sourceURL) override {+    Around around{with_};+    return RD::evaluateJavaScript(buffer, sourceURL);+  }+  std::shared_ptr<const PreparedJavaScript> prepareJavaScript(+      const std::shared_ptr<const Buffer>& buffer,+      std::string sourceURL) override {+    Around around{with_};+    return RD::prepareJavaScript(buffer, std::move(sourceURL));+  }+  Value evaluatePreparedJavaScript(+      const std::shared_ptr<const PreparedJavaScript>& js) override {+    Around around{with_};+    return RD::evaluatePreparedJavaScript(js);+  }+#if JSI_VERSION >= 4+  bool drainMicrotasks(int maxMicrotasksHint) override {+    Around around{with_};+    return RD::drainMicrotasks(maxMicrotasksHint);+  }+#endif+  Object global() override {+    Around around{with_};+    return RD::global();+  }+  std::string description() override {+    Around around{with_};+    return RD::description();+  };+  bool isInspectable() override {+    Around around{with_};+    return RD::isInspectable();+  };++  // The jsi:: prefix is necessary because MSVC compiler complains C2247:+  // Instrumentation is not accessible because RuntimeDecorator uses private+  // to inherit from Instrumentation.+  // TODO(T40821815) Consider removing this workaround when updating MSVC+  jsi::Instrumentation& instrumentation() override {+    Around around{with_};+    return RD::instrumentation();+  }++ protected:+  Runtime::PointerValue* cloneSymbol(const Runtime::PointerValue* pv) override {+    Around around{with_};+    return RD::cloneSymbol(pv);+  };+  Runtime::PointerValue* cloneString(const Runtime::PointerValue* pv) override {+    Around around{with_};+    return RD::cloneString(pv);+  };+  Runtime::PointerValue* cloneObject(const Runtime::PointerValue* pv) override {+    Around around{with_};+    return RD::cloneObject(pv);+  };+  Runtime::PointerValue* clonePropNameID(+      const Runtime::PointerValue* pv) override {+    Around around{with_};+    return RD::clonePropNameID(pv);+  };++  PropNameID createPropNameIDFromAscii(const char* str, size_t length)+      override {+    Around around{with_};+    return RD::createPropNameIDFromAscii(str, length);+  };+  PropNameID createPropNameIDFromUtf8(const uint8_t* utf8, size_t length)+      override {+    Around around{with_};+    return RD::createPropNameIDFromUtf8(utf8, length);+  };+  PropNameID createPropNameIDFromString(const String& str) override {+    Around around{with_};+    return RD::createPropNameIDFromString(str);+  };+  std::string utf8(const PropNameID& id) override {+    Around around{with_};+    return RD::utf8(id);+  };+  bool compare(const PropNameID& a, const PropNameID& b) override {+    Around around{with_};+    return RD::compare(a, b);+  };++  std::string symbolToString(const Symbol& sym) override {+    Around around{with_};+    return RD::symbolToString(sym);+  };++  String createStringFromAscii(const char* str, size_t length) override {+    Around around{with_};+    return RD::createStringFromAscii(str, length);+  };+  String createStringFromUtf8(const uint8_t* utf8, size_t length) override {+    Around around{with_};+    return RD::createStringFromUtf8(utf8, length);+  };+  std::string utf8(const String& s) override {+    Around around{with_};+    return RD::utf8(s);+  }++  Object createObject() override {+    Around around{with_};+    return RD::createObject();+  };+  Object createObject(std::shared_ptr<HostObject> ho) override {+    Around around{with_};+    return RD::createObject(std::move(ho));+  };+  std::shared_ptr<HostObject> getHostObject(const jsi::Object& o) override {+    Around around{with_};+    return RD::getHostObject(o);+  };+  HostFunctionType& getHostFunction(const jsi::Function& f) override {+    Around around{with_};+    return RD::getHostFunction(f);+  };++  Value getProperty(const Object& o, const PropNameID& name) override {+    Around around{with_};+    return RD::getProperty(o, name);+  };+  Value getProperty(const Object& o, const String& name) override {+    Around around{with_};+    return RD::getProperty(o, name);+  };+  bool hasProperty(const Object& o, const PropNameID& name) override {+    Around around{with_};+    return RD::hasProperty(o, name);+  };+  bool hasProperty(const Object& o, const String& name) override {+    Around around{with_};+    return RD::hasProperty(o, name);+  };+  void setPropertyValue(+      JSI_CONST_10 Object& o,+      const PropNameID& name,+      const Value& value) override {+    Around around{with_};+    RD::setPropertyValue(o, name, value);+  };+  void setPropertyValue(+      JSI_CONST_10 Object& o,+      const String& name,+      const Value& value) override {+    Around around{with_};+    RD::setPropertyValue(o, name, value);+  };++  bool isArray(const Object& o) const override {+    Around around{with_};+    return RD::isArray(o);+  };+  bool isArrayBuffer(const Object& o) const override {+    Around around{with_};+    return RD::isArrayBuffer(o);+  };+  bool isFunction(const Object& o) const override {+    Around around{with_};+    return RD::isFunction(o);+  };+  bool isHostObject(const jsi::Object& o) const override {+    Around around{with_};+    return RD::isHostObject(o);+  };+  bool isHostFunction(const jsi::Function& f) const override {+    Around around{with_};+    return RD::isHostFunction(f);+  };+  Array getPropertyNames(const Object& o) override {+    Around around{with_};+    return RD::getPropertyNames(o);+  };++  WeakObject createWeakObject(const Object& o) override {+    Around around{with_};+    return RD::createWeakObject(o);+  };+  Value lockWeakObject(JSI_NO_CONST_3 JSI_CONST_10 WeakObject& wo) override {+    Around around{with_};+    return RD::lockWeakObject(wo);+  };++  Array createArray(size_t length) override {+    Around around{with_};+    return RD::createArray(length);+  };+#if JSI_VERSION >= 9+  ArrayBuffer createArrayBuffer(+      std::shared_ptr<MutableBuffer> buffer) override {+    return RD::createArrayBuffer(std::move(buffer));+  };+#endif+  size_t size(const Array& a) override {+    Around around{with_};+    return RD::size(a);+  };+  size_t size(const ArrayBuffer& ab) override {+    Around around{with_};+    return RD::size(ab);+  };+  uint8_t* data(const ArrayBuffer& ab) override {+    Around around{with_};+    return RD::data(ab);+  };+  Value getValueAtIndex(const Array& a, size_t i) override {+    Around around{with_};+    return RD::getValueAtIndex(a, i);+  };+  void setValueAtIndexImpl(JSI_CONST_10 Array& a, size_t i, const Value& value)+      override {+    Around around{with_};+    RD::setValueAtIndexImpl(a, i, value);+  };++  Function createFunctionFromHostFunction(+      const PropNameID& name,+      unsigned int paramCount,+      HostFunctionType func) override {+    Around around{with_};+    return RD::createFunctionFromHostFunction(+        name, paramCount, std::move(func));+  };+  Value call(+      const Function& f,+      const Value& jsThis,+      const Value* args,+      size_t count) override {+    Around around{with_};+    return RD::call(f, jsThis, args, count);+  };+  Value callAsConstructor(const Function& f, const Value* args, size_t count)+      override {+    Around around{with_};+    return RD::callAsConstructor(f, args, count);+  };++  // Private data for managing scopes.+  Runtime::ScopeState* pushScope() override {+    Around around{with_};+    return RD::pushScope();+  }+  void popScope(Runtime::ScopeState* ss) override {+    Around around{with_};+    RD::popScope(ss);+  }++  bool strictEquals(const Symbol& a, const Symbol& b) const override {+    Around around{with_};+    return RD::strictEquals(a, b);+  };+  bool strictEquals(const String& a, const String& b) const override {+    Around around{with_};+    return RD::strictEquals(a, b);+  };+  bool strictEquals(const Object& a, const Object& b) const override {+    Around around{with_};+    return RD::strictEquals(a, b);+  };++  bool instanceOf(const Object& o, const Function& f) override {+    Around around{with_};+    return RD::instanceOf(o, f);+  };++ private:+  // Wrap an RAII type around With& to guarantee after always happens.+  struct Around {+    Around(With& with) : with_(with) {+      detail::BeforeCaller<With>::before(with_);+    }+    ~Around() {+      detail::AfterCaller<With>::after(with_);+    }++    With& with_;+  };++  With& with_;+};++} // namespace jsi+} // namespace facebook

jsi/instrumentation.h

new file mode 100644@@ -0,0 +1,117 @@+/*+ * Copyright (c) Meta Platforms, Inc. and affiliates.+ *+ * This source code is licensed under the MIT license found in the+ * LICENSE file in the root directory of this source tree.+ */++#pragma once++#include <chrono>+#include <iosfwd>+#include <string>+#include <tuple>+#include <unordered_map>++#include <jsi/jsi.h>++namespace facebook {+namespace jsi {++/// Methods for starting and collecting instrumentation, an \c Instrumentation+/// instance is associated with a particular \c Runtime instance, which it+/// controls the instrumentation of.+/// None of these functions should return newly created jsi values, nor should+/// it modify the values of any jsi values in the heap (although GCs are fine).+class JSI_EXPORT Instrumentation {+ public:+  virtual ~Instrumentation() = default;++  /// Returns GC statistics as a JSON-encoded string, with an object containing+  /// "type" and "version" fields outermost. "type" is a string, unique to a+  /// particular implementation of \c jsi::Instrumentation, and "version" is a+  /// number to indicate any revision to that implementation and its output+  /// format.+  ///+  /// \pre This call can only be made on the instrumentation instance of a+  ///   runtime initialised to collect GC statistics.+  ///+  /// \post All cumulative measurements mentioned in the output are accumulated+  ///   across the entire lifetime of the Runtime.+  ///+  /// \return the GC statistics collected so far, as a JSON-encoded string.+  virtual std::string getRecordedGCStats() = 0;++  /// Request statistics about the current state of the runtime's heap. This+  /// function can be called at any time, and should produce information that is+  /// correct at the instant it is called (i.e, not stale).+  ///+  /// \return a map from a string key to a number associated with that+  /// statistic.+  virtual std::unordered_map<std::string, int64_t> getHeapInfo(+      bool includeExpensive) = 0;++  /// Perform a full garbage collection.+  /// \param cause The cause of this collection, as it should be reported in+  ///   logs.+  virtual void collectGarbage(std::string cause) = 0;++  /// A HeapStatsUpdate is a tuple of the fragment index, the number of objects+  /// in that fragment, and the number of bytes used by those objects.+  /// A "fragment" is a view of all objects allocated within a time slice.+  using HeapStatsUpdate = std::tuple<uint64_t, uint64_t, uint64_t>;++  /// Start capturing JS stack-traces for all JS heap allocated objects. These+  /// can be accessed via \c ::createSnapshotToFile().+  /// \param fragmentCallback If present, invoke this callback every so often+  ///   with the most recently seen object ID, and a list of fragments that have+  ///   been updated. This callback will be invoked on the same thread that the+  ///   runtime is using.+  virtual void startTrackingHeapObjectStackTraces(+      std::function<void(+          uint64_t lastSeenObjectID,+          std::chrono::microseconds timestamp,+          std::vector<HeapStatsUpdate> stats)> fragmentCallback) = 0;++  /// Stop capture JS stack-traces for JS heap allocated objects.+  virtual void stopTrackingHeapObjectStackTraces() = 0;++  /// Start a heap sampling profiler that will sample heap allocations, and the+  /// stack trace they were allocated at. Reports a summary of which functions+  /// allocated the most.+  /// \param samplingInterval The number of bytes allocated to wait between+  ///   samples. This will be used as the expected value of a poisson+  ///   distribution.+  virtual void startHeapSampling(size_t samplingInterval) = 0;++  /// Turns off the heap sampling profiler previously enabled via+  /// \c startHeapSampling. Writes the output of the sampling heap profiler to+  /// \p os. The output is a JSON formatted string.+  virtual void stopHeapSampling(std::ostream& os) = 0;++  /// Captures the heap to a file+  ///+  /// \param path to save the heap capture+  virtual void createSnapshotToFile(const std::string& path) = 0;++  /// Captures the heap to an output stream+  ///+  /// \param os output stream to write to.+  virtual void createSnapshotToStream(std::ostream& os) = 0;++  /// If the runtime has been created to trace to a temp file, flush+  /// any unwritten parts of the trace of bridge traffic to the file,+  /// and return the name of  the file.  Otherwise, return the empty string.+  /// Tracing is disabled after this call.+  virtual std::string flushAndDisableBridgeTrafficTrace() = 0;++  /// Write basic block profile trace to the given file name.+  virtual void writeBasicBlockProfileTraceToFile(+      const std::string& fileName) const = 0;++  /// Dump external profiler symbols to the given file name.+  virtual void dumpProfilerSymbolsToFile(const std::string& fileName) const = 0;+};++} // namespace jsi+} // namespace facebook

jsi/jsi-inl.h

new file mode 100644@@ -0,0 +1,359 @@+/*+ * Copyright (c) Meta Platforms, Inc. and affiliates.+ *+ * This source code is licensed under the MIT license found in the+ * LICENSE file in the root directory of this source tree.+ */++#pragma once++namespace facebook {+namespace jsi {+namespace detail {++inline Value toValue(Runtime&, std::nullptr_t) {+  return Value::null();+}+inline Value toValue(Runtime&, bool b) {+  return Value(b);+}+inline Value toValue(Runtime&, double d) {+  return Value(d);+}+inline Value toValue(Runtime&, float f) {+  return Value(static_cast<double>(f));+}+inline Value toValue(Runtime&, int i) {+  return Value(i);+}+inline Value toValue(Runtime& runtime, const char* str) {+  return String::createFromAscii(runtime, str);+}+inline Value toValue(Runtime& runtime, const std::string& str) {+  return String::createFromUtf8(runtime, str);+}+template <typename T>+inline Value toValue(Runtime& runtime, const T& other) {+  static_assert(+      std::is_base_of<Pointer, T>::value,+      "This type cannot be converted to Value");+  return Value(runtime, other);+}+inline Value toValue(Runtime& runtime, const Value& value) {+  return Value(runtime, value);+}+inline Value&& toValue(Runtime&, Value&& value) {+  return std::move(value);+}++inline PropNameID toPropNameID(Runtime& runtime, const char* name) {+  return PropNameID::forAscii(runtime, name);+}+inline PropNameID toPropNameID(Runtime& runtime, const std::string& name) {+  return PropNameID::forUtf8(runtime, name);+}+inline PropNameID&& toPropNameID(Runtime&, PropNameID&& name) {+  return std::move(name);+}++/// Helper to throw while still compiling with exceptions turned off.+template <typename E, typename... Args>+[[noreturn]] inline void throwOrDie(Args&&... args) {+  std::rethrow_exception(+      std::make_exception_ptr(E{std::forward<Args>(args)...}));+}++} // namespace detail++template <typename T>+inline T Runtime::make(Runtime::PointerValue* pv) {+  return T(pv);+}++#if JSI_VERSION >= 3+inline Runtime::PointerValue* Runtime::getPointerValue(jsi::Pointer& pointer) {+  return pointer.ptr_;+}+#endif++inline const Runtime::PointerValue* Runtime::getPointerValue(+    const jsi::Pointer& pointer) {+  return pointer.ptr_;+}++inline const Runtime::PointerValue* Runtime::getPointerValue(+    const jsi::Value& value) {+  return value.data_.pointer.ptr_;+}++inline Value Object::getProperty(Runtime& runtime, const char* name) const {+  return getProperty(runtime, String::createFromAscii(runtime, name));+}++inline Value Object::getProperty(Runtime& runtime, const String& name) const {+  return runtime.getProperty(*this, name);+}++inline Value Object::getProperty(Runtime& runtime, const PropNameID& name)+    const {+  return runtime.getProperty(*this, name);+}++inline bool Object::hasProperty(Runtime& runtime, const char* name) const {+  return hasProperty(runtime, String::createFromAscii(runtime, name));+}++inline bool Object::hasProperty(Runtime& runtime, const String& name) const {+  return runtime.hasProperty(*this, name);+}++inline bool Object::hasProperty(Runtime& runtime, const PropNameID& name)+    const {+  return runtime.hasProperty(*this, name);+}++template <typename T>+void Object::setProperty(Runtime& runtime, const char* name, T&& value)+    JSI_CONST_10 {+  setProperty(+      runtime, String::createFromAscii(runtime, name), std::forward<T>(value));+}++template <typename T>+void Object::setProperty(Runtime& runtime, const String& name, T&& value)+    JSI_CONST_10 {+  setPropertyValue(+      runtime, name, detail::toValue(runtime, std::forward<T>(value)));+}++template <typename T>+void Object::setProperty(Runtime& runtime, const PropNameID& name, T&& value)+    JSI_CONST_10 {+  setPropertyValue(+      runtime, name, detail::toValue(runtime, std::forward<T>(value)));+}++inline Array Object::getArray(Runtime& runtime) const& {+  assert(runtime.isArray(*this));+  (void)runtime; // when assert is disabled we need to mark this as used+  return Array(runtime.cloneObject(ptr_));+}++inline Array Object::getArray(Runtime& runtime) && {+  assert(runtime.isArray(*this));+  (void)runtime; // when assert is disabled we need to mark this as used+  Runtime::PointerValue* value = ptr_;+  ptr_ = nullptr;+  return Array(value);+}++inline ArrayBuffer Object::getArrayBuffer(Runtime& runtime) const& {+  assert(runtime.isArrayBuffer(*this));+  (void)runtime; // when assert is disabled we need to mark this as used+  return ArrayBuffer(runtime.cloneObject(ptr_));+}++inline ArrayBuffer Object::getArrayBuffer(Runtime& runtime) && {+  assert(runtime.isArrayBuffer(*this));+  (void)runtime; // when assert is disabled we need to mark this as used+  Runtime::PointerValue* value = ptr_;+  ptr_ = nullptr;+  return ArrayBuffer(value);+}++inline Function Object::getFunction(Runtime& runtime) const& {+  assert(runtime.isFunction(*this));+  return Function(runtime.cloneObject(ptr_));+}++inline Function Object::getFunction(Runtime& runtime) && {+  assert(runtime.isFunction(*this));+  (void)runtime; // when assert is disabled we need to mark this as used+  Runtime::PointerValue* value = ptr_;+  ptr_ = nullptr;+  return Function(value);+}++template <typename T>+inline bool Object::isHostObject(Runtime& runtime) const {+  return runtime.isHostObject(*this) &&+      std::dynamic_pointer_cast<T>(runtime.getHostObject(*this));+}++template <>+inline bool Object::isHostObject<HostObject>(Runtime& runtime) const {+  return runtime.isHostObject(*this);+}++template <typename T>+inline std::shared_ptr<T> Object::getHostObject(Runtime& runtime) const {+  assert(isHostObject<T>(runtime));+  return std::static_pointer_cast<T>(runtime.getHostObject(*this));+}++template <typename T>+inline std::shared_ptr<T> Object::asHostObject(Runtime& runtime) const {+  if (!isHostObject<T>(runtime)) {+    detail::throwOrDie<JSINativeException>(+        "Object is not a HostObject of desired type");+  }+  return std::static_pointer_cast<T>(runtime.getHostObject(*this));+}++template <>+inline std::shared_ptr<HostObject> Object::getHostObject<HostObject>(+    Runtime& runtime) const {+  assert(runtime.isHostObject(*this));+  return runtime.getHostObject(*this);+}++#if JSI_VERSION >= 7+template <typename T>+inline bool Object::hasNativeState(Runtime& runtime) const {+  return runtime.hasNativeState(*this) &&+      std::dynamic_pointer_cast<T>(runtime.getNativeState(*this));+}++template <>+inline bool Object::hasNativeState<NativeState>(Runtime& runtime) const {+  return runtime.hasNativeState(*this);+}++template <typename T>+inline std::shared_ptr<T> Object::getNativeState(Runtime& runtime) const {+  assert(hasNativeState<T>(runtime));+  return std::static_pointer_cast<T>(runtime.getNativeState(*this));+}++inline void Object::setNativeState(+    Runtime& runtime,+    std::shared_ptr<NativeState> state) const {+  runtime.setNativeState(*this, state);+}+#endif++inline Array Object::getPropertyNames(Runtime& runtime) const {+  return runtime.getPropertyNames(*this);+}++inline Value WeakObject::lock(Runtime& runtime) JSI_CONST_10 {+  return runtime.lockWeakObject(*this);+}++template <typename T>+void Array::setValueAtIndex(Runtime& runtime, size_t i, T&& value)+    JSI_CONST_10 {+  setValueAtIndexImpl(+      runtime, i, detail::toValue(runtime, std::forward<T>(value)));+}++inline Value Array::getValueAtIndex(Runtime& runtime, size_t i) const {+  return runtime.getValueAtIndex(*this, i);+}++inline Function Function::createFromHostFunction(+    Runtime& runtime,+    const jsi::PropNameID& name,+    unsigned int paramCount,+    jsi::HostFunctionType func) {+  return runtime.createFunctionFromHostFunction(+      name, paramCount, std::move(func));+}++inline Value Function::call(Runtime& runtime, const Value* args, size_t count)+    const {+  return runtime.call(*this, Value::undefined(), args, count);+}++inline Value Function::call(Runtime& runtime, std::initializer_list<Value> args)+    const {+  return call(runtime, args.begin(), args.size());+}++template <typename... Args>+inline Value Function::call(Runtime& runtime, Args&&... args) const {+  // A more awesome version of this would be able to create raw values+  // which can be used directly without wrapping and unwrapping, but+  // this will do for now.+  return call(runtime, {detail::toValue(runtime, std::forward<Args>(args))...});+}++inline Value Function::callWithThis(+    Runtime& runtime,+    const Object& jsThis,+    const Value* args,+    size_t count) const {+  return runtime.call(*this, Value(runtime, jsThis), args, count);+}++inline Value Function::callWithThis(+    Runtime& runtime,+    const Object& jsThis,+    std::initializer_list<Value> args) const {+  return callWithThis(runtime, jsThis, args.begin(), args.size());+}++template <typename... Args>+inline Value Function::callWithThis(+    Runtime& runtime,+    const Object& jsThis,+    Args&&... args) const {+  // A more awesome version of this would be able to create raw values+  // which can be used directly without wrapping and unwrapping, but+  // this will do for now.+  return callWithThis(+      runtime, jsThis, {detail::toValue(runtime, std::forward<Args>(args))...});+}++template <typename... Args>+inline Array Array::createWithElements(Runtime& runtime, Args&&... args) {+  return createWithElements(+      runtime, {detail::toValue(runtime, std::forward<Args>(args))...});+}++template <typename... Args>+inline std::vector<PropNameID> PropNameID::names(+    Runtime& runtime,+    Args&&... args) {+  return names({detail::toPropNameID(runtime, std::forward<Args>(args))...});+}++template <size_t N>+inline std::vector<PropNameID> PropNameID::names(+    PropNameID (&&propertyNames)[N]) {+  std::vector<PropNameID> result;+  result.reserve(N);+  for (auto& name : propertyNames) {+    result.push_back(std::move(name));+  }+  return result;+}++inline Value Function::callAsConstructor(+    Runtime& runtime,+    const Value* args,+    size_t count) const {+  return runtime.callAsConstructor(*this, args, count);+}++inline Value Function::callAsConstructor(+    Runtime& runtime,+    std::initializer_list<Value> args) const {+  return callAsConstructor(runtime, args.begin(), args.size());+}++template <typename... Args>+inline Value Function::callAsConstructor(Runtime& runtime, Args&&... args)+    const {+  return callAsConstructor(+      runtime, {detail::toValue(runtime, std::forward<Args>(args))...});+}++#if JSI_VERSION >= 8+String BigInt::toString(Runtime& runtime, int radix) const {+  return runtime.bigintToString(*this, radix);+}+#endif++} // namespace jsi+} // namespace facebook

jsi/jsi.cpp

new file mode 100644@@ -0,0 +1,554 @@+/*+ * Copyright (c) Meta Platforms, Inc. and affiliates.+ *+ * This source code is licensed under the MIT license found in the+ * LICENSE file in the root directory of this source tree.+ */++#include <cassert>+#include <cmath>+#include <cstdlib>+#include <stdexcept>++#include <jsi/instrumentation.h>+#include <jsi/jsi.h>++namespace facebook {+namespace jsi {++namespace {++// This is used for generating short exception strings.+std::string kindToString(const Value& v, Runtime* rt = nullptr) {+  if (v.isUndefined()) {+    return "undefined";+  } else if (v.isNull()) {+    return "null";+  } else if (v.isBool()) {+    return v.getBool() ? "true" : "false";+  } else if (v.isNumber()) {+    return "a number";+  } else if (v.isString()) {+    return "a string";+  } else if (v.isSymbol()) {+    return "a symbol";+#if JSI_VERSION >= 6+  } else if (v.isBigInt()) {+    return "a bigint";+#endif+  } else {+    assert(v.isObject() && "Expecting object.");+    return rt != nullptr && v.getObject(*rt).isFunction(*rt) ? "a function"+                                                             : "an object";+  }+}++// getPropertyAsFunction() will try to create a JSError.  If the+// failure is in building a JSError, this will lead to infinite+// recursion.  This function is used in place of getPropertyAsFunction+// when building JSError, to avoid that infinite recursion.+Value callGlobalFunction(Runtime& runtime, const char* name, const Value& arg) {+  Value v = runtime.global().getProperty(runtime, name);+  if (!v.isObject()) {+    throw JSINativeException(+        std::string("callGlobalFunction: JS global property '") + name ++        "' is " + kindToString(v, &runtime) + ", expected a Function");+  }+  Object o = v.getObject(runtime);+  if (!o.isFunction(runtime)) {+    throw JSINativeException(+        std::string("callGlobalFunction: JS global property '") + name ++        "' is a non-callable Object, expected a Function");+  }+  Function f = std::move(o).getFunction(runtime);+  return f.call(runtime, arg);+}++} // namespace++Buffer::~Buffer() = default;++#if JSI_VERSION >= 9+MutableBuffer::~MutableBuffer() = default;+#endif++PreparedJavaScript::~PreparedJavaScript() = default;++Value HostObject::get(Runtime&, const PropNameID&) {+  return Value();+}++void HostObject::set(Runtime& rt, const PropNameID& name, const Value&) {+  std::string msg("TypeError: Cannot assign to property '");+  msg += name.utf8(rt);+  msg += "' on HostObject with default setter";+  throw JSError(rt, msg);+}++HostObject::~HostObject() {}++#if JSI_VERSION >= 7+NativeState::~NativeState() {}+#endif++Runtime::~Runtime() {}++Instrumentation& Runtime::instrumentation() {+  class NoInstrumentation : public Instrumentation {+    std::string getRecordedGCStats() override {+      return "";+    }++    std::unordered_map<std::string, int64_t> getHeapInfo(bool) override {+      return std::unordered_map<std::string, int64_t>{};+    }++    void collectGarbage(std::string) override {}++    void startTrackingHeapObjectStackTraces(+        std::function<void(+            uint64_t,+            std::chrono::microseconds,+            std::vector<HeapStatsUpdate>)>) override {}+    void stopTrackingHeapObjectStackTraces() override {}++    void startHeapSampling(size_t) override {}+    void stopHeapSampling(std::ostream&) override {}++    void createSnapshotToFile(const std::string&) override {+      throw JSINativeException(+          "Default instrumentation cannot create a heap snapshot");+    }++    void createSnapshotToStream(std::ostream&) override {+      throw JSINativeException(+          "Default instrumentation cannot create a heap snapshot");+    }++    std::string flushAndDisableBridgeTrafficTrace() override {+      std::abort();+    }++    void writeBasicBlockProfileTraceToFile(const std::string&) const override {+      std::abort();+    }++    void dumpProfilerSymbolsToFile(const std::string&) const override {+      std::abort();+    }+  };++  static NoInstrumentation sharedInstance;+  return sharedInstance;+}++#if JSI_VERSION >= 2+Value Runtime::createValueFromJsonUtf8(const uint8_t* json, size_t length) {+  Function parseJson = global()+                           .getPropertyAsObject(*this, "JSON")+                           .getPropertyAsFunction(*this, "parse");+  return parseJson.call(*this, String::createFromUtf8(*this, json, length));+}+#else+Value Value::createFromJsonUtf8(+    Runtime& runtime,+    const uint8_t* json,+    size_t length) {+  Function parseJson = runtime.global()+                           .getPropertyAsObject(runtime, "JSON")+                           .getPropertyAsFunction(runtime, "parse");+  return parseJson.call(runtime, String::createFromUtf8(runtime, json, length));+}+#endif++Pointer& Pointer::operator=(Pointer&& other) {+  if (ptr_) {+    ptr_->invalidate();+  }+  ptr_ = other.ptr_;+  other.ptr_ = nullptr;+  return *this;+}++Object Object::getPropertyAsObject(Runtime& runtime, const char* name) const {+  Value v = getProperty(runtime, name);++  if (!v.isObject()) {+    throw JSError(+        runtime,+        std::string("getPropertyAsObject: property '") + name + "' is " ++            kindToString(v, &runtime) + ", expected an Object");+  }++  return v.getObject(runtime);+}++Function Object::getPropertyAsFunction(Runtime& runtime, const char* name)+    const {+  Object obj = getPropertyAsObject(runtime, name);+  if (!obj.isFunction(runtime)) {+    throw JSError(+        runtime,+        std::string("getPropertyAsFunction: property '") + name + "' is " ++            kindToString(std::move(obj), &runtime) + ", expected a Function");+  };++  return std::move(obj).getFunction(runtime);+}++Array Object::asArray(Runtime& runtime) const& {+  if (!isArray(runtime)) {+    throw JSError(+        runtime,+        "Object is " + kindToString(Value(runtime, *this), &runtime) ++            ", expected an array");+  }+  return getArray(runtime);+}++Array Object::asArray(Runtime& runtime) && {+  if (!isArray(runtime)) {+    throw JSError(+        runtime,+        "Object is " + kindToString(Value(runtime, *this), &runtime) ++            ", expected an array");+  }+  return std::move(*this).getArray(runtime);+}++Function Object::asFunction(Runtime& runtime) const& {+  if (!isFunction(runtime)) {+    throw JSError(+        runtime,+        "Object is " + kindToString(Value(runtime, *this), &runtime) ++            ", expected a function");+  }+  return getFunction(runtime);+}++Function Object::asFunction(Runtime& runtime) && {+  if (!isFunction(runtime)) {+    throw JSError(+        runtime,+        "Object is " + kindToString(Value(runtime, *this), &runtime) ++            ", expected a function");+  }+  return std::move(*this).getFunction(runtime);+}++Value::Value(Value&& other) : Value(other.kind_) {+  if (kind_ == BooleanKind) {+    data_.boolean = other.data_.boolean;+  } else if (kind_ == NumberKind) {+    data_.number = other.data_.number;+  } else if (kind_ >= PointerKind) {+    new (&data_.pointer) Pointer(std::move(other.data_.pointer));+  }+  // when the other's dtor runs, nothing will happen.+  other.kind_ = UndefinedKind;+}++Value::Value(Runtime& runtime, const Value& other) : Value(other.kind_) {+  // data_ is uninitialized, so use placement new to create non-POD+  // types in it.  Any other kind of initialization will call a dtor+  // first, which is incorrect.+  if (kind_ == BooleanKind) {+    data_.boolean = other.data_.boolean;+  } else if (kind_ == NumberKind) {+    data_.number = other.data_.number;+  } else if (kind_ == SymbolKind) {+    new (&data_.pointer) Pointer(runtime.cloneSymbol(other.data_.pointer.ptr_));+#if JSI_VERSION >= 6+  } else if (kind_ == BigIntKind) {+    new (&data_.pointer) Pointer(runtime.cloneBigInt(other.data_.pointer.ptr_));+#endif+  } else if (kind_ == StringKind) {+    new (&data_.pointer) Pointer(runtime.cloneString(other.data_.pointer.ptr_));+  } else if (kind_ >= ObjectKind) {+    new (&data_.pointer) Pointer(runtime.cloneObject(other.data_.pointer.ptr_));+  }+}++Value::~Value() {+  if (kind_ >= PointerKind) {+    data_.pointer.~Pointer();+  }+}++bool Value::strictEquals(Runtime& runtime, const Value& a, const Value& b) {+  if (a.kind_ != b.kind_) {+    return false;+  }+  switch (a.kind_) {+    case UndefinedKind:+    case NullKind:+      return true;+    case BooleanKind:+      return a.data_.boolean == b.data_.boolean;+    case NumberKind:+      return a.data_.number == b.data_.number;+    case SymbolKind:+      return runtime.strictEquals(+          static_cast<const Symbol&>(a.data_.pointer),+          static_cast<const Symbol&>(b.data_.pointer));+#if JSI_VERSION >= 6+    case BigIntKind:+      return runtime.strictEquals(+          static_cast<const BigInt&>(a.data_.pointer),+          static_cast<const BigInt&>(b.data_.pointer));+#endif+    case StringKind:+      return runtime.strictEquals(+          static_cast<const String&>(a.data_.pointer),+          static_cast<const String&>(b.data_.pointer));+    case ObjectKind:+      return runtime.strictEquals(+          static_cast<const Object&>(a.data_.pointer),+          static_cast<const Object&>(b.data_.pointer));+  }+  return false;+}++bool Value::asBool() const {+  if (!isBool()) {+    throw JSINativeException(+        "Value is " + kindToString(*this) + ", expected a boolean");+  }++  return getBool();+}++double Value::asNumber() const {+  if (!isNumber()) {+    throw JSINativeException(+        "Value is " + kindToString(*this) + ", expected a number");+  }++  return getNumber();+}++Object Value::asObject(Runtime& rt) const& {+  if (!isObject()) {+    throw JSError(+        rt, "Value is " + kindToString(*this, &rt) + ", expected an Object");+  }++  return getObject(rt);+}++Object Value::asObject(Runtime& rt) && {+  if (!isObject()) {+    throw JSError(+        rt, "Value is " + kindToString(*this, &rt) + ", expected an Object");+  }+  auto ptr = data_.pointer.ptr_;+  data_.pointer.ptr_ = nullptr;+  return static_cast<Object>(ptr);+}++Symbol Value::asSymbol(Runtime& rt) const& {+  if (!isSymbol()) {+    throw JSError(+        rt, "Value is " + kindToString(*this, &rt) + ", expected a Symbol");+  }++  return getSymbol(rt);+}++Symbol Value::asSymbol(Runtime& rt) && {+  if (!isSymbol()) {+    throw JSError(+        rt, "Value is " + kindToString(*this, &rt) + ", expected a Symbol");+  }++  return std::move(*this).getSymbol(rt);+}++#if JSI_VERSION >= 6+BigInt Value::asBigInt(Runtime& rt) const& {+  if (!isBigInt()) {+    throw JSError(+        rt, "Value is " + kindToString(*this, &rt) + ", expected a BigInt");+  }++  return getBigInt(rt);+}++BigInt Value::asBigInt(Runtime& rt) && {+  if (!isBigInt()) {+    throw JSError(+        rt, "Value is " + kindToString(*this, &rt) + ", expected a BigInt");+  }++  return std::move(*this).getBigInt(rt);+}+#endif++String Value::asString(Runtime& rt) const& {+  if (!isString()) {+    throw JSError(+        rt, "Value is " + kindToString(*this, &rt) + ", expected a String");+  }++  return getString(rt);+}++String Value::asString(Runtime& rt) && {+  if (!isString()) {+    throw JSError(+        rt, "Value is " + kindToString(*this, &rt) + ", expected a String");+  }++  return std::move(*this).getString(rt);+}++String Value::toString(Runtime& runtime) const {+  Function toString = runtime.global().getPropertyAsFunction(runtime, "String");+  return toString.call(runtime, *this).getString(runtime);+}++#if JSI_VERSION >= 8+uint64_t BigInt::asUint64(Runtime& runtime) const {+  if (!isUint64(runtime)) {+    throw JSError(runtime, "Lossy truncation in BigInt64::asUint64");+  }+  return getUint64(runtime);+}++int64_t BigInt::asInt64(Runtime& runtime) const {+  if (!isInt64(runtime)) {+    throw JSError(runtime, "Lossy truncation in BigInt64::asInt64");+  }+  return getInt64(runtime);+}+#endif++Array Array::createWithElements(+    Runtime& rt,+    std::initializer_list<Value> elements) {+  Array result(rt, elements.size());+  size_t index = 0;+  for (const auto& element : elements) {+    result.setValueAtIndex(rt, index++, element);+  }+  return result;+}++std::vector<PropNameID> HostObject::getPropertyNames(Runtime&) {+  return {};+}++Runtime::ScopeState* Runtime::pushScope() {+  return nullptr;+}++void Runtime::popScope(ScopeState*) {}++JSError::JSError(Runtime& rt, Value&& value) {+  setValue(rt, std::move(value));+}++JSError::JSError(Runtime& rt, std::string msg) : message_(std::move(msg)) {+  try {+    setValue(+        rt,+        callGlobalFunction(rt, "Error", String::createFromUtf8(rt, message_)));+  } catch (const JSIException& ex) {+    message_ = std::string(ex.what()) + " (while raising " + message_ + ")";+    setValue(rt, String::createFromUtf8(rt, message_));+  }+}++JSError::JSError(Runtime& rt, std::string msg, std::string stack)+    : message_(std::move(msg)), stack_(std::move(stack)) {+  try {+    Object e(rt);+    e.setProperty(rt, "message", String::createFromUtf8(rt, message_));+    e.setProperty(rt, "stack", String::createFromUtf8(rt, stack_));+    setValue(rt, std::move(e));+  } catch (const JSIException& ex) {+    setValue(rt, String::createFromUtf8(rt, ex.what()));+  }+}++JSError::JSError(std::string what, Runtime& rt, Value&& value)+    : JSIException(std::move(what)) {+  setValue(rt, std::move(value));+}++void JSError::setValue(Runtime& rt, Value&& value) {+  value_ = std::make_shared<Value>(std::move(value));++  if ((message_.empty() || stack_.empty()) && value_->isObject()) {+    auto obj = value_->getObject(rt);++    if (message_.empty()) {+      try {+        Value message = obj.getProperty(rt, "message");+        if (!message.isUndefined() && !message.isString()) {+          message = callGlobalFunction(rt, "String", message);+        }+        if (message.isString()) {+          message_ = message.getString(rt).utf8(rt);+        } else if (!message.isUndefined()) {+          message_ = "String(e.message) is a " + kindToString(message, &rt);+        }+      } catch (const JSIException& ex) {+        message_ = std::string("[Exception while creating message string: ") ++            ex.what() + "]";+      }+    }++    if (stack_.empty()) {+      try {+        Value stack = obj.getProperty(rt, "stack");+        if (!stack.isUndefined() && !stack.isString()) {+          stack = callGlobalFunction(rt, "String", stack);+        }+        if (stack.isString()) {+          stack_ = stack.getString(rt).utf8(rt);+        } else if (!stack.isUndefined()) {+          stack_ = "String(e.stack) is a " + kindToString(stack, &rt);+        }+      } catch (const JSIException& ex) {+        message_ = std::string("[Exception while creating stack string: ") ++            ex.what() + "]";+      }+    }+  }++  if (message_.empty()) {+    try {+      if (value_->isString()) {+        message_ = value_->getString(rt).utf8(rt);+      } else {+        Value message = callGlobalFunction(rt, "String", *value_);+        if (message.isString()) {+          message_ = message.getString(rt).utf8(rt);+        } else {+          message_ = "String(e) is a " + kindToString(message, &rt);+        }+      }+    } catch (const JSIException& ex) {+      message_ = std::string("[Exception while creating message string: ") ++          ex.what() + "]";+    }+  }++  if (stack_.empty()) {+    stack_ = "no stack";+  }++  if (what_.empty()) {+    what_ = message_ + "\n\n" + stack_;+  }+}++JSIException::~JSIException() {}++JSINativeException::~JSINativeException() {}++JSError::~JSError() {}++} // namespace jsi+} // namespace facebook

jsi/jsi.h

new file mode 100644@@ -0,0 +1,1575 @@+/*+ * Copyright (c) Meta Platforms, Inc. and affiliates.+ *+ * This source code is licensed under the MIT license found in the+ * LICENSE file in the root directory of this source tree.+ */++#pragma once++#include <cassert>+#include <cstring>+#include <exception>+#include <functional>+#include <memory>+#include <string>+#include <vector>++#ifndef JSI_EXPORT+#ifdef _MSC_VER+#ifdef CREATE_SHARED_LIBRARY+#define JSI_EXPORT __declspec(dllexport)+#else+#define JSI_EXPORT+#endif // CREATE_SHARED_LIBRARY+#else // _MSC_VER+#define JSI_EXPORT __attribute__((visibility("default")))+#endif // _MSC_VER+#endif // !defined(JSI_EXPORT)++// JSI version defines set of features available in the API.+// Each significant API change must be under a new version.+#ifndef JSI_VERSION+#define JSI_VERSION 10+#endif++#if JSI_VERSION >= 3+#define JSI_NO_CONST_3+#else+#define JSI_NO_CONST_3 const+#endif++#if JSI_VERSION >= 10+#define JSI_CONST_10 const+#else+#define JSI_CONST_10+#endif++class FBJSRuntime;+namespace facebook {+namespace jsi {++/// Base class for buffers of data or bytecode that need to be passed to the+/// runtime. The buffer is expected to be fully immutable, so the result of+/// size(), data(), and the contents of the pointer returned by data() must not+/// change after construction.+class JSI_EXPORT Buffer {+ public:+  virtual ~Buffer();+  virtual size_t size() const = 0;+  virtual const uint8_t* data() const = 0;+};++class JSI_EXPORT StringBuffer : public Buffer {+ public:+  StringBuffer(std::string s) : s_(std::move(s)) {}+  size_t size() const override {+    return s_.size();+  }+  const uint8_t* data() const override {+    return reinterpret_cast<const uint8_t*>(s_.data());+  }++ private:+  std::string s_;+};++#if JSI_VERSION >= 9+/// Base class for buffers of data that need to be passed to the runtime. The+/// result of size() and data() must not change after construction. However, the+/// region pointed to by data() may be modified by the user or the runtime. The+/// user must ensure that access to the contents of the buffer is properly+/// synchronised.+class JSI_EXPORT MutableBuffer {+ public:+  virtual ~MutableBuffer();+  virtual size_t size() const = 0;+  virtual uint8_t* data() = 0;+};+#endif++/// PreparedJavaScript is a base class representing JavaScript which is in a+/// form optimized for execution, in a runtime-specific way. Construct one via+/// jsi::Runtime::prepareJavaScript().+/// ** This is an experimental API that is subject to change. **+class JSI_EXPORT PreparedJavaScript {+ protected:+  PreparedJavaScript() = default;++ public:+  virtual ~PreparedJavaScript() = 0;+};++class Runtime;+class Pointer;+class PropNameID;+class Symbol;+#if JSI_VERSION >= 6+class BigInt;+#endif+class String;+class Object;+class WeakObject;+class Array;+class ArrayBuffer;+class Function;+class Value;+class Instrumentation;+class Scope;+class JSIException;+class JSError;++/// A function which has this type can be registered as a function+/// callable from JavaScript using Function::createFromHostFunction().+/// When the function is called, args will point to the arguments, and+/// count will indicate how many arguments are passed.  The function+/// can return a Value to the caller, or throw an exception.  If a C+++/// exception is thrown, a JS Error will be created and thrown into+/// JS; if the C++ exception extends std::exception, the Error's+/// message will be whatever what() returns. Note that it is undefined whether+/// HostFunctions may or may not be called in strict mode; that is `thisVal`+/// can be any value - it will not necessarily be coerced to an object or+/// or set to the global object.+using HostFunctionType = std::function<+    Value(Runtime& rt, const Value& thisVal, const Value* args, size_t count)>;++/// An object which implements this interface can be registered as an+/// Object with the JS runtime.+class JSI_EXPORT HostObject {+ public:+  // The C++ object's dtor will be called when the GC finalizes this+  // object.  (This may be as late as when the Runtime is shut down.)+  // You have no control over which thread it is called on.  This will+  // be called from inside the GC, so it is unsafe to do any VM+  // operations which require a Runtime&.  Derived classes' dtors+  // should also avoid doing anything expensive.  Calling the dtor on+  // a jsi object is explicitly ok.  If you want to do JS operations,+  // or any nontrivial work, you should add it to a work queue, and+  // manage it externally.+  virtual ~HostObject();++  // When JS wants a property with a given name from the HostObject,+  // it will call this method.  If it throws an exception, the call+  // will throw a JS \c Error object. By default this returns undefined.+  // \return the value for the property.+  virtual Value get(Runtime&, const PropNameID& name);++  // When JS wants to set a property with a given name on the HostObject,+  // it will call this method. If it throws an exception, the call will+  // throw a JS \c Error object. By default this throws a type error exception+  // mimicking the behavior of a frozen object in strict mode.+  virtual void set(Runtime&, const PropNameID& name, const Value& value);++  // When JS wants a list of property names for the HostObject, it will+  // call this method. If it throws an exception, the call will throw a+  // JS \c Error object. The default implementation returns empty vector.+  virtual std::vector<PropNameID> getPropertyNames(Runtime& rt);+};++#if JSI_VERSION >= 7+/// Native state (and destructor) that can be attached to any JS object+/// using setNativeState.+class JSI_EXPORT NativeState {+ public:+  virtual ~NativeState();+};+#endif++/// Represents a JS runtime.  Movable, but not copyable.  Note that+/// this object may not be thread-aware, but cannot be used safely from+/// multiple threads at once.  The application is responsible for+/// ensuring that it is used safely.  This could mean using the+/// Runtime from a single thread, using a mutex, doing all work on a+/// serial queue, etc.  This restriction applies to the methods of+/// this class, and any method in the API which take a Runtime& as an+/// argument.  Destructors (all but ~Scope), operators, or other methods+/// which do not take Runtime& as an argument are safe to call from any+/// thread, but it is still forbidden to make write operations on a single+/// instance of any class from more than one thread.  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 JSI_EXPORT Runtime {+ public:+  virtual ~Runtime();++  /// Evaluates the given JavaScript \c buffer.  \c sourceURL is used+  /// to annotate the stack trace if there is an exception.  The+  /// contents may be utf8-encoded JS source code, or binary bytecode+  /// whose format is specific to the implementation.  If the input+  /// format is unknown, or evaluation causes an error, a JSIException+  /// will be thrown.+  /// Note this function should ONLY be used when there isn't another means+  /// through the JSI API. For example, it will be much slower to use this to+  /// call a global function than using the JSI APIs to read the function+  /// property from the global object and then calling it explicitly.+  virtual Value evaluateJavaScript(+      const std::shared_ptr<const Buffer>& buffer,+      const std::string& sourceURL) = 0;++  /// Prepares to evaluate the given JavaScript \c buffer by processing it into+  /// a form optimized for execution. This may include pre-parsing, compiling,+  /// etc. If the input is invalid (for example, cannot be parsed), a+  /// JSIException will be thrown. The resulting object is tied to the+  /// particular concrete type of Runtime from which it was created. It may be+  /// used (via evaluatePreparedJavaScript) in any Runtime of the same concrete+  /// type.+  /// The PreparedJavaScript object may be passed to multiple VM instances, so+  /// they can all share and benefit from the prepared script.+  /// As with evaluateJavaScript(), using JavaScript code should be avoided+  /// when the JSI API is sufficient.+  virtual std::shared_ptr<const PreparedJavaScript> prepareJavaScript(+      const std::shared_ptr<const Buffer>& buffer,+      std::string sourceURL) = 0;++  /// Evaluates a PreparedJavaScript. If evaluation causes an error, a+  /// JSIException will be thrown.+  /// As with evaluateJavaScript(), using JavaScript code should be avoided+  /// when the JSI API is sufficient.+  virtual Value evaluatePreparedJavaScript(+      const std::shared_ptr<const PreparedJavaScript>& js) = 0;++#if JSI_VERSION >= 4+  /// Drain the JavaScript VM internal Microtask (a.k.a. Job in ECMA262) queue.+  ///+  /// \param maxMicrotasksHint a hint to tell an implementation that it should+  /// make a best effort not execute more than the given number. It's default+  /// to -1 for infinity (unbounded execution).+  /// \return true if the queue is drained or false if there is more work to do.+  ///+  /// When there were exceptions thrown from the execution of microtasks,+  /// implementations shall discard the exceptional jobs. An implementation may+  /// \throw a \c JSError object to signal the hosts to handle. In that case, an+  /// implementation may or may not suspend the draining.+  ///+  /// Hosts may call this function again to resume the draining if it was+  /// suspended due to either exceptions or the \p maxMicrotasksHint bound.+  /// E.g. a host may repetitively invoke this function until the queue is+  /// drained to implement the "microtask checkpoint" defined in WHATWG HTML+  /// event loop: https://html.spec.whatwg.org/C#perform-a-microtask-checkpoint.+  ///+  /// Note that error propagation is only a concern if a host needs to implement+  /// `queueMicrotask`, a recent API that allows enqueueing arbitrary functions+  /// (hence may throw) as microtasks. Exceptions from ECMA-262 Promise Jobs are+  /// handled internally to VMs and are never propagated to hosts.+  ///+  /// This API offers some queue management to hosts at its best effort due to+  /// different behaviors and limitations imposed by different VMs and APIs. By+  /// the time this is written, An implementation may swallow exceptions (JSC),+  /// may not pause (V8), and may not support bounded executions.+  virtual bool drainMicrotasks(int maxMicrotasksHint = -1) = 0;+#endif++  /// \return the global object+  virtual Object global() = 0;++  /// \return a short printable description of the instance.  It should+  /// at least include some human-readable indication of the runtime+  /// implementation.  This should only be used by logging, debugging,+  /// and other developer-facing callers.+  virtual std::string description() = 0;++  /// \return whether or not the underlying runtime supports debugging via the+  /// Chrome remote debugging protocol.+  ///+  /// NOTE: the API for determining whether a runtime is debuggable and+  /// registering a runtime with the debugger is still in flux, so please don't+  /// use this API unless you know what you're doing.+  virtual bool isInspectable() = 0;++  /// \return an interface to extract metrics from this \c Runtime.  The default+  /// implementation of this function returns an \c Instrumentation instance+  /// which returns no metrics.+  virtual Instrumentation& instrumentation();++ protected:+  friend class Pointer;+  friend class PropNameID;+  friend class Symbol;+#if JSI_VERSION >= 6+  friend class BigInt;+#endif+  friend class String;+  friend class Object;+  friend class WeakObject;+  friend class Array;+  friend class ArrayBuffer;+  friend class Function;+  friend class Value;+  friend class Scope;+  friend class JSError;++  // Potential optimization: avoid the cloneFoo() virtual dispatch,+  // and instead just fix the number of fields, and copy them, since+  // in practice they are trivially copyable.  Sufficient use of+  // rvalue arguments/methods would also reduce the number of clones.++  struct PointerValue {+    virtual void invalidate() = 0;++   protected:+    virtual ~PointerValue() = default;+  };++  virtual PointerValue* cloneSymbol(const Runtime::PointerValue* pv) = 0;+#if JSI_VERSION >= 6+  virtual PointerValue* cloneBigInt(const Runtime::PointerValue* pv) = 0;+#endif+  virtual PointerValue* cloneString(const Runtime::PointerValue* pv) = 0;+  virtual PointerValue* cloneObject(const Runtime::PointerValue* pv) = 0;+  virtual PointerValue* clonePropNameID(const Runtime::PointerValue* pv) = 0;++  virtual PropNameID createPropNameIDFromAscii(+      const char* str,+      size_t length) = 0;+  virtual PropNameID createPropNameIDFromUtf8(+      const uint8_t* utf8,+      size_t length) = 0;+  virtual PropNameID createPropNameIDFromString(const String& str) = 0;+#if JSI_VERSION >= 5+  virtual PropNameID createPropNameIDFromSymbol(const Symbol& sym) = 0;+#endif+  virtual std::string utf8(const PropNameID&) = 0;+  virtual bool compare(const PropNameID&, const PropNameID&) = 0;++  virtual std::string symbolToString(const Symbol&) = 0;++#if JSI_VERSION >= 8+  virtual BigInt createBigIntFromInt64(int64_t) = 0;+  virtual BigInt createBigIntFromUint64(uint64_t) = 0;+  virtual bool bigintIsInt64(const BigInt&) = 0;+  virtual bool bigintIsUint64(const BigInt&) = 0;+  virtual uint64_t truncate(const BigInt&) = 0;+  virtual String bigintToString(const BigInt&, int) = 0;+#endif++  virtual String createStringFromAscii(const char* str, size_t length) = 0;+  virtual String createStringFromUtf8(const uint8_t* utf8, size_t length) = 0;+  virtual std::string utf8(const String&) = 0;++  // \return a \c Value created from a utf8-encoded JSON string. The default+  // implementation creates a \c String and invokes JSON.parse.+#if JSI_VERSION >= 2+  virtual Value createValueFromJsonUtf8(const uint8_t* json, size_t length);+#endif++  virtual Object createObject() = 0;+  virtual Object createObject(std::shared_ptr<HostObject> ho) = 0;+  virtual std::shared_ptr<HostObject> getHostObject(const jsi::Object&) = 0;+  virtual HostFunctionType& getHostFunction(const jsi::Function&) = 0;++#if JSI_VERSION >= 7+  virtual bool hasNativeState(const jsi::Object&) = 0;+  virtual std::shared_ptr<NativeState> getNativeState(const jsi::Object&) = 0;+  virtual void setNativeState(+      const jsi::Object&,+      std::shared_ptr<NativeState> state) = 0;+#endif++  virtual Value getProperty(const Object&, const PropNameID& name) = 0;+  virtual Value getProperty(const Object&, const String& name) = 0;+  virtual bool hasProperty(const Object&, const PropNameID& name) = 0;+  virtual bool hasProperty(const Object&, const String& name) = 0;+  virtual void setPropertyValue(+      JSI_CONST_10 Object&,+      const PropNameID& name,+      const Value& value) = 0;+  virtual void setPropertyValue(+      JSI_CONST_10 Object&,+      const String& name,+      const Value& value) = 0;++  virtual bool isArray(const Object&) const = 0;+  virtual bool isArrayBuffer(const Object&) const = 0;+  virtual bool isFunction(const Object&) const = 0;+  virtual bool isHostObject(const jsi::Object&) const = 0;+  virtual bool isHostFunction(const jsi::Function&) const = 0;+  virtual Array getPropertyNames(const Object&) = 0;++  virtual WeakObject createWeakObject(const Object&) = 0;+  virtual Value lockWeakObject(JSI_NO_CONST_3 JSI_CONST_10 WeakObject&) = 0;++  virtual Array createArray(size_t length) = 0;+#if JSI_VERSION >= 9+  virtual ArrayBuffer createArrayBuffer(+      std::shared_ptr<MutableBuffer> buffer) = 0;+#endif+  virtual size_t size(const Array&) = 0;+  virtual size_t size(const ArrayBuffer&) = 0;+  virtual uint8_t* data(const ArrayBuffer&) = 0;+  virtual Value getValueAtIndex(const Array&, size_t i) = 0;+  virtual void+  setValueAtIndexImpl(JSI_CONST_10 Array&, size_t i, const Value& value) = 0;++  virtual Function createFunctionFromHostFunction(+      const PropNameID& name,+      unsigned int paramCount,+      HostFunctionType func) = 0;+  virtual Value call(+      const Function&,+      const Value& jsThis,+      const Value* args,+      size_t count) = 0;+  virtual Value+  callAsConstructor(const Function&, const Value* args, size_t count) = 0;++  // Private data for managing scopes.+  struct ScopeState;+  virtual ScopeState* pushScope();+  virtual void popScope(ScopeState*);++  virtual bool strictEquals(const Symbol& a, const Symbol& b) const = 0;+#if JSI_VERSION >= 6+  virtual bool strictEquals(const BigInt& a, const BigInt& b) const = 0;+#endif+  virtual bool strictEquals(const String& a, const String& b) const = 0;+  virtual bool strictEquals(const Object& a, const Object& b) const = 0;++  virtual bool instanceOf(const Object& o, const Function& f) = 0;++  // These exist so derived classes can access the private parts of+  // Value, Symbol, String, and Object, which are all friends of Runtime.+  template <typename T>+  static T make(PointerValue* pv);+#if JSI_VERSION >= 3+  static PointerValue* getPointerValue(Pointer& pointer);+#endif+  static const PointerValue* getPointerValue(const Pointer& pointer);+  static const PointerValue* getPointerValue(const Value& value);++  friend class ::FBJSRuntime;+  template <typename Plain, typename Base>+  friend class RuntimeDecorator;+};++// Base class for pointer-storing types.+class JSI_EXPORT Pointer {+ protected:+  explicit Pointer(Pointer&& other) : ptr_(other.ptr_) {+    other.ptr_ = nullptr;+  }++  ~Pointer() {+    if (ptr_) {+      ptr_->invalidate();+    }+  }++  Pointer& operator=(Pointer&& other);++  friend class Runtime;+  friend class Value;++  explicit Pointer(Runtime::PointerValue* ptr) : ptr_(ptr) {}++  typename Runtime::PointerValue* ptr_;+};++/// Represents something that can be a JS property key.  Movable, not copyable.+class JSI_EXPORT PropNameID : public Pointer {+ public:+  using Pointer::Pointer;++  PropNameID(Runtime& runtime, const PropNameID& other)+      : Pointer(runtime.clonePropNameID(other.ptr_)) {}++  PropNameID(PropNameID&& other) = default;+  PropNameID& operator=(PropNameID&& other) = default;++  /// Create a JS property name id from ascii values.  The data is+  /// copied.+  static PropNameID forAscii(Runtime& runtime, const char* str, size_t length) {+    return runtime.createPropNameIDFromAscii(str, length);+  }++  /// Create a property name id from a nul-terminated C ascii name.  The data is+  /// copied.+  static PropNameID forAscii(Runtime& runtime, const char* str) {+    return forAscii(runtime, str, strlen(str));+  }++  /// Create a PropNameID from a C++ string. The string is copied.+  static PropNameID forAscii(Runtime& runtime, const std::string& str) {+    return forAscii(runtime, str.c_str(), str.size());+  }++  /// Create a PropNameID from utf8 values.  The data is copied.+  /// Results are undefined if \p utf8 contains invalid code points.+  static PropNameID+  forUtf8(Runtime& runtime, const uint8_t* utf8, size_t length) {+    return runtime.createPropNameIDFromUtf8(utf8, length);+  }++  /// Create a PropNameID from utf8-encoded octets stored in a+  /// std::string.  The string data is transformed and copied.+  /// Results are undefined if \p utf8 contains invalid code points.+  static PropNameID forUtf8(Runtime& runtime, const std::string& utf8) {+    return runtime.createPropNameIDFromUtf8(+        reinterpret_cast<const uint8_t*>(utf8.data()), utf8.size());+  }++  /// Create a PropNameID from a JS string.+  static PropNameID forString(Runtime& runtime, const jsi::String& str) {+    return runtime.createPropNameIDFromString(str);+  }++#if JSI_VERSION >= 5+  /// Create a PropNameID from a JS symbol.+  static PropNameID forSymbol(Runtime& runtime, const jsi::Symbol& sym) {+    return runtime.createPropNameIDFromSymbol(sym);+  }+#endif++  // Creates a vector of PropNameIDs constructed from given arguments.+  template <typename... Args>+  static std::vector<PropNameID> names(Runtime& runtime, Args&&... args);++  // Creates a vector of given PropNameIDs.+  template <size_t N>+  static std::vector<PropNameID> names(PropNameID (&&propertyNames)[N]);++  /// Copies the data in a PropNameID as utf8 into a C++ string.+  std::string utf8(Runtime& runtime) const {+    return runtime.utf8(*this);+  }++  static bool compare(+      Runtime& runtime,+      const jsi::PropNameID& a,+      const jsi::PropNameID& b) {+    return runtime.compare(a, b);+  }++  friend class Runtime;+  friend class Value;+};++/// Represents a JS Symbol (es6).  Movable, not copyable.+/// TODO T40778724: this is a limited implementation sufficient for+/// the debugger not to crash when a Symbol is a property in an Object+/// or element in an array.  Complete support for creating will come+/// later.+class JSI_EXPORT Symbol : public Pointer {+ public:+  using Pointer::Pointer;++  Symbol(Symbol&& other) = default;+  Symbol& operator=(Symbol&& other) = default;++  /// \return whether a and b refer to the same symbol.+  static bool strictEquals(Runtime& runtime, const Symbol& a, const Symbol& b) {+    return runtime.strictEquals(a, b);+  }++  /// Converts a Symbol into a C++ string as JS .toString would.  The output+  /// will look like \c Symbol(description) .+  std::string toString(Runtime& runtime) const {+    return runtime.symbolToString(*this);+  }++  friend class Runtime;+  friend class Value;+};++#if JSI_VERSION >= 6+/// Represents a JS BigInt.  Movable, not copyable.+class JSI_EXPORT BigInt : public Pointer {+ public:+  using Pointer::Pointer;++  BigInt(BigInt&& other) = default;+  BigInt& operator=(BigInt&& other) = default;++#if JSI_VERSION >= 8+  /// Create a BigInt representing the signed 64-bit \p value.+  static BigInt fromInt64(Runtime& runtime, int64_t value) {+    return runtime.createBigIntFromInt64(value);+  }++  /// Create a BigInt representing the unsigned 64-bit \p value.+  static BigInt fromUint64(Runtime& runtime, uint64_t value) {+    return runtime.createBigIntFromUint64(value);+  }++  /// \return whether a === b.+  static bool strictEquals(Runtime& runtime, const BigInt& a, const BigInt& b) {+    return runtime.strictEquals(a, b);+  }++  /// \returns This bigint truncated to a signed 64-bit integer.+  int64_t getInt64(Runtime& runtime) const {+    return runtime.truncate(*this);+  }++  /// \returns Whether this bigint can be losslessly converted to int64_t.+  bool isInt64(Runtime& runtime) const {+    return runtime.bigintIsInt64(*this);+  }++  /// \returns This bigint truncated to a signed 64-bit integer. Throws a+  /// JSIException if the truncation is lossy.+  int64_t asInt64(Runtime& runtime) const;++  /// \returns This bigint truncated to an unsigned 64-bit integer.+  uint64_t getUint64(Runtime& runtime) const {+    return runtime.truncate(*this);+  }++  /// \returns Whether this bigint can be losslessly converted to uint64_t.+  bool isUint64(Runtime& runtime) const {+    return runtime.bigintIsUint64(*this);+  }++  /// \returns This bigint truncated to an unsigned 64-bit integer. Throws a+  /// JSIException if the truncation is lossy.+  uint64_t asUint64(Runtime& runtime) const;++  /// \returns this BigInt converted to a String in base \p radix. Throws a+  /// JSIException if radix is not in the [2, 36] range.+  inline String toString(Runtime& runtime, int radix = 10) const;+#endif++  friend class Runtime;+  friend class Value;+};+#endif++/// Represents a JS String.  Movable, not copyable.+class JSI_EXPORT String : public Pointer {+ public:+  using Pointer::Pointer;++  String(String&& other) = default;+  String& operator=(String&& other) = default;++  /// Create a JS string from ascii values.  The string data is+  /// copied.+  static String+  createFromAscii(Runtime& runtime, const char* str, size_t length) {+    return runtime.createStringFromAscii(str, length);+  }++  /// Create a JS string from a nul-terminated C ascii string.  The+  /// string data is copied.+  static String createFromAscii(Runtime& runtime, const char* str) {+    return createFromAscii(runtime, str, strlen(str));+  }++  /// Create a JS string from a C++ string.  The string data is+  /// copied.+  static String createFromAscii(Runtime& runtime, const std::string& str) {+    return createFromAscii(runtime, str.c_str(), str.size());+  }++  /// Create a JS string from utf8-encoded octets.  The string data is+  /// transformed and copied.  Results are undefined if \p utf8 contains invalid+  /// code points.+  static String+  createFromUtf8(Runtime& runtime, const uint8_t* utf8, size_t length) {+    return runtime.createStringFromUtf8(utf8, length);+  }++  /// Create a JS string from utf8-encoded octets stored in a+  /// std::string.  The string data is transformed and copied.  Results are+  /// undefined if \p utf8 contains invalid code points.+  static String createFromUtf8(Runtime& runtime, const std::string& utf8) {+    return runtime.createStringFromUtf8(+        reinterpret_cast<const uint8_t*>(utf8.data()), utf8.length());+  }++  /// \return whether a and b contain the same characters.+  static bool strictEquals(Runtime& runtime, const String& a, const String& b) {+    return runtime.strictEquals(a, b);+  }++  /// Copies the data in a JS string as utf8 into a C++ string.+  std::string utf8(Runtime& runtime) const {+    return runtime.utf8(*this);+  }++  friend class Runtime;+  friend class Value;+};++class Array;+class Function;++/// Represents a JS Object.  Movable, not copyable.+class JSI_EXPORT Object : public Pointer {+ public:+  using Pointer::Pointer;++  Object(Object&& other) = default;+  Object& operator=(Object&& other) = default;++  /// Creates a new Object instance, like '{}' in JS.+  Object(Runtime& runtime) : Object(runtime.createObject()) {}++  static Object createFromHostObject(+      Runtime& runtime,+      std::shared_ptr<HostObject> ho) {+    return runtime.createObject(ho);+  }++  /// \return whether this and \c obj are the same JSObject or not.+  static bool strictEquals(Runtime& runtime, const Object& a, const Object& b) {+    return runtime.strictEquals(a, b);+  }++  /// \return the result of `this instanceOf ctor` in JS.+  bool instanceOf(Runtime& rt, const Function& ctor) JSI_CONST_10 {+    return rt.instanceOf(*this, ctor);+  }++  /// \return the property of the object with the given ascii name.+  /// If the name isn't a property on the object, returns the+  /// undefined value.+  Value getProperty(Runtime& runtime, const char* name) const;++  /// \return the property of the object with the String name.+  /// If the name isn't a property on the object, returns the+  /// undefined value.+  Value getProperty(Runtime& runtime, const String& name) const;++  /// \return the property of the object with the given JS PropNameID+  /// name.  If the name isn't a property on the object, returns the+  /// undefined value.+  Value getProperty(Runtime& runtime, const PropNameID& name) const;++  /// \return true if and only if the object has a property with the+  /// given ascii name.+  bool hasProperty(Runtime& runtime, const char* name) const;++  /// \return true if and only if the object has a property with the+  /// given String name.+  bool hasProperty(Runtime& runtime, const String& name) const;++  /// \return true if and only if the object has a property with the+  /// given PropNameID name.+  bool hasProperty(Runtime& runtime, const PropNameID& name) const;++  /// Sets the property value from a Value or anything which can be+  /// used to make one: nullptr_t, bool, double, int, const char*,+  /// String, or Object.+  template <typename T>+  void setProperty(Runtime& runtime, const char* name, T&& value) JSI_CONST_10;++  /// Sets the property value from a Value or anything which can be+  /// used to make one: nullptr_t, bool, double, int, const char*,+  /// String, or Object.+  template <typename T>+  void setProperty(Runtime& runtime, const String& name, T&& value)+      JSI_CONST_10;++  /// Sets the property value from a Value or anything which can be+  /// used to make one: nullptr_t, bool, double, int, const char*,+  /// String, or Object.+  template <typename T>+  void setProperty(Runtime& runtime, const PropNameID& name, T&& value)+      JSI_CONST_10;++  /// \return true iff JS \c Array.isArray() would return \c true.  If+  /// so, then \c getArray() will succeed.+  bool isArray(Runtime& runtime) const {+    return runtime.isArray(*this);+  }++  /// \return true iff the Object is an ArrayBuffer. If so, then \c+  /// getArrayBuffer() will succeed.+  bool isArrayBuffer(Runtime& runtime) const {+    return runtime.isArrayBuffer(*this);+  }++  /// \return true iff the Object is callable.  If so, then \c+  /// getFunction will succeed.+  bool isFunction(Runtime& runtime) const {+    return runtime.isFunction(*this);+  }++  /// \return true iff the Object was initialized with \c createFromHostObject+  /// and the HostObject passed is of type \c T. If returns \c true then+  /// \c getHostObject<T> will succeed.+  template <typename T = HostObject>+  bool isHostObject(Runtime& runtime) const;++  /// \return an Array instance which refers to the same underlying+  /// object.  If \c isArray() would return false, this will assert.+  Array getArray(Runtime& runtime) const&;++  /// \return an Array instance which refers to the same underlying+  /// object.  If \c isArray() would return false, this will assert.+  Array getArray(Runtime& runtime) &&;++  /// \return an Array instance which refers to the same underlying+  /// object.  If \c isArray() would return false, this will throw+  /// JSIException.+  Array asArray(Runtime& runtime) const&;++  /// \return an Array instance which refers to the same underlying+  /// object.  If \c isArray() would return false, this will throw+  /// JSIException.+  Array asArray(Runtime& runtime) &&;++  /// \return an ArrayBuffer instance which refers to the same underlying+  /// object.  If \c isArrayBuffer() would return false, this will assert.+  ArrayBuffer getArrayBuffer(Runtime& runtime) const&;++  /// \return an ArrayBuffer instance which refers to the same underlying+  /// object.  If \c isArrayBuffer() would return false, this will assert.+  ArrayBuffer getArrayBuffer(Runtime& runtime) &&;++  /// \return a Function instance which refers to the same underlying+  /// object.  If \c isFunction() would return false, this will assert.+  Function getFunction(Runtime& runtime) const&;++  /// \return a Function instance which refers to the same underlying+  /// object.  If \c isFunction() would return false, this will assert.+  Function getFunction(Runtime& runtime) &&;++  /// \return a Function instance which refers to the same underlying+  /// object.  If \c isFunction() would return false, this will throw+  /// JSIException.+  Function asFunction(Runtime& runtime) const&;++  /// \return a Function instance which refers to the same underlying+  /// object.  If \c isFunction() would return false, this will throw+  /// JSIException.+  Function asFunction(Runtime& runtime) &&;++  /// \return a shared_ptr<T> which refers to the same underlying+  /// \c HostObject that was used to create this object. If \c isHostObject<T>+  /// is false, this will assert. Note that this does a type check and will+  /// assert if the underlying HostObject isn't of type \c T+  template <typename T = HostObject>+  std::shared_ptr<T> getHostObject(Runtime& runtime) const;++  /// \return a shared_ptr<T> which refers to the same underlying+  /// \c HostObject that was used to create this object. If \c isHostObject<T>+  /// is false, this will throw.+  template <typename T = HostObject>+  std::shared_ptr<T> asHostObject(Runtime& runtime) const;++#if JSI_VERSION >= 7+  /// \return whether this object has native state of type T previously set by+  /// \c setNativeState.+  template <typename T = NativeState>+  bool hasNativeState(Runtime& runtime) const;++  /// \return a shared_ptr to the state previously set by \c setNativeState.+  /// If \c hasNativeState<T> is false, this will assert. Note that this does a+  /// type check and will assert if the native state isn't of type \c T+  template <typename T = NativeState>+  std::shared_ptr<T> getNativeState(Runtime& runtime) const;++  /// Set the internal native state property of this object, overwriting any old+  /// value. Creates a new shared_ptr to the object managed by \p state, which+  /// will live until the value at this property becomes unreachable.+  ///+  /// Throws a type error if this object is a proxy or host object.+  void setNativeState(Runtime& runtime, std::shared_ptr<NativeState> state)+      const;+#endif++  /// \return same as \c getProperty(name).asObject(), except with+  /// a better exception message.+  Object getPropertyAsObject(Runtime& runtime, const char* name) const;++  /// \return similar to \c+  /// getProperty(name).getObject().getFunction(), except it will+  /// throw JSIException instead of asserting if the property is+  /// not an object, or the object is not callable.+  Function getPropertyAsFunction(Runtime& runtime, const char* name) const;++  /// \return an Array consisting of all enumerable property names in+  /// the object and its prototype chain.  All values in the return+  /// will be isString().  (This is probably not optimal, but it+  /// works.  I only need it in one place.)+  Array getPropertyNames(Runtime& runtime) const;++ protected:+  void setPropertyValue(+      Runtime& runtime,+      const String& name,+      const Value& value) JSI_CONST_10 {+    return runtime.setPropertyValue(*this, name, value);+  }++  void setPropertyValue(+      Runtime& runtime,+      const PropNameID& name,+      const Value& value) JSI_CONST_10 {+    return runtime.setPropertyValue(*this, name, value);+  }++  friend class Runtime;+  friend class Value;+};++/// Represents a weak reference to a JS Object.  If the only reference+/// to an Object are these, the object is eligible for GC.  Method+/// names are inspired by C++ weak_ptr.  Movable, not copyable.+class JSI_EXPORT WeakObject : public Pointer {+ public:+  using Pointer::Pointer;++  WeakObject(WeakObject&& other) = default;+  WeakObject& operator=(WeakObject&& other) = default;++  /// Create a WeakObject from an Object.+  WeakObject(Runtime& runtime, const Object& o)+      : WeakObject(runtime.createWeakObject(o)) {}++  /// \return a Value representing the underlying Object if it is still valid;+  /// otherwise returns \c undefined.  Note that this method has nothing to do+  /// with threads or concurrency.  The name is based on std::weak_ptr::lock()+  /// which serves a similar purpose.+  Value lock(Runtime& runtime) JSI_CONST_10;++  friend class Runtime;+};++/// Represents a JS Object which can be efficiently used as an array+/// with integral indices.+class JSI_EXPORT Array : public Object {+ public:+  Array(Array&&) = default;+  /// Creates a new Array instance, with \c length undefined elements.+  Array(Runtime& runtime, size_t length) : Array(runtime.createArray(length)) {}++  Array& operator=(Array&&) = default;++  /// \return the size of the Array, according to its length property.+  /// (C++ naming convention)+  size_t size(Runtime& runtime) const {+    return runtime.size(*this);+  }++  /// \return the size of the Array, according to its length property.+  /// (JS naming convention)+  size_t length(Runtime& runtime) const {+    return size(runtime);+  }++  /// \return the property of the array at index \c i.  If there is no+  /// such property, returns the undefined value.  If \c i is out of+  /// range [ 0..\c length ] throws a JSIException.+  Value getValueAtIndex(Runtime& runtime, size_t i) const;++  /// Sets the property of the array at index \c i.  The argument+  /// value behaves as with Object::setProperty().  If \c i is out of+  /// range [ 0..\c length ] throws a JSIException.+  template <typename T>+  void setValueAtIndex(Runtime& runtime, size_t i, T&& value) JSI_CONST_10;++  /// There is no current API for changing the size of an array once+  /// created.  We'll probably need that eventually.++  /// Creates a new Array instance from provided values+  template <typename... Args>+  static Array createWithElements(Runtime&, Args&&... args);++  /// Creates a new Array instance from initializer list.+  static Array createWithElements(+      Runtime& runtime,+      std::initializer_list<Value> elements);++ private:+  friend class Object;+  friend class Value;++  void setValueAtIndexImpl(Runtime& runtime, size_t i, const Value& value)+      JSI_CONST_10 {+    return runtime.setValueAtIndexImpl(*this, i, value);+  }++  Array(Runtime::PointerValue* value) : Object(value) {}+};++/// Represents a JSArrayBuffer+class JSI_EXPORT ArrayBuffer : public Object {+ public:+  ArrayBuffer(ArrayBuffer&&) = default;+  ArrayBuffer& operator=(ArrayBuffer&&) = default;++#if JSI_VERSION >= 9+  ArrayBuffer(Runtime& runtime, std::shared_ptr<MutableBuffer> buffer)+      : ArrayBuffer(runtime.createArrayBuffer(std::move(buffer))) {}+#endif++  /// \return the size of the ArrayBuffer, according to its byteLength property.+  /// (C++ naming convention)+  size_t size(Runtime& runtime) const {+    return runtime.size(*this);+  }++  size_t length(Runtime& runtime) const {+    return runtime.size(*this);+  }++  uint8_t* data(Runtime& runtime) JSI_CONST_10 {+    return runtime.data(*this);+  }++ private:+  friend class Object;+  friend class Value;++  ArrayBuffer(Runtime::PointerValue* value) : Object(value) {}+};++/// Represents a JS Object which is guaranteed to be Callable.+class JSI_EXPORT Function : public Object {+ public:+  Function(Function&&) = default;+  Function& operator=(Function&&) = default;++  /// Create a function which, when invoked, calls C++ code. If the+  /// function throws an exception, a JS Error will be created and+  /// thrown.+  /// \param name the name property for the function.+  /// \param paramCount the length property for the function, which+  /// may not be the number of arguments the function is passed.+  static Function createFromHostFunction(+      Runtime& runtime,+      const jsi::PropNameID& name,+      unsigned int paramCount,+      jsi::HostFunctionType func);++  /// Calls the function with \c count \c args.  The \c this value of the JS+  /// function will not be set by the C++ caller, similar to calling+  /// Function.prototype.apply(undefined, args) in JS.+  /// \b Note: as with Function.prototype.apply, \c this may not always be+  /// \c undefined in the function itself.  If the function is non-strict,+  /// \c this will be set to the global object.+  Value call(Runtime& runtime, const Value* args, size_t count) const;++  /// Calls the function with a \c std::initializer_list of Value+  /// arguments.  The \c this value of the JS function will not be set by the+  /// C++ caller, similar to calling Function.prototype.apply(undefined, args)+  /// in JS.+  /// \b Note: as with Function.prototype.apply, \c this may not always be+  /// \c undefined in the function itself.  If the function is non-strict,+  /// \c this will be set to the global object.+  Value call(Runtime& runtime, std::initializer_list<Value> args) const;++  /// Calls the function with any number of arguments similarly to+  /// Object::setProperty().  The \c this value of the JS function will not be+  /// set by the C++ caller, similar to calling+  /// Function.prototype.call(undefined, ...args) in JS.+  /// \b Note: as with Function.prototype.call, \c this may not always be+  /// \c undefined in the function itself.  If the function is non-strict,+  /// \c this will be set to the global object.+  template <typename... Args>+  Value call(Runtime& runtime, Args&&... args) const;++  /// Calls the function with \c count \c args and \c jsThis value passed+  /// as the \c this value.+  Value callWithThis(+      Runtime& Runtime,+      const Object& jsThis,+      const Value* args,+      size_t count) const;++  /// Calls the function with a \c std::initializer_list of Value+  /// arguments and \c jsThis passed as the \c this value.+  Value callWithThis(+      Runtime& runtime,+      const Object& jsThis,+      std::initializer_list<Value> args) const;++  /// Calls the function with any number of arguments similarly to+  /// Object::setProperty(), and with \c jsThis passed as the \c this value.+  template <typename... Args>+  Value callWithThis(Runtime& runtime, const Object& jsThis, Args&&... args)+      const;++  /// Calls the function as a constructor with \c count \c args. Equivalent+  /// to calling `new Func` where `Func` is the js function reqresented by+  /// this.+  Value callAsConstructor(Runtime& runtime, const Value* args, size_t count)+      const;++  /// Same as above `callAsConstructor`, except use an initializer_list to+  /// supply the arguments.+  Value callAsConstructor(Runtime& runtime, std::initializer_list<Value> args)+      const;++  /// Same as above `callAsConstructor`, but automatically converts/wraps+  /// any argument with a jsi Value.+  template <typename... Args>+  Value callAsConstructor(Runtime& runtime, Args&&... args) const;++  /// Returns whether this was created with Function::createFromHostFunction.+  /// If true then you can use getHostFunction to get the underlying+  /// HostFunctionType.+  bool isHostFunction(Runtime& runtime) const {+    return runtime.isHostFunction(*this);+  }++  /// Returns the underlying HostFunctionType iff isHostFunction returns true+  /// and asserts otherwise. You can use this to use std::function<>::target+  /// to get the object that was passed to create the HostFunctionType.+  ///+  /// Note: The reference returned is borrowed from the JS object underlying+  ///       \c this, and thus only lasts as long as the object underlying+  ///       \c this does.+  HostFunctionType& getHostFunction(Runtime& runtime) const {+    assert(isHostFunction(runtime));+    return runtime.getHostFunction(*this);+  }++ private:+  friend class Object;+  friend class Value;++  Function(Runtime::PointerValue* value) : Object(value) {}+};++/// Represents any JS Value (undefined, null, boolean, number, symbol,+/// string, or object).  Movable, or explicitly copyable (has no copy+/// ctor).+class JSI_EXPORT Value {+ public:+  /// Default ctor creates an \c undefined JS value.+  Value() : Value(UndefinedKind) {}++  /// Creates a \c null JS value.+  /* implicit */ Value(std::nullptr_t) : kind_(NullKind) {}++  /// Creates a boolean JS value.+  /* implicit */ Value(bool b) : Value(BooleanKind) {+    data_.boolean = b;+  }++  /// Creates a number JS value.+  /* implicit */ Value(double d) : Value(NumberKind) {+    data_.number = d;+  }++  /// Creates a number JS value.+  /* implicit */ Value(int i) : Value(NumberKind) {+    data_.number = i;+  }++  /// Moves a Symbol, String, or Object rvalue into a new JS value.+  template <typename T>+  /* implicit */ Value(T&& other) : Value(kindOf(other)) {+    static_assert(+        std::is_base_of<Symbol, T>::value ||+#if JSI_VERSION >= 6+            std::is_base_of<BigInt, T>::value ||+#endif+            std::is_base_of<String, T>::value ||+            std::is_base_of<Object, T>::value,+        "Value cannot be implicitly move-constructed from this type");+    new (&data_.pointer) T(std::move(other));+  }++  /// Value("foo") will treat foo as a bool.  This makes doing that a+  /// compile error.+  template <typename T = void>+  Value(const char*) {+    static_assert(+        !std::is_same<void, T>::value,+        "Value cannot be constructed directly from const char*");+  }++  Value(Value&& value);++  /// Copies a Symbol lvalue into a new JS value.+  Value(Runtime& runtime, const Symbol& sym) : Value(SymbolKind) {+    new (&data_.pointer) Symbol(runtime.cloneSymbol(sym.ptr_));+  }++#if JSI_VERSION >= 6+  /// Copies a BigInt lvalue into a new JS value.+  Value(Runtime& runtime, const BigInt& bigint) : Value(BigIntKind) {+    new (&data_.pointer) BigInt(runtime.cloneBigInt(bigint.ptr_));+  }+#endif++  /// Copies a String lvalue into a new JS value.+  Value(Runtime& runtime, const String& str) : Value(StringKind) {+    new (&data_.pointer) String(runtime.cloneString(str.ptr_));+  }++  /// Copies a Object lvalue into a new JS value.+  Value(Runtime& runtime, const Object& obj) : Value(ObjectKind) {+    new (&data_.pointer) Object(runtime.cloneObject(obj.ptr_));+  }++  /// Creates a JS value from another Value lvalue.+  Value(Runtime& runtime, const Value& value);++  /// Value(rt, "foo") will treat foo as a bool.  This makes doing+  /// that a compile error.+  template <typename T = void>+  Value(Runtime&, const char*) {+    static_assert(+        !std::is_same<T, void>::value,+        "Value cannot be constructed directly from const char*");+  }++  ~Value();+  // \return the undefined \c Value.+  static Value undefined() {+    return Value();+  }++  // \return the null \c Value.+  static Value null() {+    return Value(nullptr);+  }++  // \return a \c Value created from a utf8-encoded JSON string.+  static Value+  createFromJsonUtf8(Runtime& runtime, const uint8_t* json, size_t length)+#if JSI_VERSION >= 2+  {+    return runtime.createValueFromJsonUtf8(json, length);+  }+#else+      ;+#endif++  /// \return according to the Strict Equality Comparison algorithm, see:+  /// https://262.ecma-international.org/11.0/#sec-strict-equality-comparison+  static bool strictEquals(Runtime& runtime, const Value& a, const Value& b);++  Value& operator=(Value&& other) {+    this->~Value();+    new (this) Value(std::move(other));+    return *this;+  }++  bool isUndefined() const {+    return kind_ == UndefinedKind;+  }++  bool isNull() const {+    return kind_ == NullKind;+  }++  bool isBool() const {+    return kind_ == BooleanKind;+  }++  bool isNumber() const {+    return kind_ == NumberKind;+  }++  bool isString() const {+    return kind_ == StringKind;+  }++#if JSI_VERSION >= 6+  bool isBigInt() const {+    return kind_ == BigIntKind;+  }+#endif++  bool isSymbol() const {+    return kind_ == SymbolKind;+  }++  bool isObject() const {+    return kind_ == ObjectKind;+  }++  /// \return the boolean value, or asserts if not a boolean.+  bool getBool() const {+    assert(isBool());+    return data_.boolean;+  }++  /// \return the boolean value, or throws JSIException if not a+  /// boolean.+  bool asBool() const;++  /// \return the number value, or asserts if not a number.+  double getNumber() const {+    assert(isNumber());+    return data_.number;+  }++  /// \return the number value, or throws JSIException if not a+  /// number.+  double asNumber() const;++  /// \return the Symbol value, or asserts if not a symbol.+  Symbol getSymbol(Runtime& runtime) const& {+    assert(isSymbol());+    return Symbol(runtime.cloneSymbol(data_.pointer.ptr_));+  }++  /// \return the Symbol value, or asserts if not a symbol.+  /// Can be used on rvalue references to avoid cloning more symbols.+  Symbol getSymbol(Runtime&) && {+    assert(isSymbol());+    auto ptr = data_.pointer.ptr_;+    data_.pointer.ptr_ = nullptr;+    return static_cast<Symbol>(ptr);+  }++  /// \return the Symbol value, or throws JSIException if not a+  /// symbol+  Symbol asSymbol(Runtime& runtime) const&;+  Symbol asSymbol(Runtime& runtime) &&;++#if JSI_VERSION >= 6+  /// \return the BigInt value, or asserts if not a bigint.+  BigInt getBigInt(Runtime& runtime) const& {+    assert(isBigInt());+    return BigInt(runtime.cloneBigInt(data_.pointer.ptr_));+  }++  /// \return the BigInt value, or asserts if not a bigint.+  /// Can be used on rvalue references to avoid cloning more bigints.+  BigInt getBigInt(Runtime&) && {+    assert(isBigInt());+    auto ptr = data_.pointer.ptr_;+    data_.pointer.ptr_ = nullptr;+    return static_cast<BigInt>(ptr);+  }++  /// \return the BigInt value, or throws JSIException if not a+  /// bigint+  BigInt asBigInt(Runtime& runtime) const&;+  BigInt asBigInt(Runtime& runtime) &&;+#endif++  /// \return the String value, or asserts if not a string.+  String getString(Runtime& runtime) const& {+    assert(isString());+    return String(runtime.cloneString(data_.pointer.ptr_));+  }++  /// \return the String value, or asserts if not a string.+  /// Can be used on rvalue references to avoid cloning more strings.+  String getString(Runtime&) && {+    assert(isString());+    auto ptr = data_.pointer.ptr_;+    data_.pointer.ptr_ = nullptr;+    return static_cast<String>(ptr);+  }++  /// \return the String value, or throws JSIException if not a+  /// string.+  String asString(Runtime& runtime) const&;+  String asString(Runtime& runtime) &&;++  /// \return the Object value, or asserts if not an object.+  Object getObject(Runtime& runtime) const& {+    assert(isObject());+    return Object(runtime.cloneObject(data_.pointer.ptr_));+  }++  /// \return the Object value, or asserts if not an object.+  /// Can be used on rvalue references to avoid cloning more objects.+  Object getObject(Runtime&) && {+    assert(isObject());+    auto ptr = data_.pointer.ptr_;+    data_.pointer.ptr_ = nullptr;+    return static_cast<Object>(ptr);+  }++  /// \return the Object value, or throws JSIException if not an+  /// object.+  Object asObject(Runtime& runtime) const&;+  Object asObject(Runtime& runtime) &&;++  // \return a String like JS .toString() would do.+  String toString(Runtime& runtime) const;++ private:+  friend class Runtime;++  enum ValueKind {+    UndefinedKind,+    NullKind,+    BooleanKind,+    NumberKind,+    SymbolKind,+#if JSI_VERSION >= 6+    BigIntKind,+#endif+    StringKind,+    ObjectKind,+    PointerKind = SymbolKind,+  };++  union Data {+    // Value's ctor and dtor will manage the lifecycle of the contained Data.+    Data() {+      static_assert(+          sizeof(Data) == sizeof(uint64_t),+          "Value data should fit in a 64-bit register");+    }+    ~Data() {}++    // scalars+    bool boolean;+    double number;+    // pointers+    Pointer pointer; // Symbol, String, Object, Array, Function+  };++  Value(ValueKind kind) : kind_(kind) {}++  constexpr static ValueKind kindOf(const Symbol&) {+    return SymbolKind;+  }+#if JSI_VERSION >= 6+  constexpr static ValueKind kindOf(const BigInt&) {+    return BigIntKind;+  }+#endif+  constexpr static ValueKind kindOf(const String&) {+    return StringKind;+  }+  constexpr static ValueKind kindOf(const Object&) {+    return ObjectKind;+  }++  ValueKind kind_;+  Data data_;++  // In the future: Value becomes NaN-boxed. See T40538354.+};++/// Not movable and not copyable RAII marker advising the underlying+/// JavaScript VM to track resources allocated since creation until+/// destruction so that they can be recycled eagerly when the Scope+/// goes out of scope instead of floating in the air until the next+/// garbage collection or any other delayed release occurs.+///+/// This API should be treated only as advice, implementations can+/// choose to ignore the fact that Scopes are created or destroyed.+///+/// This class is an exception to the rule allowing destructors to be+/// called without proper synchronization (see Runtime documentation).+/// The whole point of this class is to enable all sorts of clean ups+/// when the destructor is called and this proper synchronization is+/// required at that time.+///+/// Instances of this class are intended to be created as automatic stack+/// variables in which case destructor calls don't require any additional+/// locking, provided that the lock (if any) is managed with RAII helpers.+class JSI_EXPORT Scope {+ public:+  explicit Scope(Runtime& rt) : rt_(rt), prv_(rt.pushScope()) {}+  ~Scope() {+    rt_.popScope(prv_);+  };++  Scope(const Scope&) = delete;+  Scope(Scope&&) = delete;++  Scope& operator=(const Scope&) = delete;+  Scope& operator=(Scope&&) = delete;++  template <typename F>+  static auto callInNewScope(Runtime& rt, F f) -> decltype(f()) {+    Scope s(rt);+    return f();+  }++ private:+  Runtime& rt_;+  Runtime::ScopeState* prv_;+};++/// Base class for jsi exceptions+class JSI_EXPORT JSIException : public std::exception {+ protected:+  JSIException(){};+  JSIException(std::string what) : what_(std::move(what)){};++ public:+  JSIException(const JSIException&) = default;++  virtual const char* what() const noexcept override {+    return what_.c_str();+  }++  virtual ~JSIException() override;++ protected:+  std::string what_;+};++/// This exception will be thrown by API functions on errors not related to+/// JavaScript execution.+class JSI_EXPORT JSINativeException : public JSIException {+ public:+  JSINativeException(std::string what) : JSIException(std::move(what)) {}++  JSINativeException(const JSINativeException&) = default;++  virtual ~JSINativeException();+};++/// This exception will be thrown by API functions whenever a JS+/// operation causes an exception as described by the spec, or as+/// otherwise described.+class JSI_EXPORT JSError : public JSIException {+ public:+  /// Creates a JSError referring to provided \c value+  JSError(Runtime& r, Value&& value);++  /// Creates a JSError referring to new \c Error instance capturing current+  /// JavaScript stack. The error message property is set to given \c message.+  JSError(Runtime& rt, std::string message);++  /// Creates a JSError referring to new \c Error instance capturing current+  /// JavaScript stack. The error message property is set to given \c message.+  JSError(Runtime& rt, const char* message)+      : JSError(rt, std::string(message)){};++  /// Creates a JSError referring to a JavaScript Object having message and+  /// stack properties set to provided values.+  JSError(Runtime& rt, std::string message, std::string stack);++  /// Creates a JSError referring to provided value and what string+  /// set to provided message.  This argument order is a bit weird,+  /// but necessary to avoid ambiguity with the above.+  JSError(std::string what, Runtime& rt, Value&& value);++  JSError(const JSError&) = default;++  virtual ~JSError();++  const std::string& getStack() const {+    return stack_;+  }++  const std::string& getMessage() const {+    return message_;+  }++  const jsi::Value& value() const {+    assert(value_);+    return *value_;+  }++ private:+  // This initializes the value_ member and does some other+  // validation, so it must be called by every branch through the+  // constructors.+  void setValue(Runtime& rt, Value&& value);++  // This needs to be on the heap, because throw requires the object+  // be copyable, and Value is not.+  std::shared_ptr<jsi::Value> value_;+  std::string message_;+  std::string stack_;+};++} // namespace jsi+} // namespace facebook++#include <jsi/jsi-inl.h>

jsi/jsilib-posix.cpp

new file mode 100644@@ -0,0 +1,108 @@+/*+ * Copyright (c) Meta Platforms, Inc. and affiliates.+ *+ * This source code is licensed under the MIT license found in the+ * LICENSE file in the root directory of this source tree.+ */++#ifndef _WINDOWS++#include <fcntl.h>+#include <sys/mman.h>+#include <sys/stat.h>+#include <sys/types.h>+#include <unistd.h>+#include <cerrno>+#include <cstdarg>+#include <stdexcept>++#include <jsi/jsilib.h>++namespace facebook {+namespace jsi {++namespace {++constexpr size_t kErrorBufferSize = 512;++__attribute__((format(printf, 1, 2))) void throwFormattedError(+    const char* fmt,+    ...) {+  char logBuffer[kErrorBufferSize];++  va_list va_args;+  va_start(va_args, fmt);+  int result = vsnprintf(logBuffer, sizeof(logBuffer), fmt, va_args);+  va_end(va_args);++  if (result < 0) {+    throw JSINativeException(+        std::string("Failed to format error message: ") + fmt);+  }++  throw JSINativeException(logBuffer);+}++class ScopedFile {+ public:+  ScopedFile(const std::string& path)+      : path_(path), fd_(::open(path.c_str(), O_RDONLY)) {+    if (fd_ == -1) {+      throwFormattedError(+          "Could not open %s: %s", path.c_str(), strerror(errno));+    }+  }++  ~ScopedFile() {+    ::close(fd_);+  }++  size_t size() {+    struct stat fileInfo;+    if (::fstat(fd_, &fileInfo) == -1) {+      throwFormattedError(+          "Could not stat %s: %s", path_.c_str(), strerror(errno));+    }+    return fileInfo.st_size;+  }++  uint8_t* mmap(size_t size) {+    void* result = ::mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd_, 0);+    if (result == MAP_FAILED) {+      throwFormattedError(+          "Could not mmap %s: %s", path_.c_str(), strerror(errno));+    }+    return reinterpret_cast<uint8_t*>(result);+  }++  const std::string& path_;+  const int fd_;+};++} // namespace++FileBuffer::FileBuffer(const std::string& path) {+  ScopedFile file(path);+  size_ = file.size();+  data_ = file.mmap(size_);+}++FileBuffer::~FileBuffer() {+  if (::munmap(data_, size_)) {+    // terminate the program with pending exception+    try {+      throwFormattedError(+          "Could not unmap memory (%p, %zu bytes): %s",+          data_,+          size_,+          strerror(errno));+    } catch (...) {+      std::terminate();+    }+  }+}++} // namespace jsi+} // namespace facebook++#endif // !defined(_WINDOWS)

jsi/jsilib-windows.cpp

new file mode 100644@@ -0,0 +1,27 @@+/*+ * Copyright (c) Meta Platforms, Inc. and affiliates.+ *+ * This source code is licensed under the MIT license found in the+ * LICENSE file in the root directory of this source tree.+ */++#ifdef _WINDOWS++#include <jsi/jsilib.h>++namespace facebook {+namespace jsi {++FileBuffer::FileBuffer(const std::string&) {+  // TODO(T41045067) Implement this on Windows+  throw new JSINativeException("FileBuffer is not implemented on Windows");+}++FileBuffer::~FileBuffer() {+  assert(false && "FileBuffer is not implemented on Windows");+}++} // namespace jsi+} // namespace facebook++#endif //_WINDOWS

jsi/jsilib.h

new file mode 100644@@ -0,0 +1,59 @@+/*+ * Copyright (c) Meta Platforms, Inc. and affiliates.+ *+ * This source code is licensed under the MIT license found in the+ * LICENSE file in the root directory of this source tree.+ */++#pragma once++#include <jsi/jsi.h>++namespace facebook {+namespace jsi {++class FileBuffer : public Buffer {+ public:+  FileBuffer(const std::string& path);+  ~FileBuffer() override;++  size_t size() const override {+    return size_;+  }++  const uint8_t* data() const override {+    return data_;+  }++ private:+  size_t size_;+  uint8_t* data_;+};++// A trivial implementation of PreparedJavaScript that simply stores the source+// buffer and URL.+class SourceJavaScriptPreparation final : public jsi::PreparedJavaScript,+                                          public jsi::Buffer {+  std::shared_ptr<const jsi::Buffer> buf_;+  std::string sourceURL_;++ public:+  SourceJavaScriptPreparation(+      std::shared_ptr<const jsi::Buffer> buf,+      std::string sourceURL)+      : buf_(std::move(buf)), sourceURL_(std::move(sourceURL)) {}++  const std::string& sourceURL() const {+    return sourceURL_;+  }++  size_t size() const override {+    return buf_->size();+  }+  const uint8_t* data() const override {+    return buf_->data();+  }+};++} // namespace jsi+} // namespace facebook

jsi/test/testlib.cpp

new file mode 100644@@ -0,0 +1,1431 @@+/*+ * Copyright (c) Meta Platforms, Inc. and affiliates.+ *+ * This source code is licensed under the MIT license found in the+ * LICENSE file in the root directory of this source tree.+ */++#include <jsi/test/testlib.h>++#include <gtest/gtest.h>+#include <jsi/decorator.h>+#include <jsi/jsi.h>++#include <chrono>+#include <cstdlib>+#include <functional>+#include <thread>+#include <unordered_map>+#include <unordered_set>++using namespace facebook::jsi;++class JSITest : public JSITestBase {};++TEST_P(JSITest, RuntimeTest) {+  auto v = rt.evaluateJavaScript(std::make_unique<StringBuffer>("1"), "");+  EXPECT_EQ(v.getNumber(), 1);++  rt.evaluateJavaScript(std::make_unique<StringBuffer>("x = 1"), "");+  EXPECT_EQ(rt.global().getProperty(rt, "x").getNumber(), 1);+}++TEST_P(JSITest, PropNameIDTest) {+  // This is a little weird to test, because it doesn't really exist+  // in JS yet.  All I can do is create them, compare them, and+  // receive one as an argument to a HostObject.++  PropNameID quux = PropNameID::forAscii(rt, "quux1", 4);+  PropNameID movedQuux = std::move(quux);+  EXPECT_EQ(movedQuux.utf8(rt), "quux");+  movedQuux = PropNameID::forAscii(rt, "quux2");+  EXPECT_EQ(movedQuux.utf8(rt), "quux2");+  PropNameID copiedQuux = PropNameID(rt, movedQuux);+  EXPECT_TRUE(PropNameID::compare(rt, movedQuux, copiedQuux));++  EXPECT_TRUE(PropNameID::compare(rt, movedQuux, movedQuux));+  EXPECT_TRUE(PropNameID::compare(+      rt, movedQuux, PropNameID::forAscii(rt, std::string("quux2"))));+  EXPECT_FALSE(PropNameID::compare(+      rt, movedQuux, PropNameID::forAscii(rt, std::string("foo"))));+  uint8_t utf8[] = {0xF0, 0x9F, 0x86, 0x97};+  PropNameID utf8PropNameID = PropNameID::forUtf8(rt, utf8, sizeof(utf8));+  EXPECT_EQ(utf8PropNameID.utf8(rt), u8"\U0001F197");+  EXPECT_TRUE(PropNameID::compare(+      rt, utf8PropNameID, PropNameID::forUtf8(rt, utf8, sizeof(utf8))));+  PropNameID nonUtf8PropNameID = PropNameID::forUtf8(rt, "meow");+  EXPECT_TRUE(PropNameID::compare(+      rt, nonUtf8PropNameID, PropNameID::forAscii(rt, "meow")));+  EXPECT_EQ(nonUtf8PropNameID.utf8(rt), "meow");+  PropNameID strPropNameID =+      PropNameID::forString(rt, String::createFromAscii(rt, "meow"));+  EXPECT_TRUE(PropNameID::compare(rt, nonUtf8PropNameID, strPropNameID));++  auto names = PropNameID::names(+      rt, "Ala", std::string("ma"), PropNameID::forAscii(rt, "kota"));+  EXPECT_EQ(names.size(), 3);+  EXPECT_TRUE(+      PropNameID::compare(rt, names[0], PropNameID::forAscii(rt, "Ala")));+  EXPECT_TRUE(+      PropNameID::compare(rt, names[1], PropNameID::forAscii(rt, "ma")));+  EXPECT_TRUE(+      PropNameID::compare(rt, names[2], PropNameID::forAscii(rt, "kota")));+}++TEST_P(JSITest, StringTest) {+  EXPECT_TRUE(checkValue(String::createFromAscii(rt, "foobar", 3), "'foo'"));+  EXPECT_TRUE(checkValue(String::createFromAscii(rt, "foobar"), "'foobar'"));++  std::string baz = "baz";+  EXPECT_TRUE(checkValue(String::createFromAscii(rt, baz), "'baz'"));++  uint8_t utf8[] = {0xF0, 0x9F, 0x86, 0x97};+  EXPECT_TRUE(checkValue(+      String::createFromUtf8(rt, utf8, sizeof(utf8)), "'\\uD83C\\uDD97'"));++  EXPECT_EQ(eval("'quux'").getString(rt).utf8(rt), "quux");+  EXPECT_EQ(eval("'\\u20AC'").getString(rt).utf8(rt), "\xe2\x82\xac");++  String quux = String::createFromAscii(rt, "quux");+  String movedQuux = std::move(quux);+  EXPECT_EQ(movedQuux.utf8(rt), "quux");+  movedQuux = String::createFromAscii(rt, "quux2");+  EXPECT_EQ(movedQuux.utf8(rt), "quux2");+}++TEST_P(JSITest, ObjectTest) {+  eval("x = {1:2, '3':4, 5:'six', 'seven':['eight', 'nine']}");+  Object x = rt.global().getPropertyAsObject(rt, "x");+  EXPECT_EQ(x.getPropertyNames(rt).size(rt), 4);+  EXPECT_TRUE(x.hasProperty(rt, "1"));+  EXPECT_TRUE(x.hasProperty(rt, PropNameID::forAscii(rt, "1")));+  EXPECT_FALSE(x.hasProperty(rt, "2"));+  EXPECT_FALSE(x.hasProperty(rt, PropNameID::forAscii(rt, "2")));+  EXPECT_TRUE(x.hasProperty(rt, "3"));+  EXPECT_TRUE(x.hasProperty(rt, PropNameID::forAscii(rt, "3")));+  EXPECT_TRUE(x.hasProperty(rt, "seven"));+  EXPECT_TRUE(x.hasProperty(rt, PropNameID::forAscii(rt, "seven")));+  EXPECT_EQ(x.getProperty(rt, "1").getNumber(), 2);+  EXPECT_EQ(x.getProperty(rt, PropNameID::forAscii(rt, "1")).getNumber(), 2);+  EXPECT_EQ(x.getProperty(rt, "3").getNumber(), 4);+  Value five = 5;+  EXPECT_EQ(+      x.getProperty(rt, PropNameID::forString(rt, five.toString(rt)))+          .getString(rt)+          .utf8(rt),+      "six");++  x.setProperty(rt, "ten", 11);+  EXPECT_EQ(x.getPropertyNames(rt).size(rt), 5);+  EXPECT_TRUE(eval("x.ten == 11").getBool());++  x.setProperty(rt, "e_as_float", 2.71f);+  EXPECT_TRUE(eval("Math.abs(x.e_as_float - 2.71) < 0.001").getBool());++  x.setProperty(rt, "e_as_double", 2.71);+  EXPECT_TRUE(eval("x.e_as_double == 2.71").getBool());++  uint8_t utf8[] = {0xF0, 0x9F, 0x86, 0x97};+  String nonAsciiName = String::createFromUtf8(rt, utf8, sizeof(utf8));+  x.setProperty(rt, PropNameID::forString(rt, nonAsciiName), "emoji");+  EXPECT_EQ(x.getPropertyNames(rt).size(rt), 8);+  EXPECT_TRUE(eval("x['\\uD83C\\uDD97'] == 'emoji'").getBool());++  Object seven = x.getPropertyAsObject(rt, "seven");+  EXPECT_TRUE(seven.isArray(rt));+  Object evalf = rt.global().getPropertyAsObject(rt, "eval");+  EXPECT_TRUE(evalf.isFunction(rt));++  Object movedX = Object(rt);+  movedX = std::move(x);+  EXPECT_EQ(movedX.getPropertyNames(rt).size(rt), 8);+  EXPECT_EQ(movedX.getProperty(rt, "1").getNumber(), 2);++  Object obj = Object(rt);+  obj.setProperty(rt, "roses", "red");+  obj.setProperty(rt, "violets", "blue");+  Object oprop = Object(rt);+  obj.setProperty(rt, "oprop", oprop);+  obj.setProperty(rt, "aprop", Array(rt, 1));++  EXPECT_TRUE(function("function (obj) { return "+                       "obj.roses == 'red' && "+                       "obj['violets'] == 'blue' && "+                       "typeof obj.oprop == 'object' && "+                       "Array.isArray(obj.aprop); }")+                  .call(rt, obj)+                  .getBool());++  // Check that getPropertyNames doesn't return non-enumerable+  // properties.+  obj = function(+            "function () {"+            "  obj = {};"+            "  obj.a = 1;"+            "  Object.defineProperty(obj, 'b', {"+            "    enumerable: false,"+            "    value: 2"+            "  });"+            "  return obj;"+            "}")+            .call(rt)+            .getObject(rt);+  EXPECT_EQ(obj.getProperty(rt, "a").getNumber(), 1);+  EXPECT_EQ(obj.getProperty(rt, "b").getNumber(), 2);+  Array names = obj.getPropertyNames(rt);+  EXPECT_EQ(names.size(rt), 1);+  EXPECT_EQ(names.getValueAtIndex(rt, 0).getString(rt).utf8(rt), "a");+}++TEST_P(JSITest, HostObjectTest) {+  class ConstantHostObject : public HostObject {+    Value get(Runtime&, const PropNameID& sym) override {+      return 9000;+    }++    void set(Runtime&, const PropNameID&, const Value&) override {}+  };++  Object cho =+      Object::createFromHostObject(rt, std::make_shared<ConstantHostObject>());+  EXPECT_TRUE(function("function (obj) { return obj.someRandomProp == 9000; }")+                  .call(rt, cho)+                  .getBool());+  EXPECT_TRUE(cho.isHostObject(rt));+  EXPECT_TRUE(cho.getHostObject<ConstantHostObject>(rt).get() != nullptr);++  struct SameRuntimeHostObject : HostObject {+    SameRuntimeHostObject(Runtime& rt) : rt_(rt){};++    Value get(Runtime& rt, const PropNameID& sym) override {+      EXPECT_EQ(&rt, &rt_);+      return Value();+    }++    void set(Runtime& rt, const PropNameID& name, const Value& value) override {+      EXPECT_EQ(&rt, &rt_);+    }++    std::vector<PropNameID> getPropertyNames(Runtime& rt) override {+      EXPECT_EQ(&rt, &rt_);+      return {};+    }++    Runtime& rt_;+  };++  Object srho = Object::createFromHostObject(+      rt, std::make_shared<SameRuntimeHostObject>(rt));+  // Test get's Runtime is as expected+  function("function (obj) { return obj.isSame; }").call(rt, srho);+  // ... and set+  function("function (obj) { obj['k'] = 'v'; }").call(rt, srho);+  // ... and getPropertyNames+  function("function (obj) { for (k in obj) {} }").call(rt, srho);++  class TwiceHostObject : public HostObject {+    Value get(Runtime& rt, const PropNameID& sym) override {+      return String::createFromUtf8(rt, sym.utf8(rt) + sym.utf8(rt));+    }++    void set(Runtime&, const PropNameID&, const Value&) override {}+  };++  Object tho =+      Object::createFromHostObject(rt, std::make_shared<TwiceHostObject>());+  EXPECT_TRUE(function("function (obj) { return obj.abc == 'abcabc'; }")+                  .call(rt, tho)+                  .getBool());+  EXPECT_TRUE(function("function (obj) { return obj['def'] == 'defdef'; }")+                  .call(rt, tho)+                  .getBool());+  EXPECT_TRUE(function("function (obj) { return obj[12] === '1212'; }")+                  .call(rt, tho)+                  .getBool());+  EXPECT_TRUE(tho.isHostObject(rt));+  EXPECT_TRUE(+      std::dynamic_pointer_cast<ConstantHostObject>(tho.getHostObject(rt)) ==+      nullptr);+  EXPECT_TRUE(tho.getHostObject<TwiceHostObject>(rt).get() != nullptr);++  class PropNameIDHostObject : public HostObject {+    Value get(Runtime& rt, const PropNameID& sym) override {+      if (PropNameID::compare(rt, sym, PropNameID::forAscii(rt, "undef"))) {+        return Value::undefined();+      } else {+        return PropNameID::compare(+            rt, sym, PropNameID::forAscii(rt, "somesymbol"));+      }+    }++    void set(Runtime&, const PropNameID&, const Value&) override {}+  };++  Object sho = Object::createFromHostObject(+      rt, std::make_shared<PropNameIDHostObject>());+  EXPECT_TRUE(sho.isHostObject(rt));+  EXPECT_TRUE(function("function (obj) { return obj.undef; }")+                  .call(rt, sho)+                  .isUndefined());+  EXPECT_TRUE(function("function (obj) { return obj.somesymbol; }")+                  .call(rt, sho)+                  .getBool());+  EXPECT_FALSE(function("function (obj) { return obj.notsomuch; }")+                   .call(rt, sho)+                   .getBool());++  class BagHostObject : public HostObject {+   public:+    const std::string& getThing() {+      return bag_["thing"];+    }++   private:+    Value get(Runtime& rt, const PropNameID& sym) override {+      if (sym.utf8(rt) == "thing") {+        return String::createFromUtf8(rt, bag_[sym.utf8(rt)]);+      }+      return Value::undefined();+    }++    void set(Runtime& rt, const PropNameID& sym, const Value& val) override {+      std::string key(sym.utf8(rt));+      if (key == "thing") {+        bag_[key] = val.toString(rt).utf8(rt);+      }+    }++    std::unordered_map<std::string, std::string> bag_;+  };++  std::shared_ptr<BagHostObject> shbho = std::make_shared<BagHostObject>();+  Object bho = Object::createFromHostObject(rt, shbho);+  EXPECT_TRUE(bho.isHostObject(rt));+  EXPECT_TRUE(function("function (obj) { return obj.undef; }")+                  .call(rt, bho)+                  .isUndefined());+  EXPECT_EQ(+      function("function (obj) { obj.thing = 'hello'; return obj.thing; }")+          .call(rt, bho)+          .toString(rt)+          .utf8(rt),+      "hello");+  EXPECT_EQ(shbho->getThing(), "hello");++  class ThrowingHostObject : public HostObject {+    Value get(Runtime& rt, const PropNameID& sym) override {+      throw std::runtime_error("Cannot get");+    }++    void set(Runtime& rt, const PropNameID& sym, const Value& val) override {+      throw std::runtime_error("Cannot set");+    }+  };++  Object thro =+      Object::createFromHostObject(rt, std::make_shared<ThrowingHostObject>());+  EXPECT_TRUE(thro.isHostObject(rt));+  std::string exc;+  try {+    function("function (obj) { return obj.thing; }").call(rt, thro);+  } catch (const JSError& ex) {+    exc = ex.what();+  }+  EXPECT_NE(exc.find("Cannot get"), std::string::npos);+  exc = "";+  try {+    function("function (obj) { obj.thing = 'hello'; }").call(rt, thro);+  } catch (const JSError& ex) {+    exc = ex.what();+  }+  EXPECT_NE(exc.find("Cannot set"), std::string::npos);++  class NopHostObject : public HostObject {};+  Object nopHo =+      Object::createFromHostObject(rt, std::make_shared<NopHostObject>());+  EXPECT_TRUE(nopHo.isHostObject(rt));+  EXPECT_TRUE(function("function (obj) { return obj.thing; }")+                  .call(rt, nopHo)+                  .isUndefined());++  std::string nopExc;+  try {+    function("function (obj) { obj.thing = 'pika'; }").call(rt, nopHo);+  } catch (const JSError& ex) {+    nopExc = ex.what();+  }+  EXPECT_NE(nopExc.find("TypeError: "), std::string::npos);++  class HostObjectWithPropertyNames : public HostObject {+    std::vector<PropNameID> getPropertyNames(Runtime& rt) override {+      return PropNameID::names(+          rt, "a_prop", "1", "false", "a_prop", "3", "c_prop");+    }+  };++  Object howpn = Object::createFromHostObject(+      rt, std::make_shared<HostObjectWithPropertyNames>());+  EXPECT_TRUE(+      function(+          "function (o) { return Object.getOwnPropertyNames(o).length == 5 }")+          .call(rt, howpn)+          .getBool());++  auto hasOwnPropertyName = function(+      "function (o, p) {"+      "  return Object.getOwnPropertyNames(o).indexOf(p) >= 0"+      "}");+  EXPECT_TRUE(+      hasOwnPropertyName.call(rt, howpn, String::createFromAscii(rt, "a_prop"))+          .getBool());+  EXPECT_TRUE(+      hasOwnPropertyName.call(rt, howpn, String::createFromAscii(rt, "1"))+          .getBool());+  EXPECT_TRUE(+      hasOwnPropertyName.call(rt, howpn, String::createFromAscii(rt, "false"))+          .getBool());+  EXPECT_TRUE(+      hasOwnPropertyName.call(rt, howpn, String::createFromAscii(rt, "3"))+          .getBool());+  EXPECT_TRUE(+      hasOwnPropertyName.call(rt, howpn, String::createFromAscii(rt, "c_prop"))+          .getBool());+  EXPECT_FALSE(hasOwnPropertyName+                   .call(rt, howpn, String::createFromAscii(rt, "not_existing"))+                   .getBool());+}++TEST_P(JSITest, HostObjectProtoTest) {+  class ProtoHostObject : public HostObject {+    Value get(Runtime& rt, const PropNameID&) override {+      return String::createFromAscii(rt, "phoprop");+    }+  };++  rt.global().setProperty(+      rt,+      "pho",+      Object::createFromHostObject(rt, std::make_shared<ProtoHostObject>()));++  EXPECT_EQ(+      eval("({__proto__: pho})[Symbol.toPrimitive]").getString(rt).utf8(rt),+      "phoprop");+}++TEST_P(JSITest, ArrayTest) {+  eval("x = {1:2, '3':4, 5:'six', 'seven':['eight', 'nine']}");++  Object x = rt.global().getPropertyAsObject(rt, "x");+  Array names = x.getPropertyNames(rt);+  EXPECT_EQ(names.size(rt), 4);+  std::unordered_set<std::string> strNames;+  for (size_t i = 0; i < names.size(rt); ++i) {+    Value n = names.getValueAtIndex(rt, i);+    EXPECT_TRUE(n.isString());+    strNames.insert(n.getString(rt).utf8(rt));+  }++  EXPECT_EQ(strNames.size(), 4);+  EXPECT_EQ(strNames.count("1"), 1);+  EXPECT_EQ(strNames.count("3"), 1);+  EXPECT_EQ(strNames.count("5"), 1);+  EXPECT_EQ(strNames.count("seven"), 1);++  Object seven = x.getPropertyAsObject(rt, "seven");+  Array arr = seven.getArray(rt);++  EXPECT_EQ(arr.size(rt), 2);+  EXPECT_EQ(arr.getValueAtIndex(rt, 0).getString(rt).utf8(rt), "eight");+  EXPECT_EQ(arr.getValueAtIndex(rt, 1).getString(rt).utf8(rt), "nine");+  // TODO: test out of range++  EXPECT_EQ(x.getPropertyAsObject(rt, "seven").getArray(rt).size(rt), 2);++  // Check that property access with both symbols and strings can access+  // array values.+  EXPECT_EQ(seven.getProperty(rt, "0").getString(rt).utf8(rt), "eight");+  EXPECT_EQ(seven.getProperty(rt, "1").getString(rt).utf8(rt), "nine");+  seven.setProperty(rt, "1", "modified");+  EXPECT_EQ(seven.getProperty(rt, "1").getString(rt).utf8(rt), "modified");+  EXPECT_EQ(arr.getValueAtIndex(rt, 1).getString(rt).utf8(rt), "modified");+  EXPECT_EQ(+      seven.getProperty(rt, PropNameID::forAscii(rt, "0"))+          .getString(rt)+          .utf8(rt),+      "eight");+  seven.setProperty(rt, PropNameID::forAscii(rt, "0"), "modified2");+  EXPECT_EQ(arr.getValueAtIndex(rt, 0).getString(rt).utf8(rt), "modified2");++  Array alpha = Array(rt, 4);+  EXPECT_TRUE(alpha.getValueAtIndex(rt, 0).isUndefined());+  EXPECT_TRUE(alpha.getValueAtIndex(rt, 3).isUndefined());+  EXPECT_EQ(alpha.size(rt), 4);+  alpha.setValueAtIndex(rt, 0, "a");+  alpha.setValueAtIndex(rt, 1, "b");+  EXPECT_EQ(alpha.length(rt), 4);+  alpha.setValueAtIndex(rt, 2, "c");+  alpha.setValueAtIndex(rt, 3, "d");+  EXPECT_EQ(alpha.size(rt), 4);++  EXPECT_TRUE(+      function(+          "function (arr) { return "+          "arr.length == 4 && "+          "['a','b','c','d'].every(function(v,i) { return v === arr[i]}); }")+          .call(rt, alpha)+          .getBool());++  Array alpha2 = Array(rt, 1);+  alpha2 = std::move(alpha);+  EXPECT_EQ(alpha2.size(rt), 4);+}++TEST_P(JSITest, FunctionTest) {+  // test move ctor+  Function fmove = function("function() { return 1 }");+  {+    Function g = function("function() { return 2 }");+    fmove = std::move(g);+  }+  EXPECT_EQ(fmove.call(rt).getNumber(), 2);++  // This tests all the function argument converters, and all the+  // non-lvalue overloads of call().+  Function f = function(+      "function(n, b, d, df, i, s1, s2, s3, s_sun, s_bad, o, a, f, v) { "+      "return "+      "n === null && "+      "b === true && "+      "d === 3.14 && "+      "Math.abs(df - 2.71) < 0.001 && "+      "i === 17 && "+      "s1 == 's1' && "+      "s2 == 's2' && "+      "s3 == 's3' && "+      "s_sun == 's\\u2600' && "+      "typeof s_bad == 'string' && "+      "typeof o == 'object' && "+      "Array.isArray(a) && "+      "typeof f == 'function' && "+      "v == 42 }");+  EXPECT_TRUE(f.call(+                   rt,+                   nullptr,+                   true,+                   3.14,+                   2.71f,+                   17,+                   "s1",+                   String::createFromAscii(rt, "s2"),+                   std::string{"s3"},+                   std::string{u8"s\u2600"},+                   // invalid UTF8 sequence due to unexpected continuation byte+                   std::string{"s\x80"},+                   Object(rt),+                   Array(rt, 1),+                   function("function(){}"),+                   Value(42))+                  .getBool());++  // lvalue overloads of call()+  Function flv = function(+      "function(s, o, a, f, v) { return "+      "s == 's' && "+      "typeof o == 'object' && "+      "Array.isArray(a) && "+      "typeof f == 'function' && "+      "v == 42 }");++  String s = String::createFromAscii(rt, "s");+  Object o = Object(rt);+  Array a = Array(rt, 1);+  Value v = 42;+  EXPECT_TRUE(flv.call(rt, s, o, a, f, v).getBool());++  Function f1 = function("function() { return 1; }");+  Function f2 = function("function() { return 2; }");+  f2 = std::move(f1);+  EXPECT_EQ(f2.call(rt).getNumber(), 1);+}++TEST_P(JSITest, FunctionThisTest) {+  Function checkPropertyFunction =+      function("function() { return this.a === 'a_property' }");++  Object jsObject = Object(rt);+  jsObject.setProperty(rt, "a", String::createFromUtf8(rt, "a_property"));++  class APropertyHostObject : public HostObject {+    Value get(Runtime& rt, const PropNameID& sym) override {+      return String::createFromUtf8(rt, "a_property");+    }++    void set(Runtime&, const PropNameID&, const Value&) override {}+  };+  Object hostObject =+      Object::createFromHostObject(rt, std::make_shared<APropertyHostObject>());++  EXPECT_TRUE(checkPropertyFunction.callWithThis(rt, jsObject).getBool());+  EXPECT_TRUE(checkPropertyFunction.callWithThis(rt, hostObject).getBool());+  EXPECT_FALSE(checkPropertyFunction.callWithThis(rt, Array(rt, 5)).getBool());+  EXPECT_FALSE(checkPropertyFunction.call(rt).getBool());+}++TEST_P(JSITest, FunctionConstructorTest) {+  Function ctor = function(+      "function (a) {"+      "  if (typeof a !== 'undefined') {"+      "   this.pika = a;"+      "  }"+      "}");+  ctor.getProperty(rt, "prototype")+      .getObject(rt)+      .setProperty(rt, "pika", "chu");+  auto empty = ctor.callAsConstructor(rt);+  EXPECT_TRUE(empty.isObject());+  auto emptyObj = std::move(empty).getObject(rt);+  EXPECT_EQ(emptyObj.getProperty(rt, "pika").getString(rt).utf8(rt), "chu");+  auto who = ctor.callAsConstructor(rt, "who");+  EXPECT_TRUE(who.isObject());+  auto whoObj = std::move(who).getObject(rt);+  EXPECT_EQ(whoObj.getProperty(rt, "pika").getString(rt).utf8(rt), "who");++  auto instanceof = function("function (o, b) { return o instanceof b; }");+  EXPECT_TRUE(instanceof.call(rt, emptyObj, ctor).getBool());+  EXPECT_TRUE(instanceof.call(rt, whoObj, ctor).getBool());++  auto dateCtor = rt.global().getPropertyAsFunction(rt, "Date");+  auto date = dateCtor.callAsConstructor(rt);+  EXPECT_TRUE(date.isObject());+  EXPECT_TRUE(instanceof.call(rt, date, dateCtor).getBool());+  // Sleep for 50 milliseconds+  std::this_thread::sleep_for(std::chrono::milliseconds(50));+  EXPECT_GE(+      function("function (d) { return (new Date()).getTime() - d.getTime(); }")+          .call(rt, date)+          .getNumber(),+      50);+}++TEST_P(JSITest, InstanceOfTest) {+  auto ctor = function("function Rick() { this.say = 'wubalubadubdub'; }");+  auto newObj = function("function (ctor) { return new ctor(); }");+  auto instance = newObj.call(rt, ctor).getObject(rt);+  EXPECT_TRUE(instance.instanceOf(rt, ctor));+  EXPECT_EQ(+      instance.getProperty(rt, "say").getString(rt).utf8(rt), "wubalubadubdub");+  EXPECT_FALSE(Object(rt).instanceOf(rt, ctor));+  EXPECT_TRUE(ctor.callAsConstructor(rt, nullptr, 0)+                  .getObject(rt)+                  .instanceOf(rt, ctor));+}++TEST_P(JSITest, HostFunctionTest) {+  auto one = std::make_shared<int>(1);+  Function plusOne = Function::createFromHostFunction(+      rt,+      PropNameID::forAscii(rt, "plusOne"),+      2,+      [one, savedRt = &rt](+          Runtime& rt, const Value& thisVal, const Value* args, size_t count) {+        EXPECT_EQ(savedRt, &rt);+        // We don't know if we're in strict mode or not, so it's either global+        // or undefined.+        EXPECT_TRUE(+            Value::strictEquals(rt, thisVal, rt.global()) ||+            thisVal.isUndefined());+        return *one + args[0].getNumber() + args[1].getNumber();+      });++  EXPECT_EQ(plusOne.call(rt, 1, 2).getNumber(), 4);+  EXPECT_TRUE(checkValue(plusOne.call(rt, 3, 5), "9"));+  rt.global().setProperty(rt, "plusOne", plusOne);+  EXPECT_TRUE(eval("plusOne(20, 300) == 321").getBool());++  Function dot = Function::createFromHostFunction(+      rt,+      PropNameID::forAscii(rt, "dot"),+      2,+      [](Runtime& rt, const Value& thisVal, const Value* args, size_t count) {+        EXPECT_TRUE(+            Value::strictEquals(rt, thisVal, rt.global()) ||+            thisVal.isUndefined());+        if (count != 2) {+          throw std::runtime_error("expected 2 args");+        }+        std::string ret = args[0].getString(rt).utf8(rt) + "." ++            args[1].getString(rt).utf8(rt);+        return String::createFromUtf8(+            rt, reinterpret_cast<const uint8_t*>(ret.data()), ret.size());+      });++  rt.global().setProperty(rt, "cons", dot);+  EXPECT_TRUE(eval("cons('left', 'right') == 'left.right'").getBool());+  EXPECT_TRUE(eval("cons.name == 'dot'").getBool());+  EXPECT_TRUE(eval("cons.length == 2").getBool());+  EXPECT_TRUE(eval("cons instanceof Function").getBool());++  EXPECT_TRUE(eval("(function() {"+                   "  try {"+                   "    cons('fail'); return false;"+                   "  } catch (e) {"+                   "    return ((e instanceof Error) &&"+                   "            (e.message == 'Exception in HostFunction: ' +"+                   "                          'expected 2 args'));"+                   "  }})()")+                  .getBool());++  Function coolify = Function::createFromHostFunction(+      rt,+      PropNameID::forAscii(rt, "coolify"),+      0,+      [](Runtime& rt, const Value& thisVal, const Value* args, size_t count) {+        EXPECT_EQ(count, 0);+        std::string ret = thisVal.toString(rt).utf8(rt) + " is cool";+        return String::createFromUtf8(+            rt, reinterpret_cast<const uint8_t*>(ret.data()), ret.size());+      });+  rt.global().setProperty(rt, "coolify", coolify);+  EXPECT_TRUE(eval("coolify.name == 'coolify'").getBool());+  EXPECT_TRUE(eval("coolify.length == 0").getBool());+  EXPECT_TRUE(eval("coolify.bind('R&M')() == 'R&M is cool'").getBool());+  EXPECT_TRUE(eval("(function() {"+                   "  var s = coolify.bind(function(){})();"+                   "  return s.lastIndexOf(' is cool') == (s.length - 8);"+                   "})()")+                  .getBool());++  Function lookAtMe = Function::createFromHostFunction(+      rt,+      PropNameID::forAscii(rt, "lookAtMe"),+      0,+      [](Runtime& rt, const Value& thisVal, const Value* args, size_t count)+          -> Value {+        EXPECT_TRUE(thisVal.isObject());+        EXPECT_EQ(+            thisVal.getObject(rt)+                .getProperty(rt, "name")+                .getString(rt)+                .utf8(rt),+            "mr.meeseeks");+        return Value();+      });+  rt.global().setProperty(rt, "lookAtMe", lookAtMe);+  EXPECT_TRUE(eval("lookAtMe.bind({'name': 'mr.meeseeks'})()").isUndefined());++  struct Callable {+    Callable(std::string s) : str(s) {}++    Value+    operator()(Runtime& rt, const Value&, const Value* args, size_t count) {+      if (count != 1) {+        return Value();+      }+      return String::createFromUtf8(+          rt, args[0].toString(rt).utf8(rt) + " was called with " + str);+    }++    std::string str;+  };++  Function callable = Function::createFromHostFunction(+      rt,+      PropNameID::forAscii(rt, "callable"),+      1,+      Callable("std::function::target"));+  EXPECT_EQ(+      function("function (f) { return f('A cat'); }")+          .call(rt, callable)+          .getString(rt)+          .utf8(rt),+      "A cat was called with std::function::target");+  EXPECT_TRUE(callable.isHostFunction(rt));+  EXPECT_NE(callable.getHostFunction(rt).target<Callable>(), nullptr);++  std::string strval = "strval1";+  auto getter = Object(rt);+  getter.setProperty(+      rt,+      "get",+      Function::createFromHostFunction(+          rt,+          PropNameID::forAscii(rt, "getter"),+          1,+          [&strval](+              Runtime& rt,+              const Value& thisVal,+              const Value* args,+              size_t count) -> Value {+            return String::createFromUtf8(rt, strval);+          }));+  auto obj = Object(rt);+  rt.global()+      .getPropertyAsObject(rt, "Object")+      .getPropertyAsFunction(rt, "defineProperty")+      .call(rt, obj, "prop", getter);+  EXPECT_TRUE(function("function(value) { return value.prop == 'strval1'; }")+                  .call(rt, obj)+                  .getBool());+  strval = "strval2";+  EXPECT_TRUE(function("function(value) { return value.prop == 'strval2'; }")+                  .call(rt, obj)+                  .getBool());+}++TEST_P(JSITest, ValueTest) {+  EXPECT_TRUE(checkValue(Value::undefined(), "undefined"));+  EXPECT_TRUE(checkValue(Value(), "undefined"));+  EXPECT_TRUE(checkValue(Value::null(), "null"));+  EXPECT_TRUE(checkValue(nullptr, "null"));++  EXPECT_TRUE(checkValue(Value(false), "false"));+  EXPECT_TRUE(checkValue(false, "false"));+  EXPECT_TRUE(checkValue(true, "true"));++  EXPECT_TRUE(checkValue(Value(1.5), "1.5"));+  EXPECT_TRUE(checkValue(2.5, "2.5"));++  EXPECT_TRUE(checkValue(Value(10), "10"));+  EXPECT_TRUE(checkValue(20, "20"));+  EXPECT_TRUE(checkValue(30, "30"));++  // rvalue implicit conversion+  EXPECT_TRUE(checkValue(String::createFromAscii(rt, "one"), "'one'"));+  // lvalue explicit copy+  String s = String::createFromAscii(rt, "two");+  EXPECT_TRUE(checkValue(Value(rt, s), "'two'"));++  {+    // rvalue assignment of trivial value+    Value v1 = 100;+    Value v2 = String::createFromAscii(rt, "hundred");+    v2 = std::move(v1);+    EXPECT_TRUE(v2.isNumber());+    EXPECT_EQ(v2.getNumber(), 100);+  }++  {+    // rvalue assignment of js heap value+    Value v1 = String::createFromAscii(rt, "hundred");+    Value v2 = 100;+    v2 = std::move(v1);+    EXPECT_TRUE(v2.isString());+    EXPECT_EQ(v2.getString(rt).utf8(rt), "hundred");+  }++  Object o = Object(rt);+  EXPECT_TRUE(function("function(value) { return typeof(value) == 'object'; }")+                  .call(rt, Value(rt, o))+                  .getBool());++  uint8_t utf8[] = "[null, 2, \"c\", \"emoji: \xf0\x9f\x86\x97\", {}]";++  EXPECT_TRUE(+      function("function (arr) { return "+               "Array.isArray(arr) && "+               "arr.length == 5 && "+               "arr[0] === null && "+               "arr[1] == 2 && "+               "arr[2] == 'c' && "+               "arr[3] == 'emoji: \\uD83C\\uDD97' && "+               "typeof arr[4] == 'object'; }")+          .call(rt, Value::createFromJsonUtf8(rt, utf8, sizeof(utf8) - 1))+          .getBool());++  EXPECT_TRUE(eval("undefined").isUndefined());+  EXPECT_TRUE(eval("null").isNull());+  EXPECT_TRUE(eval("true").isBool());+  EXPECT_TRUE(eval("false").isBool());+  EXPECT_TRUE(eval("123").isNumber());+  EXPECT_TRUE(eval("123.4").isNumber());+  EXPECT_TRUE(eval("'str'").isString());+  // "{}" returns undefined.  empty code block?+  EXPECT_TRUE(eval("({})").isObject());+  EXPECT_TRUE(eval("[]").isObject());+  EXPECT_TRUE(eval("(function(){})").isObject());++  EXPECT_EQ(eval("123").getNumber(), 123);+  EXPECT_EQ(eval("123.4").getNumber(), 123.4);+  EXPECT_EQ(eval("'str'").getString(rt).utf8(rt), "str");+  EXPECT_TRUE(eval("[]").getObject(rt).isArray(rt));++  EXPECT_TRUE(eval("true").asBool());+  EXPECT_THROW(eval("123").asBool(), JSIException);+  EXPECT_EQ(eval("456").asNumber(), 456);+  EXPECT_THROW(eval("'word'").asNumber(), JSIException);+  EXPECT_EQ(+      eval("({1:2, 3:4})").asObject(rt).getProperty(rt, "1").getNumber(), 2);+  EXPECT_THROW(eval("'oops'").asObject(rt), JSIException);++  EXPECT_EQ(eval("['zero',1,2,3]").toString(rt).utf8(rt), "zero,1,2,3");+}++TEST_P(JSITest, EqualsTest) {+  EXPECT_TRUE(Object::strictEquals(rt, rt.global(), rt.global()));+  EXPECT_TRUE(Value::strictEquals(rt, 1, 1));+  EXPECT_FALSE(Value::strictEquals(rt, true, 1));+  EXPECT_FALSE(Value::strictEquals(rt, true, false));+  EXPECT_TRUE(Value::strictEquals(rt, false, false));+  EXPECT_FALSE(Value::strictEquals(rt, nullptr, 1));+  EXPECT_TRUE(Value::strictEquals(rt, nullptr, nullptr));+  EXPECT_TRUE(Value::strictEquals(rt, Value::undefined(), Value()));+  EXPECT_TRUE(Value::strictEquals(rt, rt.global(), Value(rt.global())));+  EXPECT_FALSE(Value::strictEquals(+      rt,+      std::numeric_limits<double>::quiet_NaN(),+      std::numeric_limits<double>::quiet_NaN()));+  EXPECT_FALSE(Value::strictEquals(+      rt,+      std::numeric_limits<double>::signaling_NaN(),+      std::numeric_limits<double>::signaling_NaN()));+  EXPECT_TRUE(Value::strictEquals(rt, +0.0, -0.0));+  EXPECT_TRUE(Value::strictEquals(rt, -0.0, +0.0));++  Function noop = Function::createFromHostFunction(+      rt,+      PropNameID::forAscii(rt, "noop"),+      0,+      [](const Runtime&, const Value&, const Value*, size_t) {+        return Value();+      });+  auto noopDup = Value(rt, noop).getObject(rt);+  EXPECT_TRUE(Object::strictEquals(rt, noop, noopDup));+  EXPECT_TRUE(Object::strictEquals(rt, noopDup, noop));+  EXPECT_FALSE(Object::strictEquals(rt, noop, rt.global()));+  EXPECT_TRUE(Object::strictEquals(rt, noop, noop));+  EXPECT_TRUE(Value::strictEquals(rt, Value(rt, noop), Value(rt, noop)));++  String str = String::createFromAscii(rt, "rick");+  String strDup = String::createFromAscii(rt, "rick");+  String otherStr = String::createFromAscii(rt, "morty");+  EXPECT_TRUE(String::strictEquals(rt, str, str));+  EXPECT_TRUE(String::strictEquals(rt, str, strDup));+  EXPECT_TRUE(String::strictEquals(rt, strDup, str));+  EXPECT_FALSE(String::strictEquals(rt, str, otherStr));+  EXPECT_TRUE(Value::strictEquals(rt, Value(rt, str), Value(rt, str)));+  EXPECT_FALSE(Value::strictEquals(rt, Value(rt, str), Value(rt, noop)));+  EXPECT_FALSE(Value::strictEquals(rt, Value(rt, str), 1.0));+}++TEST_P(JSITest, ExceptionStackTraceTest) {+  static const char invokeUndefinedScript[] =+      "function hello() {"+      "  var a = {}; a.log(); }"+      "function world() { hello(); }"+      "world()";+  std::string stack;+  try {+    rt.evaluateJavaScript(+        std::make_unique<StringBuffer>(invokeUndefinedScript), "");+  } catch (JSError& e) {+    stack = e.getStack();+  }+  EXPECT_NE(stack.find("world"), std::string::npos);+}++TEST_P(JSITest, PreparedJavaScriptSourceTest) {+  rt.evaluateJavaScript(std::make_unique<StringBuffer>("var q = 0;"), "");+  auto prep = rt.prepareJavaScript(std::make_unique<StringBuffer>("q++;"), "");+  EXPECT_EQ(rt.global().getProperty(rt, "q").getNumber(), 0);+  rt.evaluatePreparedJavaScript(prep);+  EXPECT_EQ(rt.global().getProperty(rt, "q").getNumber(), 1);+  rt.evaluatePreparedJavaScript(prep);+  EXPECT_EQ(rt.global().getProperty(rt, "q").getNumber(), 2);+}++TEST_P(JSITest, PreparedJavaScriptURLInBacktrace) {+  std::string sourceURL = "//PreparedJavaScriptURLInBacktrace/Test/URL";+  std::string throwingSource =+      "function thrower() { throw new Error('oops')}"+      "thrower();";+  auto prep = rt.prepareJavaScript(+      std::make_unique<StringBuffer>(throwingSource), sourceURL);+  try {+    rt.evaluatePreparedJavaScript(prep);+    FAIL() << "prepareJavaScript should have thrown an exception";+  } catch (facebook::jsi::JSError err) {+    EXPECT_NE(std::string::npos, err.getStack().find(sourceURL))+        << "Backtrace should contain source URL";+  }+}++namespace {++unsigned countOccurences(const std::string& of, const std::string& in) {+  unsigned occurences = 0;+  std::string::size_type lastOccurence = -1;+  while ((lastOccurence = in.find(of, lastOccurence + 1)) !=+         std::string::npos) {+    occurences++;+  }+  return occurences;+}++} // namespace++TEST_P(JSITest, JSErrorsArePropagatedNicely) {+  unsigned callsBeforeError = 5;++  Function sometimesThrows = function(+      "function sometimesThrows(shouldThrow, callback) {"+      "  if (shouldThrow) {"+      "    throw Error('Omg, what a nasty exception')"+      "  }"+      "  callback(callback);"+      "}");++  Function callback = Function::createFromHostFunction(+      rt,+      PropNameID::forAscii(rt, "callback"),+      0,+      [&sometimesThrows, &callsBeforeError](+          Runtime& rt, const Value& thisVal, const Value* args, size_t count) {+        return sometimesThrows.call(rt, --callsBeforeError == 0, args[0]);+      });++  try {+    sometimesThrows.call(rt, false, callback);+  } catch (JSError& error) {+    EXPECT_EQ(error.getMessage(), "Omg, what a nasty exception");+    EXPECT_EQ(countOccurences("sometimesThrows", error.getStack()), 6);++    // system JSC JSI does not implement host function names+    // EXPECT_EQ(countOccurences("callback", error.getStack(rt)), 5);+  }+}++TEST_P(JSITest, JSErrorsCanBeConstructedWithStack) {+  auto err = JSError(rt, "message", "stack");+  EXPECT_EQ(err.getMessage(), "message");+  EXPECT_EQ(err.getStack(), "stack");+}++TEST_P(JSITest, JSErrorDoesNotInfinitelyRecurse) {+  Value globalError = rt.global().getProperty(rt, "Error");+  rt.global().setProperty(rt, "Error", Value::undefined());+  try {+    rt.global().getPropertyAsFunction(rt, "NotAFunction");+    FAIL() << "expected exception";+  } catch (const JSError& ex) {+    EXPECT_EQ(+        ex.getMessage(),+        "callGlobalFunction: JS global property 'Error' is undefined, "+        "expected a Function (while raising getPropertyAsObject: "+        "property 'NotAFunction' is undefined, expected an Object)");+  }++  // If Error is missing, this is fundamentally a problem with JS code+  // messing up the global object, so it should present in JS code as+  // a catchable string.  Not an Error (because that's broken), or as+  // a C++ failure.++  auto fails = [](Runtime& rt, const Value&, const Value*, size_t) -> Value {+    return rt.global().getPropertyAsObject(rt, "NotAProperty");+  };+  EXPECT_EQ(+      function("function (f) { try { f(); return 'undefined'; }"+               "catch (e) { return typeof e; } }")+          .call(+              rt,+              Function::createFromHostFunction(+                  rt, PropNameID::forAscii(rt, "fails"), 0, fails))+          .getString(rt)+          .utf8(rt),+      "string");++  rt.global().setProperty(rt, "Error", globalError);+}++TEST_P(JSITest, JSErrorStackOverflowHandling) {+  rt.global().setProperty(+      rt,+      "callSomething",+      Function::createFromHostFunction(+          rt,+          PropNameID::forAscii(rt, "callSomething"),+          0,+          [this](+              Runtime& rt2,+              const Value& thisVal,+              const Value* args,+              size_t count) {+            EXPECT_EQ(&rt, &rt2);+            return function("function() { return 0; }").call(rt);+          }));+  try {+    eval("(function f() { callSomething(); f.apply(); })()");+    FAIL();+  } catch (const JSError& ex) {+    EXPECT_NE(std::string(ex.what()).find("exceeded"), std::string::npos);+  }+}++TEST_P(JSITest, ScopeDoesNotCrashTest) {+  Scope scope(rt);+  Object o(rt);+}++TEST_P(JSITest, ScopeDoesNotCrashWhenValueEscapes) {+  Value v;+  Scope::callInNewScope(rt, [&]() {+    Object o(rt);+    o.setProperty(rt, "a", 5);+    v = std::move(o);+  });+  EXPECT_EQ(v.getObject(rt).getProperty(rt, "a").getNumber(), 5);+}++// Verifies you can have a host object that emulates a normal object+TEST_P(JSITest, HostObjectWithValueMembers) {+  class Bag : public HostObject {+   public:+    Bag() = default;++    const Value& operator[](const std::string& name) const {+      auto iter = data_.find(name);+      if (iter == data_.end()) {+        return undef_;+      }+      return iter->second;+    }++   protected:+    Value get(Runtime& rt, const PropNameID& name) override {+      return Value(rt, (*this)[name.utf8(rt)]);+    }++    void set(Runtime& rt, const PropNameID& name, const Value& val) override {+      data_.emplace(name.utf8(rt), Value(rt, val));+    }++    Value undef_;+    std::map<std::string, Value> data_;+  };++  auto sharedBag = std::make_shared<Bag>();+  auto& bag = *sharedBag;+  Object jsbag = Object::createFromHostObject(rt, std::move(sharedBag));+  auto set = function(+      "function (o) {"+      "  o.foo = 'bar';"+      "  o.count = 37;"+      "  o.nul = null;"+      "  o.iscool = true;"+      "  o.obj = { 'foo': 'bar' };"+      "}");+  set.call(rt, jsbag);+  auto checkFoo = function("function (o) { return o.foo === 'bar'; }");+  auto checkCount = function("function (o) { return o.count === 37; }");+  auto checkNul = function("function (o) { return o.nul === null; }");+  auto checkIsCool = function("function (o) { return o.iscool === true; }");+  auto checkObj = function(+      "function (o) {"+      "  return (typeof o.obj) === 'object' && o.obj.foo === 'bar';"+      "}");+  // Check this looks good from js+  EXPECT_TRUE(checkFoo.call(rt, jsbag).getBool());+  EXPECT_TRUE(checkCount.call(rt, jsbag).getBool());+  EXPECT_TRUE(checkNul.call(rt, jsbag).getBool());+  EXPECT_TRUE(checkIsCool.call(rt, jsbag).getBool());+  EXPECT_TRUE(checkObj.call(rt, jsbag).getBool());++  // Check this looks good from c+++  EXPECT_EQ(bag["foo"].getString(rt).utf8(rt), "bar");+  EXPECT_EQ(bag["count"].getNumber(), 37);+  EXPECT_TRUE(bag["nul"].isNull());+  EXPECT_TRUE(bag["iscool"].getBool());+  EXPECT_EQ(+      bag["obj"].getObject(rt).getProperty(rt, "foo").getString(rt).utf8(rt),+      "bar");+}++TEST_P(JSITest, DecoratorTest) {+  struct Count {+    // init here is just to show that a With type does not need to be+    // default constructible.+    explicit Count(int init) : count(init) {}++    // Test optional before method.++    void after() {+      ++count;+    }++    int count;+  };++  static constexpr int kInit = 17;++  class CountRuntime final : public WithRuntimeDecorator<Count> {+   public:+    explicit CountRuntime(std::unique_ptr<Runtime> rt)+        : WithRuntimeDecorator<Count>(*rt, count_),+          rt_(std::move(rt)),+          count_(kInit) {}++    int count() {+      return count_.count;+    }++   private:+    std::unique_ptr<Runtime> rt_;+    Count count_;+  };++  CountRuntime crt(factory());++  crt.description();+  EXPECT_EQ(crt.count(), kInit + 1);++  crt.global().setProperty(crt, "o", Object(crt));+  EXPECT_EQ(crt.count(), kInit + 6);+}++TEST_P(JSITest, MultiDecoratorTest) {+  struct Inc {+    void before() {+      ++count;+    }++    // Test optional after method.++    int count = 0;+  };++  struct Nest {+    void before() {+      ++nest;+    }++    void after() {+      --nest;+    }++    int nest = 0;+  };++  class MultiRuntime final+      : public WithRuntimeDecorator<std::tuple<Inc, Nest>> {+   public:+    explicit MultiRuntime(std::unique_ptr<Runtime> rt)+        : WithRuntimeDecorator<std::tuple<Inc, Nest>>(*rt, tuple_),+          rt_(std::move(rt)) {}++    int count() {+      return std::get<0>(tuple_).count;+    }+    int nest() {+      return std::get<1>(tuple_).nest;+    }++   private:+    std::unique_ptr<Runtime> rt_;+    std::tuple<Inc, Nest> tuple_;+  };++  MultiRuntime mrt(factory());++  Function expectNestOne = Function::createFromHostFunction(+      mrt,+      PropNameID::forAscii(mrt, "expectNestOne"),+      0,+      [](Runtime& rt, const Value& thisVal, const Value* args, size_t count) {+        MultiRuntime* funcmrt = dynamic_cast<MultiRuntime*>(&rt);+        EXPECT_NE(funcmrt, nullptr);+        EXPECT_EQ(funcmrt->count(), 3);+        EXPECT_EQ(funcmrt->nest(), 1);+        return Value::undefined();+      });++  expectNestOne.call(mrt);++  EXPECT_EQ(mrt.count(), 3);+  EXPECT_EQ(mrt.nest(), 0);+}++TEST_P(JSITest, SymbolTest) {+  if (!rt.global().hasProperty(rt, "Symbol")) {+    // Symbol is an es6 feature which doesn't exist in older VMs.  So+    // the tests which might be elsewhere are all here so they can be+    // skipped.+    return;+  }++  // ObjectTest+  eval("x = {1:2, 'three':Symbol('four')}");+  Object x = rt.global().getPropertyAsObject(rt, "x");+  EXPECT_EQ(x.getPropertyNames(rt).size(rt), 2);+  EXPECT_TRUE(x.hasProperty(rt, "three"));+  EXPECT_EQ(+      x.getProperty(rt, "three").getSymbol(rt).toString(rt), "Symbol(four)");++  // ValueTest+  EXPECT_TRUE(eval("Symbol('sym')").isSymbol());+  EXPECT_EQ(eval("Symbol('sym')").getSymbol(rt).toString(rt), "Symbol(sym)");++  // EqualsTest+  EXPECT_FALSE(Symbol::strictEquals(+      rt,+      eval("Symbol('a')").getSymbol(rt),+      eval("Symbol('a')").getSymbol(rt)));+  EXPECT_TRUE(Symbol::strictEquals(+      rt,+      eval("Symbol.for('a')").getSymbol(rt),+      eval("Symbol.for('a')").getSymbol(rt)));+  EXPECT_FALSE(+      Value::strictEquals(rt, eval("Symbol('a')"), eval("Symbol('a')")));+  EXPECT_TRUE(Value::strictEquals(+      rt, eval("Symbol.for('a')"), eval("Symbol.for('a')")));+  EXPECT_FALSE(Value::strictEquals(rt, eval("Symbol('a')"), eval("'a'")));+}++TEST_P(JSITest, JSErrorTest) {+  // JSError creation can lead to further errors.  Make sure these+  // cases are handled and don't cause weird crashes or other issues.+  //+  // Getting message property can throw++  EXPECT_THROW(+      eval("var GetMessageThrows = {get message() { throw Error('ex'); }};"+           "throw GetMessageThrows;"),+      JSIException);++  EXPECT_THROW(+      eval("var GetMessageThrows = {get message() { throw GetMessageThrows; }};"+           "throw GetMessageThrows;"),+      JSIException);++  // Converting exception message to String can throw++  EXPECT_THROW(+      eval(+          "Object.defineProperty("+          "  globalThis, 'String', {configurable:true, get() { var e = Error(); e.message = 23; throw e; }});"+          "var e = Error();"+          "e.message = 17;"+          "throw e;"),+      JSIException);++  EXPECT_THROW(+      eval(+          "var e = Error();"+          "Object.defineProperty("+          "  e, 'message', {configurable:true, get() { throw Error('getter'); }});"+          "throw e;"),+      JSIException);++  EXPECT_THROW(+      eval("var e = Error();"+           "String = function() { throw Error('ctor'); };"+           "throw e;"),+      JSIException);++  // Converting an exception message to String can return a non-String++  EXPECT_THROW(+      eval("String = function() { return 42; };"+           "var e = Error();"+           "e.message = 17;"+           "throw e;"),+      JSIException);++  // Exception can be non-Object++  EXPECT_THROW(eval("throw 17;"), JSIException);++  EXPECT_THROW(eval("throw undefined;"), JSIException);++  // Converting exception with no message or stack property to String can throw++  EXPECT_THROW(+      eval("var e = {toString() { throw new Error('errstr'); }};"+           "throw e;"),+      JSIException);+}++//----------------------------------------------------------------------+// Test that multiple levels of delegation in DecoratedHostObjects works.++class RD1 : public RuntimeDecorator<Runtime, Runtime> {+ public:+  RD1(Runtime& plain) : RuntimeDecorator(plain) {}++  Object createObject(std::shared_ptr<HostObject> ho) {+    class DHO1 : public DecoratedHostObject {+     public:+      using DecoratedHostObject::DecoratedHostObject;++      Value get(Runtime& rt, const PropNameID& name) override {+        numGets++;+        return DecoratedHostObject::get(rt, name);+      }+    };+    return Object::createFromHostObject(+        plain(), std::make_shared<DHO1>(*this, ho));+  }++  static unsigned numGets;+};++class RD2 : public RuntimeDecorator<Runtime, Runtime> {+ public:+  RD2(Runtime& plain) : RuntimeDecorator(plain) {}++  Object createObject(std::shared_ptr<HostObject> ho) {+    class DHO2 : public DecoratedHostObject {+     public:+      using DecoratedHostObject::DecoratedHostObject;++      Value get(Runtime& rt, const PropNameID& name) override {+        numGets++;+        return DecoratedHostObject::get(rt, name);+      }+    };+    return Object::createFromHostObject(+        plain(), std::make_shared<DHO2>(*this, ho));+  }++  static unsigned numGets;+};++class HO : public HostObject {+ public:+  explicit HO(Runtime* expectedRT) : expectedRT_(expectedRT) {}++  Value get(Runtime& rt, const PropNameID& name) override {+    EXPECT_EQ(expectedRT_, &rt);+    return Value(17.0);+  }++ private:+  // The runtime we expect to be called with.+  Runtime* expectedRT_;+};++unsigned RD1::numGets = 0;+unsigned RD2::numGets = 0;++TEST_P(JSITest, MultilevelDecoratedHostObject) {+  // This test will be run for various test instantiations, so initialize these+  // counters.+  RD1::numGets = 0;+  RD2::numGets = 0;++  RD1 rd1(rt);+  RD2 rd2(rd1);+  // We expect the "get" operation of ho to be called with rd2.+  auto ho = std::make_shared<HO>(&rd2);+  auto obj = Object::createFromHostObject(rd2, ho);+  Value v = obj.getProperty(rd2, "p");+  EXPECT_TRUE(v.isNumber());+  EXPECT_EQ(17.0, v.asNumber());+  auto ho2 = obj.getHostObject(rd2);+  EXPECT_EQ(ho, ho2);+  EXPECT_EQ(1, RD1::numGets);+  EXPECT_EQ(1, RD2::numGets);+}++INSTANTIATE_TEST_SUITE_P(+    Runtimes,+    JSITest,+    ::testing::ValuesIn(runtimeGenerators()));

jsi/test/testlib.h

new file mode 100644@@ -0,0 +1,49 @@+/*+ * Copyright (c) Meta Platforms, Inc. and affiliates.+ *+ * This source code is licensed under the MIT license found in the+ * LICENSE file in the root directory of this source tree.+ */++#pragma once++#include <functional>+#include <memory>+#include <vector>++#include <gtest/gtest.h>+#include <jsi/jsi.h>++namespace facebook {+namespace jsi {++class Runtime;++using RuntimeFactory = std::function<std::unique_ptr<Runtime>()>;++std::vector<RuntimeFactory> runtimeGenerators();++class JSITestBase : public ::testing::TestWithParam<RuntimeFactory> {+ public:+  JSITestBase() : factory(GetParam()), runtime(factory()), rt(*runtime) {}++  Value eval(const char* code) {+    return rt.global().getPropertyAsFunction(rt, "eval").call(rt, code);+  }++  Function function(const std::string& code) {+    return eval(("(" + code + ")").c_str()).getObject(rt).getFunction(rt);+  }++  bool checkValue(const Value& value, const std::string& jsValue) {+    return function("function(value) { return value == " + jsValue + "; }")+        .call(rt, std::move(value))+        .getBool();+  }++  RuntimeFactory factory;+  std::unique_ptr<Runtime> runtime;+  Runtime& rt;+};+} // namespace jsi+} // namespace facebook

jsi/test/testlib_ext.cpp

new file mode 100644@@ -0,0 +1,453 @@+/*+ * Copyright (c) Meta Platforms, Inc. and affiliates.+ *+ * This source code is licensed under the MIT license found in the+ * LICENSE file in the root directory of this source tree.+ */++// These tests are adopted from the Hermes API tests++#include <jsi/test/testlib.h>++#include <gtest/gtest.h>+#include <jsi/decorator.h>+#include <jsi/jsi.h>++#include <array>+#include <chrono>+#include <cstdlib>+#include <functional>+#include <thread>+#include <unordered_map>+#include <unordered_set>++using namespace facebook::jsi;++class JSITestExt : public JSITestBase {};++TEST_P(JSITestExt, StrictHostFunctionBindTest) {+  Function coolify = Function::createFromHostFunction(+      rt,+      PropNameID::forAscii(rt, "coolify"),+      0,+      [](Runtime&, const Value& thisVal, const Value* args, size_t count) {+        EXPECT_TRUE(thisVal.isUndefined());+        return thisVal.isUndefined();+      });+  rt.global().setProperty(rt, "coolify", coolify);+  EXPECT_TRUE(eval("(function() {"+                   "  \"use strict\";"+                   "  return coolify.bind(undefined)();"+                   "})()")+                  .getBool());+}++TEST_P(JSITestExt, DescriptionTest) {+  // Description is not empty+  EXPECT_NE(rt.description().size(), 0);+}++TEST_P(JSITestExt, ArrayBufferTest) {+  eval(+      "var buffer = new ArrayBuffer(16);\+        var int32View = new Int32Array(buffer);\+        int32View[0] = 1234;\+        int32View[1] = 5678;");++  Object object = rt.global().getPropertyAsObject(rt, "buffer");+  EXPECT_TRUE(object.isArrayBuffer(rt));++  auto arrayBuffer = object.getArrayBuffer(rt);+  EXPECT_EQ(arrayBuffer.size(rt), 16);++  int32_t* buffer = reinterpret_cast<int32_t*>(arrayBuffer.data(rt));+  EXPECT_EQ(buffer[0], 1234);+  EXPECT_EQ(buffer[1], 5678);+}++#if JSI_VERSION >= 9+TEST_P(JSITestExt, ExternalArrayBufferTest) {+  struct FixedBuffer : MutableBuffer {+    size_t size() const override {+      return sizeof(arr);+    }+    uint8_t* data() override {+      return reinterpret_cast<uint8_t*>(arr.data());+    }++    std::array<uint32_t, 256> arr;+  };++  {+    auto buf = std::make_shared<FixedBuffer>();+    for (uint32_t i = 0; i < buf->arr.size(); i++)+      buf->arr[i] = i;+    auto arrayBuffer = ArrayBuffer(rt, buf);+    auto square = eval(+        R"#(+(function (buf) {+  var view = new Uint32Array(buf);+  for(var i = 0; i < view.length; i++) view[i] = view[i] * view[i];+})+)#");+    square.asObject(rt).asFunction(rt).call(rt, arrayBuffer);+    for (uint32_t i = 0; i < 256; i++)+      EXPECT_EQ(buf->arr[i], i * i);+  }+}+#endif++TEST_P(JSITestExt, NoCorruptionOnJSError) {+  // If the test crashes or infinite loops, the likely cause is that+  // Hermes API library is not built with proper compiler flags+  // (-fexception in GCC/CLANG, /EHsc in MSVC)+  try {+    rt.evaluateJavaScript(std::make_unique<StringBuffer>("foo.bar = 1"), "");+    FAIL() << "Expected JSIException";+  } catch (const facebook::jsi::JSIException&) {+    // expected exception, ignore+  }+  try {+    rt.evaluateJavaScript(std::make_unique<StringBuffer>("foo.baz = 1"), "");+    FAIL() << "Expected JSIException";+  } catch (const facebook::jsi::JSIException&) {+    // expected exception, ignore+  }+  rt.evaluateJavaScript(std::make_unique<StringBuffer>("gc()"), "");+}++TEST_P(JSITestExt, SpreadHostObjectWithOwnProperties) {+  class HostObjectWithPropertyNames : public HostObject {+    std::vector<PropNameID> getPropertyNames(Runtime& rt) override {+      return PropNameID::names(rt, "prop1", "1", "2", "prop2", "3");+    }+    Value get(Runtime& runtime, const PropNameID& name) override {+      return Value();+    }+  };++  Object ho = Object::createFromHostObject(+      rt, std::make_shared<HostObjectWithPropertyNames>());+  rt.global().setProperty(rt, "ho", ho);++  auto res = eval(R"###(+var spreaded = {...ho};+var props = Object.getOwnPropertyNames(spreaded);+props.toString();+)###")+                 .getString(rt)+                 .utf8(rt);+  EXPECT_EQ(res, "1,2,3,prop1,prop2");+}++TEST_P(JSITestExt, HostObjectWithOwnProperties) {+  class HostObjectWithPropertyNames : public HostObject {+    std::vector<PropNameID> getPropertyNames(Runtime& rt) override {+      return PropNameID::names(rt, "prop1", "1", "2", "prop2", "3");+    }+    Value get(Runtime& runtime, const PropNameID& name) override {+      if (PropNameID::compare(+              runtime, name, PropNameID::forAscii(runtime, "prop1")))+        return 10;+      return Value();+    }+  };++  Object ho = Object::createFromHostObject(+      rt, std::make_shared<HostObjectWithPropertyNames>());+  rt.global().setProperty(rt, "ho", ho);++  EXPECT_TRUE(eval("\"prop1\" in ho").getBool());+  EXPECT_TRUE(eval("1 in ho").getBool());+  EXPECT_TRUE(eval("2 in ho").getBool());+  EXPECT_TRUE(eval("\"prop2\" in ho").getBool());+  EXPECT_TRUE(eval("3 in ho").getBool());+  // HostObjects say they own any property, even if it's not in their property+  // names list.+  // This is an explicit design choice, to avoid the runtime and API costs of+  // handling checking for property existence.+  EXPECT_TRUE(eval("\"foo\" in ho").getBool());++  EXPECT_TRUE(eval("var properties = Object.getOwnPropertyNames(ho);"+                   "properties[0] === '1' && "+                   "properties[1] === '2' && "+                   "properties[2] === '3' && "+                   "properties[3] === 'prop1' && "+                   "properties[4] === 'prop2' && "+                   "properties.length === 5")+                  .getBool());+  EXPECT_TRUE(eval("ho[2] === undefined").getBool());+  EXPECT_TRUE(eval("ho.prop2 === undefined").getBool());++  eval("Object.defineProperty(ho, '0', {value: 'hi there'})");+  eval("Object.defineProperty(ho, '2', {value: 'hi there'})");+  eval("Object.defineProperty(ho, '4', {value: 'hi there'})");+  eval("Object.defineProperty(ho, 'prop2', {value: 'hi there'})");++  EXPECT_TRUE(eval("var properties = Object.getOwnPropertyNames(ho);"+                   "properties[0] === '0' && "+                   "properties[1] === '1' && "+                   "properties[2] === '2' && "+                   "properties[3] === '3' && "+                   "properties[4] === '4' && "+                   "properties[5] === 'prop2' && "+                   "properties[6] === 'prop1' && "+                   "properties.length === 7")+                  .getBool());+  EXPECT_TRUE(eval("ho[2] === 'hi there'").getBool());+  EXPECT_TRUE(eval("ho.prop2 === 'hi there'").getBool());++  // hasOwnProperty() always succeeds on HostObject+  EXPECT_TRUE(+      eval("Object.prototype.hasOwnProperty.call(ho, 'prop1')").getBool());+  EXPECT_TRUE(+      eval("Object.prototype.hasOwnProperty.call(ho, 'any-string')").getBool());++  // getOwnPropertyDescriptor() always succeeds on HostObject+  EXPECT_TRUE(eval("var d = Object.getOwnPropertyDescriptor(ho, 'prop1');"+                   "d != undefined && "+                   "d.value == 10 && "+                   "d.enumerable && "+                   "d.writable ")+                  .getBool());+  EXPECT_TRUE(eval("var d = Object.getOwnPropertyDescriptor(ho, 'any-string');"+                   "d != undefined && "+                   "d.value == undefined && "+                   "d.enumerable && "+                   "d.writable")+                  .getBool());+}++TEST_P(JSITestExt, HostObjectAsParentTest) {+  class HostObjectWithProp : public HostObject {+    Value get(Runtime& runtime, const PropNameID& name) override {+      if (PropNameID::compare(+              runtime, name, PropNameID::forAscii(runtime, "prop1")))+        return 10;+      return Value();+    }+  };++  Object ho =+      Object::createFromHostObject(rt, std::make_shared<HostObjectWithProp>());+  rt.global().setProperty(rt, "ho", ho);++  EXPECT_TRUE(+      eval("var subClass = {__proto__: ho}; subClass.prop1 == 10;").getBool());+}++#if JSI_VERSION >= 5+TEST_P(JSITestExt, PropNameIDFromSymbol) {+  auto strProp = PropNameID::forAscii(rt, "a");+  auto secretProp = PropNameID::forSymbol(+      rt, eval("var secret = Symbol('a'); secret;").getSymbol(rt));+  auto globalProp =+      PropNameID::forSymbol(rt, eval("Symbol.for('a');").getSymbol(rt));+  auto x =+      eval("({a : 'str', [secret] : 'secret', [Symbol.for('a')] : 'global'});")+          .getObject(rt);++  EXPECT_EQ(x.getProperty(rt, strProp).getString(rt).utf8(rt), "str");+  EXPECT_EQ(x.getProperty(rt, secretProp).getString(rt).utf8(rt), "secret");+  EXPECT_EQ(x.getProperty(rt, globalProp).getString(rt).utf8(rt), "global");+}+#endif++TEST_P(JSITestExt, HasComputedTest) {+  // The only use of JSObject::hasComputed() is in HermesRuntimeImpl,+  // so we test its Proxy support here, instead of from JS.++  EXPECT_FALSE(eval("'prop' in new Proxy({}, {})").getBool());+  EXPECT_TRUE(eval("'prop' in new Proxy({prop:1}, {})").getBool());+  EXPECT_FALSE(+      eval("'prop' in new Proxy({}, {has() { return false; }})").getBool());+  EXPECT_TRUE(+      eval("'prop' in new Proxy({}, {has() { return true; }})").getBool());++  // While we're here, test that a HostFunction can be used as a proxy+  // trap.  This could be very powerful in the right hands.+  Function returnTrue = Function::createFromHostFunction(+      rt,+      PropNameID::forAscii(rt, "returnTrue"),+      0,+      [](Runtime& rt, const Value&, const Value* args, size_t count) {+        EXPECT_EQ(count, 2);+        EXPECT_EQ(args[1].toString(rt).utf8(rt), "prop");+        return true;+      });+  rt.global().setProperty(rt, "returnTrue", returnTrue);+  EXPECT_TRUE(eval("'prop' in new Proxy({}, {has: returnTrue})").getBool());+}++TEST_P(JSITestExt, GlobalObjectTest) {+  rt.global().setProperty(rt, "a", 5);+  eval("f = function(b) { return a + b; }");+  eval("gc()");+  EXPECT_EQ(eval("f(10)").getNumber(), 15);+}++#if JSI_VERSION >= 8+TEST_P(JSITestExt, BigIntJSI) {+  Function bigintCtor = rt.global().getPropertyAsFunction(rt, "BigInt");+  auto BigInt = [&](const char* v) { return bigintCtor.call(rt, eval(v)); };++  auto v0 = BigInt("0");+  auto b0 = v0.asBigInt(rt);+  EXPECT_EQ(v0.toString(rt).utf8(rt), "0");+  EXPECT_EQ(b0.toString(rt).utf8(rt), "0");++  auto vffffffffffffffff = BigInt("0xffffffffffffffffn");+  auto bffffffffffffffff = vffffffffffffffff.asBigInt(rt);+  EXPECT_EQ(vffffffffffffffff.toString(rt).utf8(rt), "18446744073709551615");+  EXPECT_EQ(bffffffffffffffff.toString(rt, 16).utf8(rt), "ffffffffffffffff");+  EXPECT_EQ(bffffffffffffffff.toString(rt, 36).utf8(rt), "3w5e11264sgsf");++  auto vNeg1 = BigInt("-1");+  auto bNeg1 = vNeg1.asBigInt(rt);+  EXPECT_EQ(vNeg1.toString(rt).utf8(rt), "-1");+  EXPECT_EQ(bNeg1.toString(rt, 16).utf8(rt), "-1");+  EXPECT_EQ(bNeg1.toString(rt, 36).utf8(rt), "-1");++  EXPECT_TRUE(BigInt::strictEquals(rt, b0, b0));+  EXPECT_TRUE(BigInt::strictEquals(rt, bffffffffffffffff, bffffffffffffffff));+  EXPECT_FALSE(BigInt::strictEquals(rt, bNeg1, bffffffffffffffff));+}++TEST_P(JSITestExt, BigIntJSIFromScalar) {+  Function bigintCtor = rt.global().getPropertyAsFunction(rt, "BigInt");+  auto BigInt = [&](const char* v) {+    return bigintCtor.call(rt, eval(v)).asBigInt(rt);+  };++  EXPECT_TRUE(BigInt::strictEquals(rt, BigInt("0"), BigInt::fromUint64(rt, 0)));+  EXPECT_TRUE(BigInt::strictEquals(rt, BigInt("0"), BigInt::fromInt64(rt, 0)));+  EXPECT_TRUE(BigInt::strictEquals(+      rt, BigInt("0xdeadbeef"), BigInt::fromUint64(rt, 0xdeadbeef)));+  EXPECT_TRUE(BigInt::strictEquals(+      rt, BigInt("0xc0ffee"), BigInt::fromInt64(rt, 0xc0ffee)));+  EXPECT_TRUE(BigInt::strictEquals(+      rt, BigInt("0xffffffffffffffffn"), BigInt::fromUint64(rt, ~0ull)));+  EXPECT_TRUE(+      BigInt::strictEquals(rt, BigInt("-1"), BigInt::fromInt64(rt, ~0ull)));+}++TEST_P(JSITestExt, BigIntJSIToString) {+  auto b = BigInt::fromUint64(rt, 1);+  // Test all possible radixes.+  for (int radix = 2; radix <= 36; ++radix) {+    EXPECT_EQ(b.toString(rt, radix).utf8(rt), "1") << radix;+  }++  // Test some invaild radixes.+  EXPECT_THROW(b.toString(rt, -1), JSIException);+  EXPECT_THROW(b.toString(rt, 0), JSIException);+  EXPECT_THROW(b.toString(rt, 1), JSIException);+  EXPECT_THROW(b.toString(rt, 37), JSIException);+  EXPECT_THROW(b.toString(rt, 100), JSIException);++  Function bigintCtor = rt.global().getPropertyAsFunction(rt, "BigInt");+  auto BigInt = [&](int value) {+    return bigintCtor.call(rt, value).asBigInt(rt);+  };++  // Now test that the radix is being passed to the VM.+  for (int radix = 2; radix <= 36; ++radix) {+    EXPECT_EQ(BigInt(radix + 1).toString(rt, radix).utf8(rt), "11") << radix;+    EXPECT_EQ(BigInt(-(radix + 1)).toString(rt, radix).utf8(rt), "-11")+        << radix;+  }+}++TEST_P(JSITestExt, BigIntJSITruncation) {+  auto lossless = [](uint64_t value) { return std::make_tuple(value, true); };+  auto lossy = [](uint64_t value) { return std::make_tuple(value, false); };++  auto toInt64 = [this](const BigInt& b) {+    return std::make_tuple(b.getInt64(rt), b.isInt64(rt));+  };++  auto toUint64 = [this](const BigInt& b) {+    return std::make_tuple(b.getUint64(rt), b.isUint64(rt));+  };++  Function bigintCtor = rt.global().getPropertyAsFunction(rt, "BigInt");+  auto BigInt = [&](const char* v) {+    return bigintCtor.call(rt, eval(v)).asBigInt(rt);+  };++  // 0n can be truncated losslessly to either int64_t and uint64_t+  auto b = BigInt::fromUint64(rt, 0);+  EXPECT_EQ(toUint64(b), lossless(0));+  EXPECT_TRUE(+      BigInt::strictEquals(rt, BigInt::fromUint64(rt, b.getUint64(rt)), b));+  EXPECT_EQ(toInt64(b), lossless(0));+  EXPECT_TRUE(+      BigInt::strictEquals(rt, BigInt::fromInt64(rt, b.getInt64(rt)), b));++  // Creating BigInt from an ~0ull. This value can't be truncated losslessly to+  // int64_t.+  b = BigInt::fromUint64(rt, ~0ull);+  EXPECT_EQ(toUint64(b), lossless(~0ull));+  EXPECT_TRUE(+      BigInt::strictEquals(rt, BigInt::fromUint64(rt, b.getUint64(rt)), b));+  EXPECT_EQ(toInt64(b), lossy(~0ull));++  // Creating BigInt from an -1ull. This value can't be truncated losslessly to+  // int64_t.+  b = BigInt::fromInt64(rt, -1ull);+  EXPECT_EQ(toUint64(b), lossy(-1ull));+  EXPECT_EQ(toInt64(b), lossless(-1ull));+  EXPECT_TRUE(+      BigInt::strictEquals(rt, BigInt::fromInt64(rt, b.getInt64(rt)), b));++  // 0x10000000000000000n can't be truncated to int64_t nor uint64_t.+  b = BigInt("0x10000000000000000n");+  EXPECT_EQ(toUint64(b), lossy(0));+  EXPECT_EQ(toInt64(b), lossy(0));++  // -0x10000000000000000n can't be truncated to int64_t nor uint64_t.+  b = BigInt("-0x10000000000000000n");+  EXPECT_EQ(toUint64(b), lossy(0));+  EXPECT_EQ(toInt64(b), lossy(0));++  // (1n << 65n) - 1n can't be truncated to int64_t nor uint64_t.+  b = BigInt("(1n << 65n) - 1n");+  EXPECT_EQ(toUint64(b), lossy(~0ull));+  EXPECT_EQ(toInt64(b), lossy(~0ull));+}+#endif++TEST_P(JSITestExt, NativeExceptionDoesNotUseGlobalError) {+  Function alwaysThrows = Function::createFromHostFunction(+      rt,+      PropNameID::forAscii(rt, "alwaysThrows"),+      0,+      [](Runtime&, const Value&, const Value*, size_t) -> Value {+        throw std::logic_error(+            "Native std::logic_error C++ exception in Host Function");+      });+  rt.global().setProperty(rt, "alwaysThrows", alwaysThrows);+  rt.global().setProperty(rt, "Error", 10);++  auto test = eval(+                  R"#((function(val) {+                          'use strict';+                          try {+                            alwaysThrows(val);+                          } catch(e) {+                            return 'typeof Error is ' + typeof(Error) + '; ' + e.message;+                          }+                          throw new Error('Unreachable statement');+                       }))#")+                  .getObject(rt)+                  .getFunction(rt);+  EXPECT_EQ(+      "typeof Error is number; Exception in HostFunction: Native "+      "std::logic_error C++ exception in Host Function",+      test.call(rt).getString(rt).utf8(rt));+}++INSTANTIATE_TEST_SUITE_P(+    Runtimes,+    JSITestExt,+    ::testing::ValuesIn(runtimeGenerators()));

jsi/threadsafe.h

new file mode 100644@@ -0,0 +1,79 @@+/*+ * Copyright (c) Meta Platforms, Inc. and affiliates.+ *+ * This source code is licensed under the MIT license found in the+ * LICENSE file in the root directory of this source tree.+ */++#pragma once++#include <mutex>++#include <jsi/decorator.h>+#include <jsi/jsi.h>++namespace facebook {+namespace jsi {++class ThreadSafeRuntime : public Runtime {+ public:+  virtual void lock() const = 0;+  virtual void unlock() const = 0;+  virtual Runtime& getUnsafeRuntime() = 0;+};++namespace detail {++template <typename R, typename L>+struct WithLock {+  L lock;+  WithLock(R& r) : lock(r) {}+  void before() {+    lock.lock();+  }+  void after() {+    lock.unlock();+  }+};++// The actual implementation of a given ThreadSafeRuntime. It's parameterized+// by:+//+// - R: The actual Runtime type that this wraps+// - L: A lock type that has three members:+//   - L(R& r)       // ctor+//   - void lock()+//   - void unlock()+template <typename R, typename L>+class ThreadSafeRuntimeImpl final+    : public WithRuntimeDecorator<WithLock<R, L>, R, ThreadSafeRuntime> {+ public:+  template <typename... Args>+  ThreadSafeRuntimeImpl(Args&&... args)+      : WithRuntimeDecorator<WithLock<R, L>, R, ThreadSafeRuntime>(+            unsafe_,+            lock_),+        unsafe_(std::forward<Args>(args)...),+        lock_(unsafe_) {}++  R& getUnsafeRuntime() override {+    return WithRuntimeDecorator<WithLock<R, L>, R, ThreadSafeRuntime>::plain();+  }++  void lock() const override {+    lock_.before();+  }++  void unlock() const override {+    lock_.after();+  }++ private:+  R unsafe_;+  mutable WithLock<R, L> lock_;+};++} // namespace detail++} // namespace jsi+} // namespace facebook

src/HermesApi.cpp

new file mode 100644@@ -0,0 +1,33 @@+// Copyright (c) Microsoft Corporation.+// Licensed under the MIT License.++#include "HermesApi.h"++namespace Microsoft::NodeApiJsi {++namespace {++struct HermesNames {+#define HERMES_FUNC(func) static constexpr const char func[] = #func;+#include "HermesFunctions.inc"+};++} // namespace++thread_local HermesApi *HermesApi::current_{};++HermesApi::HermesApi(IFuncResolver *funcResolver)+    : NodeApi(funcResolver)+#define HERMES_FUNC(func) \+  , func(&ApiFuncResolver<NodeApi, decltype(::func) *, HermesNames::func, offsetof(HermesApi, func)>::stub)+#include "HermesFunctions.inc"+{+}++HermesApi *HermesApi::fromLib() {+  static LibFuncResolver funcResolver("hermes");+  static HermesApi *libHermesApi = new HermesApi(&funcResolver);+  return libHermesApi;+}++} // namespace Microsoft::NodeApiJsi

src/HermesApi.h

new file mode 100644@@ -0,0 +1,53 @@+// Copyright (c) Microsoft Corporation.+// Licensed under the MIT License.++#pragma once+#ifndef SRC_HERMESAPI_H_+#define SRC_HERMESAPI_H_++#include <napi/hermes_api.h>+#include "NodeApi.h"++namespace Microsoft::NodeApiJsi {++class HermesApi : public NodeApi {+ public:+  HermesApi(IFuncResolver *funcResolver);++  static HermesApi *current() noexcept {+    return current_;+  }++  static void setCurrent(HermesApi *current) noexcept {+    NodeApi::setCurrent(current);+    current_ = current;+  }++  static HermesApi *fromLib();++  class Scope : public NodeApi::Scope {+   public:+    Scope() : Scope(HermesApi::fromLib()) {}++    Scope(HermesApi *hermesApi) : NodeApi::Scope(hermesApi), prevHermesApi_(HermesApi::current_) {+      HermesApi::current_ = hermesApi;+    }++    ~Scope() {+      HermesApi::current_ = prevHermesApi_;+    }++   private:+    HermesApi *prevHermesApi_;+  };++#define HERMES_FUNC(func) decltype(::func) *const func;+#include "HermesFunctions.inc"++ private:+  static thread_local HermesApi *current_;+};++} // namespace Microsoft::NodeApiJsi++#endif // !SRC_HERMESAPI_H_

src/HermesFunctions.inc

new file mode 100644@@ -0,0 +1,34 @@+// Copyright (c) Microsoft Corporation.+// Licensed under the MIT License.++// The Hermes functions sorted alphabetically++#ifndef HERMES_FUNC+#error "HERMES_FUNC must exist before including this file"+#endif++HERMES_FUNC(hermes_create_runtime)+HERMES_FUNC(hermes_delete_runtime)+HERMES_FUNC(hermes_get_node_api_env)+HERMES_FUNC(hermes_dump_crash_data)+HERMES_FUNC(hermes_sampling_profiler_enable)+HERMES_FUNC(hermes_sampling_profiler_disable)+HERMES_FUNC(hermes_sampling_profiler_add)+HERMES_FUNC(hermes_sampling_profiler_remove)+HERMES_FUNC(hermes_sampling_profiler_dump_to_file)+HERMES_FUNC(hermes_create_config)+HERMES_FUNC(hermes_delete_config)+HERMES_FUNC(hermes_config_enable_default_crash_handler)+HERMES_FUNC(hermes_config_enable_debugger)+HERMES_FUNC(hermes_config_set_debugger_runtime_name)+HERMES_FUNC(hermes_config_set_debugger_port)+HERMES_FUNC(hermes_config_set_debugger_break_on_start)+HERMES_FUNC(hermes_config_set_task_runner)+HERMES_FUNC(hermes_config_set_script_cache)+HERMES_FUNC(hermes_set_inspector)+HERMES_FUNC(hermes_create_local_connection)+HERMES_FUNC(hermes_delete_local_connection)+HERMES_FUNC(hermes_local_connection_send_message)+HERMES_FUNC(hermes_local_connection_disconnect)++#undef HERMES_FUNC

src/NodeApi.cpp

new file mode 100644@@ -0,0 +1,89 @@+// Copyright (c) Microsoft Corporation.+// Licensed under the MIT License.++#include "NodeApi.h"++EXTERN_C_START++extern napi_status NAPI_CDECL default_napi_ext_get_description(napi_env env, char *buf, size_t bufsize, size_t *result);++extern napi_status NAPI_CDECL default_napi_ext_drain_microtasks(napi_env env, int32_t max_count_hint, bool *result);++extern napi_status NAPI_CDECL default_napi_ext_is_inspectable(napi_env env, bool *result);++extern napi_status NAPI_CDECL default_napi_ext_create_prepared_script(+    napi_env env,+    uint8_t *script_data,+    size_t script_length,+    napi_finalize finalize_cb,+    void *finalize_hint,+    const char *source_url,+    napi_ext_prepared_script *result);++extern napi_status NAPI_CDECL+default_napi_ext_delete_prepared_script(napi_env env, napi_ext_prepared_script prepared_script);++extern napi_status NAPI_CDECL+default_napi_ext_prepared_script_run(napi_env env, napi_ext_prepared_script prepared_script, napi_value *result);++EXTERN_C_END++namespace Microsoft::NodeApiJsi {++namespace {++struct NodeApiNames {+#define NODE_API_FUNC(func) static constexpr const char func[] = #func;+#define NODE_API_EXT_FUNC NODE_API_FUNC+#define NODE_API_PREPARED_SCRIPT NODE_API_FUNC+#include "NodeApiFunctions.inc"+};++// Prepared script function either should be all loaded or we use all default functions.+void loadPreparedScriptFuncs() {+  NodeApi *current = NodeApi::current();+  bool useDefault = false;+#define NODE_API_PREPARED_SCRIPT(func)                                                                         \+  decltype(func) *loaded_##func = reinterpret_cast<decltype(func) *>(current->getFuncPtr(NodeApiNames::func)); \+  useDefault = useDefault || loaded_##func == nullptr;+#include "NodeApiFunctions.inc"+#define NODE_API_PREPARED_SCRIPT(func) \+  const_cast<decltype(func) *>(current->func) = useDefault ? &default_##func : loaded_##func;+#include "NodeApiFunctions.inc"+}++} // namespace++LibFuncResolver::LibFuncResolver(const char *libName) : libHandle_(LibLoader::loadLib(libName)) {}++FuncPtr LibFuncResolver::getFuncPtr(const char *funcName) {+  return LibLoader::getFuncPtr(libHandle_, funcName);+}++DelayLoadedApi::DelayLoadedApi(IFuncResolver *funcResolver) : funcResolver_(funcResolver) {}++DelayLoadedApi::~DelayLoadedApi() = default;++FuncPtr DelayLoadedApi::getFuncPtr(const char *funcName) {+  return funcResolver_->getFuncPtr(funcName);+}++thread_local NodeApi *NodeApi::current_{};++NodeApi::NodeApi(IFuncResolver *funcResolver)+    : DelayLoadedApi(funcResolver)+#define NODE_API_FUNC(func) \+  , func(&ApiFuncResolver<NodeApi, decltype(::func) *, NodeApiNames::func, offsetof(NodeApi, func)>::stub)+#define NODE_API_EXT_FUNC(func)                                                                                      \+  ,                                                                                                                  \+      func(&ApiFuncResolver<NodeApi, decltype(::func) *, NodeApiNames::func, offsetof(NodeApi, func)>::optionalStub< \+           &default_##func>)+#define NODE_API_PREPARED_SCRIPT(func)                                                                              \+  ,                                                                                                                 \+      func(&ApiFuncResolver<NodeApi, decltype(::func) *, NodeApiNames::func, offsetof(NodeApi, func)>::preloadStub< \+           &loadPreparedScriptFuncs>)+#include "NodeApiFunctions.inc"+{+}++} // namespace Microsoft::NodeApiJsi

src/NodeApi.h

new file mode 100644@@ -0,0 +1,129 @@+// Copyright (c) Microsoft Corporation.+// Licensed under the MIT License.++#pragma once+#ifndef SRC_NODEAPI_H_+#define SRC_NODEAPI_H_++#include <napi/js_native_ext_api.h>++namespace Microsoft::NodeApiJsi {++using LibHandle = struct LibHandle_t *;+using FuncPtr = struct FuncPtr_t *;++class LibLoader {+ public:+  static LibHandle loadLib(const char *libName);+  static FuncPtr getFuncPtr(LibHandle libHandle, const char *funcName);+};++template <typename TApi, typename T, const char *funcName, size_t offset>+struct ApiFuncResolver;++template <typename TApi, typename TResult, const char *funcName, size_t offset, typename... TArgs>+struct ApiFuncResolver<TApi, TResult(NAPI_CDECL *)(TArgs...), funcName, offset> {+  // Stub is a special function that loads the targeting function on the first call,+  // replaces function pointer field with the loaded function, and executes the function.+  // After this all future calls to the loaded function are going to be directly from+  // the field and not use this Stub.+  static TResult NAPI_CDECL stub(TArgs... args) {+    using TFunc = TResult(NAPI_CDECL *)(TArgs...);+    TApi *current = TApi::current();+    TFunc func = reinterpret_cast<TFunc>(current->getFuncPtr(funcName));+    *reinterpret_cast<TFunc *>(reinterpret_cast<char *>(current) + offset) = func;+    return (*func)(args...);+  }++  // Optional stub tries to load target function on the first call and set it+  // as the function pointer. If loading fails, then it uses provided default+  // function implementation. The default function can either do the required+  // work or return a failure.+  template <TResult(NAPI_CDECL *defaultFunc)(TArgs...)>+  static TResult NAPI_CDECL optionalStub(TArgs... args) {+    using TFunc = TResult(NAPI_CDECL *)(TArgs...);+    TApi *current = TApi::current();+    TFunc func = reinterpret_cast<TFunc>(current->getFuncPtr(funcName));+    if (func == nullptr) {+      func = defaultFunc;+    }+    *reinterpret_cast<TFunc *>(reinterpret_cast<char *>(current) + offset) = func;+    return (*func)(args...);+  }++  template <void (*preloadFunc)()>+  static TResult NAPI_CDECL preloadStub(TArgs... args) {+    using TFunc = TResult(NAPI_CDECL *)(TArgs...);+    preloadFunc();+    TApi *current = TApi::current();+    TFunc func = *reinterpret_cast<TFunc *>(reinterpret_cast<char *>(current) + offset);+    return (*func)(args...);+  }+};++struct IFuncResolver {+  virtual FuncPtr getFuncPtr(const char *funcName) = 0;+};++class LibFuncResolver : public IFuncResolver {+ public:+  LibFuncResolver(const char *libName);+  FuncPtr getFuncPtr(const char *funcName) override;++ private:+  LibHandle libHandle_;+};++class DelayLoadedApi {+ public:+  DelayLoadedApi(IFuncResolver *funcResolver);+  ~DelayLoadedApi();++  FuncPtr getFuncPtr(const char *funcName);++  DelayLoadedApi(const DelayLoadedApi &) = delete;+  DelayLoadedApi &operator=(const DelayLoadedApi &) = delete;++ private:+  IFuncResolver *funcResolver_;+};++class NodeApi : public DelayLoadedApi {+ public:+  NodeApi(IFuncResolver *funcResolver);++  static NodeApi *current() {+    return current_;+  }++  static void setCurrent(NodeApi *current) {+    current_ = current;+  }++  class Scope {+   public:+    Scope(NodeApi *nodeApi) : prevNodeApi_(NodeApi::current_) {+      NodeApi::current_ = nodeApi;+    }++    ~Scope() {+      NodeApi::current_ = prevNodeApi_;+    }++   private:+    NodeApi *prevNodeApi_;+  };++ public:+#define NODE_API_FUNC(func) decltype(::func) *const func;+#define NODE_API_EXT_FUNC NODE_API_FUNC+#define NODE_API_PREPARED_SCRIPT NODE_API_FUNC+#include "NodeApiFunctions.inc"++ private:+  static thread_local NodeApi *current_;+};++} // namespace Microsoft::NodeApiJsi++#endif // !SRC_NODEAPI_H_

src/NodeApiFunctions.inc

new file mode 100644@@ -0,0 +1,145 @@+// Copyright (c) Microsoft Corporation.+// Licensed under the MIT License.++#ifndef NODE_API_FUNC+#define NODE_API_FUNC(func) +#endif++#ifndef NODE_API_EXT_FUNC+#define NODE_API_EXT_FUNC(func) +#endif++#ifndef NODE_API_PREPARED_SCRIPT+#define NODE_API_PREPARED_SCRIPT(func) +#endif++// The Node-API functions sorted alphabetically.+NODE_API_FUNC(napi_add_finalizer)+NODE_API_FUNC(napi_adjust_external_memory)+NODE_API_FUNC(napi_call_function)+NODE_API_FUNC(napi_check_object_type_tag)+NODE_API_FUNC(napi_close_escapable_handle_scope)+NODE_API_FUNC(napi_close_handle_scope)+NODE_API_FUNC(napi_coerce_to_bool)+NODE_API_FUNC(napi_coerce_to_number)+NODE_API_FUNC(napi_coerce_to_object)+NODE_API_FUNC(napi_coerce_to_string)+NODE_API_FUNC(napi_create_array_with_length)+NODE_API_FUNC(napi_create_array)+NODE_API_FUNC(napi_create_arraybuffer)+NODE_API_FUNC(napi_create_bigint_int64)+NODE_API_FUNC(napi_create_bigint_uint64)+NODE_API_FUNC(napi_create_bigint_words)+NODE_API_FUNC(napi_create_dataview)+NODE_API_FUNC(napi_create_date)+NODE_API_FUNC(napi_create_double)+NODE_API_FUNC(napi_create_error)+NODE_API_FUNC(napi_create_external_arraybuffer)+NODE_API_FUNC(napi_create_external)+NODE_API_FUNC(napi_create_function)+NODE_API_FUNC(napi_create_int32)+NODE_API_FUNC(napi_create_int64)+NODE_API_FUNC(napi_create_object)+NODE_API_FUNC(napi_create_promise)+NODE_API_FUNC(napi_create_range_error)+NODE_API_FUNC(napi_create_reference)+NODE_API_FUNC(napi_create_string_latin1)+NODE_API_FUNC(napi_create_string_utf16)+NODE_API_FUNC(napi_create_string_utf8)+NODE_API_FUNC(napi_create_symbol)+NODE_API_FUNC(napi_create_type_error)+NODE_API_FUNC(napi_create_typedarray)+NODE_API_FUNC(napi_create_uint32)+NODE_API_FUNC(napi_define_class)+NODE_API_FUNC(napi_define_properties)+NODE_API_FUNC(napi_delete_element)+NODE_API_FUNC(napi_delete_property)+NODE_API_FUNC(napi_delete_reference)+NODE_API_FUNC(napi_detach_arraybuffer)+NODE_API_FUNC(napi_escape_handle)+NODE_API_FUNC(napi_get_all_property_names)+NODE_API_FUNC(napi_get_and_clear_last_exception)+NODE_API_FUNC(napi_get_array_length)+NODE_API_FUNC(napi_get_arraybuffer_info)+NODE_API_FUNC(napi_get_boolean)+NODE_API_FUNC(napi_get_cb_info)+NODE_API_FUNC(napi_get_dataview_info)+NODE_API_FUNC(napi_get_date_value)+NODE_API_FUNC(napi_get_element)+NODE_API_FUNC(napi_get_global)+NODE_API_FUNC(napi_get_instance_data)+NODE_API_FUNC(napi_get_last_error_info)+NODE_API_FUNC(napi_get_named_property)+NODE_API_FUNC(napi_get_new_target)+NODE_API_FUNC(napi_get_null)+NODE_API_FUNC(napi_get_property_names)+NODE_API_FUNC(napi_get_property)+NODE_API_FUNC(napi_get_prototype)+NODE_API_FUNC(napi_get_reference_value)+NODE_API_FUNC(napi_get_typedarray_info)+NODE_API_FUNC(napi_get_undefined)+NODE_API_FUNC(napi_get_value_bigint_int64)+NODE_API_FUNC(napi_get_value_bigint_uint64)+NODE_API_FUNC(napi_get_value_bigint_words)+NODE_API_FUNC(napi_get_value_bool)+NODE_API_FUNC(napi_get_value_double)+NODE_API_FUNC(napi_get_value_external)+NODE_API_FUNC(napi_get_value_int32)+NODE_API_FUNC(napi_get_value_int64)+NODE_API_FUNC(napi_get_value_string_latin1)+NODE_API_FUNC(napi_get_value_string_utf16)+NODE_API_FUNC(napi_get_value_string_utf8)+NODE_API_FUNC(napi_get_value_uint32)+NODE_API_FUNC(napi_get_version)+NODE_API_FUNC(napi_has_element)+NODE_API_FUNC(napi_has_named_property)+NODE_API_FUNC(napi_has_own_property)+NODE_API_FUNC(napi_has_property)+NODE_API_FUNC(napi_instanceof)+NODE_API_FUNC(napi_is_array)+NODE_API_FUNC(napi_is_arraybuffer)+NODE_API_FUNC(napi_is_dataview)+NODE_API_FUNC(napi_is_date)+NODE_API_FUNC(napi_is_detached_arraybuffer)+NODE_API_FUNC(napi_is_error)+NODE_API_FUNC(napi_is_exception_pending)+NODE_API_FUNC(napi_is_promise)+NODE_API_FUNC(napi_is_typedarray)+NODE_API_FUNC(napi_new_instance)+NODE_API_FUNC(napi_object_freeze)+NODE_API_FUNC(napi_object_seal)+NODE_API_FUNC(napi_open_escapable_handle_scope)+NODE_API_FUNC(napi_open_handle_scope)+NODE_API_FUNC(napi_reference_ref)+NODE_API_FUNC(napi_reference_unref)+NODE_API_FUNC(napi_reject_deferred)+NODE_API_FUNC(napi_remove_wrap)+NODE_API_FUNC(napi_resolve_deferred)+NODE_API_FUNC(napi_run_script)+NODE_API_FUNC(napi_set_element)+NODE_API_FUNC(napi_set_instance_data)+NODE_API_FUNC(napi_set_named_property)+NODE_API_FUNC(napi_set_property)+NODE_API_FUNC(napi_strict_equals)+NODE_API_FUNC(napi_throw_error)+NODE_API_FUNC(napi_throw_range_error)+NODE_API_FUNC(napi_throw_type_error)+NODE_API_FUNC(napi_throw)+NODE_API_FUNC(napi_type_tag_object)+NODE_API_FUNC(napi_typeof)+NODE_API_FUNC(napi_unwrap)+NODE_API_FUNC(napi_wrap)++// The Node-API extensions functions sorted alphabetically.+NODE_API_EXT_FUNC(napi_ext_drain_microtasks)+NODE_API_EXT_FUNC(napi_ext_get_description)+NODE_API_EXT_FUNC(napi_ext_is_inspectable)++// The Node-API extensions functions for prepared script.+NODE_API_PREPARED_SCRIPT(napi_ext_create_prepared_script)+NODE_API_PREPARED_SCRIPT(napi_ext_delete_prepared_script)+NODE_API_PREPARED_SCRIPT(napi_ext_prepared_script_run)++#undef NODE_API_FUNC+#undef NODE_API_EXT_FUNC+#undef NODE_API_PREPARED_SCRIPT

src/NodeApiJsiRuntime.cpp

new file mode 100644@@ -0,0 +1,2751 @@+// Copyright (c) Microsoft Corporation.+// Licensed under the MIT License.++#define NAPI_EXPERIMENTAL++#include "NodeApiJsiRuntime.h"++#include <algorithm>+#include <array>+#include <optional>+#include <sstream>+#include <string_view>+#include <unordered_map>+#include <unordered_set>++using namespace facebook;+using namespace std::string_view_literals;++// We use macros to report errors.+// Macros provide more flexibility to show assert and provide failure context.++// Check condition and crash process if it fails.+#define CHECK_ELSE_CRASH(condition, message)               \+  do {                                                     \+    if (!(condition)) {                                    \+      assert(false && "Failed: " #condition && (message)); \+      *((int *)0) = 1;                                     \+    }                                                      \+  } while (false)++// Check condition and throw native exception if it fails.+#define CHECK_ELSE_THROW(condition, message) \+  do {                                       \+    if (!(condition)) {                      \+      throwNativeException(message);         \+    }                                        \+  } while (false)++// Check NAPI result and and throw JS exception if it is not napi_ok.+#define CHECK_NAPI(...)                             \+  do {                                              \+    napi_status temp_error_code_ = (__VA_ARGS__);   \+    if (temp_error_code_ != napi_status::napi_ok) { \+      runtime.throwJSException(temp_error_code_);   \+    }                                               \+  } while (false)++// Check NAPI result and and crash if it is not napi_ok.+#define CHECK_NAPI_ELSE_CRASH(expression)              \+  do {                                                 \+    napi_status temp_error_code_ = (expression);       \+    if (temp_error_code_ != napi_status::napi_ok) {    \+      CHECK_ELSE_CRASH(false, "Failed: " #expression); \+    }                                                  \+  } while (false)++// Check NAPI result and return it when it is an error.+#define NAPI_CALL(expression)                       \+  do {                                              \+    napi_status temp_error_code_ = (expression);    \+    if (temp_error_code_ != napi_status::napi_ok) { \+      return temp_error_code_;                      \+    }                                               \+  } while (false)++#ifdef __cpp_lib_span+#include <span>+#endif // __cpp_lib_span++namespace Microsoft::NodeApiJsi {++namespace {++#ifdef __cpp_lib_span+using std::span;+#else+/**+ * @brief A span of values that can be used to pass arguments to a function.+ *+ * This should be replaced with std::span once C++ 2020 is supported.+ */+template <typename T>+class span {+ public:+  constexpr span(std::initializer_list<T> il) noexcept : data_{const_cast<T *>(il.begin())}, size_{il.size()} {}+  constexpr span(T *data, size_t size) noexcept : data_{data}, size_{size} {}++  [[nodiscard]] constexpr T *data() const noexcept {+    return data_;+  }++  [[nodiscard]] constexpr size_t size() const noexcept {+    return size_;+  }++  [[nodiscard]] constexpr T *begin() const noexcept {+    return data_;+  }++  [[nodiscard]] constexpr T *end() const noexcept {+    return *(data_ + size_);+  }++  const T &operator[](size_t index) const noexcept {+    return *(data_ + index);+  }++ private:+  T *data_;+  size_t size_;+};+#endif // __cpp_lib_span++// To be used as a key in a unordered_map.+class StringKey {+ public:+  explicit StringKey(std::string &&string) noexcept;+  explicit StringKey(std::string_view view) noexcept;+  explicit StringKey(const char *data, size_t length) noexcept;+  StringKey(StringKey &&other) noexcept;+  StringKey &operator=(StringKey &&other) noexcept;+  StringKey(const StringKey &other) = delete;+  StringKey &operator=(const StringKey &other) = delete;+  ~StringKey();++  std::string_view getStringView() const;+  bool equalTo(const StringKey &other) const;+  size_t hash() const;++  struct Hash {+    size_t operator()(const StringKey &key) const;+  };++  struct EqualTo {+    bool operator()(const StringKey &left, const StringKey &right) const;+  };++ private:+  enum class Type {+    String,+    View,+  };++ private:+  union {+    std::string string_;+    std::string_view view_;+  };+  Type type_{Type::String};+  size_t hash_;+};++// Implementation of N-API JSI Runtime+class NodeApiJsiRuntime : public jsi::Runtime {+ public:+  NodeApiJsiRuntime(napi_env env, NodeApi *nodeApi, std::function<void()> onDelete) noexcept;+  ~NodeApiJsiRuntime() override;++  jsi::Value evaluateJavaScript(const std::shared_ptr<const jsi::Buffer> &buffer, const std::string &sourceURL)+      override;+  std::shared_ptr<const jsi::PreparedJavaScript> prepareJavaScript(+      const std::shared_ptr<const jsi::Buffer> &buffer,+      std::string sourceURL) override;+  jsi::Value evaluatePreparedJavaScript(const std::shared_ptr<const jsi::PreparedJavaScript> &js) override;+  bool drainMicrotasks(int maxMicrotasksHint = -1) override;+  jsi::Object global() override;+  std::string description() override;+  bool isInspectable() override;++ protected:+  PointerValue *cloneSymbol(const PointerValue *pointerValue) override;+  PointerValue *cloneBigInt(const PointerValue *pointerValue) override;+  PointerValue *cloneString(const PointerValue *pointerValue) override;+  PointerValue *cloneObject(const PointerValue *pointerValue) override;+  PointerValue *clonePropNameID(const PointerValue *pointerValue) override;++  jsi::PropNameID createPropNameIDFromAscii(const char *str, size_t length) override;+  jsi::PropNameID createPropNameIDFromUtf8(const uint8_t *utf8, size_t length) override;+  jsi::PropNameID createPropNameIDFromString(const jsi::String &str) override;+  jsi::PropNameID createPropNameIDFromSymbol(const jsi::Symbol &sym) override;+  std::string utf8(const jsi::PropNameID &id) override;+  bool compare(const jsi::PropNameID &lhs, const jsi::PropNameID &rhs) override;++  std::string symbolToString(const jsi::Symbol &s) override;++  jsi::BigInt createBigIntFromInt64(int64_t value) override;+  jsi::BigInt createBigIntFromUint64(uint64_t value) override;+  bool bigintIsInt64(const jsi::BigInt &value) override;+  bool bigintIsUint64(const jsi::BigInt &value) override;+  uint64_t truncate(const jsi::BigInt &value) override;+  jsi::String bigintToString(const jsi::BigInt &value, int radix) override;++  jsi::String createStringFromAscii(const char *str, size_t length) override;+  jsi::String createStringFromUtf8(const uint8_t *utf8, size_t length) override;+  std::string utf8(const jsi::String &str) override;++  jsi::Object createObject() override;+  jsi::Object createObject(std::shared_ptr<jsi::HostObject> ho) override;+  std::shared_ptr<jsi::HostObject> getHostObject(const jsi::Object &) override;+  jsi::HostFunctionType &getHostFunction(const jsi::Function &) override;++  bool hasNativeState(const jsi::Object &value) override;+  std::shared_ptr<jsi::NativeState> getNativeState(const jsi::Object &value) override;+  void setNativeState(const jsi::Object &value, std::shared_ptr<jsi::NativeState> state) override;++  jsi::Value getProperty(const jsi::Object &obj, const jsi::PropNameID &name) override;+  jsi::Value getProperty(const jsi::Object &obj, const jsi::String &name) override;+  bool hasProperty(const jsi::Object &obj, const jsi::PropNameID &name) override;+  bool hasProperty(const jsi::Object &obj, const jsi::String &name) override;+  void setPropertyValue(const jsi::Object &obj, const jsi::PropNameID &name, const jsi::Value &value) override;+  void setPropertyValue(const jsi::Object &obj, const jsi::String &name, const jsi::Value &value) override;++  bool isArray(const jsi::Object &obj) const override;+  bool isArrayBuffer(const jsi::Object &obj) const override;+  bool isFunction(const jsi::Object &obj) const override;+  bool isHostObject(const jsi::Object &obj) const override;+  bool isHostFunction(const jsi::Function &func) const override;+  jsi::Array getPropertyNames(const jsi::Object &obj) override;++  jsi::WeakObject createWeakObject(const jsi::Object &obj) override;+  jsi::Value lockWeakObject(const jsi::WeakObject &weakObj) override;++  jsi::Array createArray(size_t length) override;+  jsi::ArrayBuffer createArrayBuffer(std::shared_ptr<jsi::MutableBuffer> buffer);+  size_t size(const jsi::Array &arr) override;+  size_t size(const jsi::ArrayBuffer &arrBuf) override;+  uint8_t *data(const jsi::ArrayBuffer &arrBuff) override;+  jsi::Value getValueAtIndex(const jsi::Array &arr, size_t index) override;+  void setValueAtIndexImpl(const jsi::Array &arr, size_t index, const jsi::Value &value) override;++  jsi::Function createFunctionFromHostFunction(+      const jsi::PropNameID &name,+      unsigned int paramCount,+      jsi::HostFunctionType func) override;+  jsi::Value call(const jsi::Function &func, const jsi::Value &jsThis, const jsi::Value *args, size_t count) override;+  jsi::Value callAsConstructor(const jsi::Function &func, const jsi::Value *args, size_t count) override;++  ScopeState *pushScope() override;+  void popScope(ScopeState *) override;++  bool strictEquals(const jsi::Symbol &a, const jsi::Symbol &b) const override;+  bool strictEquals(const jsi::BigInt &a, const jsi::BigInt &b) const override;+  bool strictEquals(const jsi::String &a, const jsi::String &b) const override;+  bool strictEquals(const jsi::Object &a, const jsi::Object &b) const override;++  bool instanceOf(const jsi::Object &obj, const jsi::Function &func) override;++ private:+  // RAII class to open and close the environment scope.+  class NodeApiScope {+   public:+    NodeApiScope(NodeApiJsiRuntime &runtime) noexcept;+    ~NodeApiScope() noexcept;++    NodeApiScope(const NodeApiScope &) = delete;+    NodeApiScope &operator=(const NodeApiScope &) = delete;++   private:+    NodeApiJsiRuntime &runtime_;+    ScopeState *scopeState_{};+  };++  // RAII class to open and close the environment scope.+  class NodeApiPointerValueScope {+   public:+    NodeApiPointerValueScope(NodeApiJsiRuntime &runtime) noexcept;+    ~NodeApiPointerValueScope() noexcept;++    NodeApiPointerValueScope(const NodeApiPointerValueScope &) = delete;+    NodeApiPointerValueScope &operator=(const NodeApiPointerValueScope &) = delete;++   private:+    NodeApiJsiRuntime &runtime_;+  };++  // Sets the variable in the constructor and then restores its value in the destructor.+  template <typename T>+  class AutoRestore {+   public:+    AutoRestore(T &varRef, T value);+    ~AutoRestore();++    AutoRestore(const AutoRestore &) = delete;+    AutoRestore &operator=(const AutoRestore &) = delete;++   private:+    T &varRef_;+    T oldValue_;+  };++  enum class NodeApiPointerValueKind {+    Object,+    WeakObject,+    String,+    StringPropNameID,+    Symbol,+    BigInt,+  };++  class NodeApiRefCountedPointerValue;++  // NodeApiPointerValue is used by jsi::Pointer derived classes.+  struct NodeApiPointerValue : PointerValue {+    virtual NodeApiRefCountedPointerValue *clone(NodeApiJsiRuntime &runtime) const = 0;+    virtual napi_value getValue(NodeApiJsiRuntime &runtime) noexcept = 0;+    virtual NodeApiPointerValueKind getKind() const noexcept = 0;+  };++  // NodeApiStackOnlyPointerValue helps to avoid memory allocation in some scenarios.+  // It is used by the JsiValueView, JsiValueViewArgs, and PropNameIDView classes+  // to keep temporary PointerValues on the call stack.+  class NodeApiStackOnlyPointerValue final : public NodeApiPointerValue {+   public:+    NodeApiStackOnlyPointerValue(napi_value value, NodeApiPointerValueKind pointerKind) noexcept;++    void invalidate() noexcept override;+    NodeApiRefCountedPointerValue *clone(NodeApiJsiRuntime &runtime) const override;+    napi_value getValue(NodeApiJsiRuntime &runtime) noexcept override;+    NodeApiPointerValueKind getKind() const noexcept override;++    NodeApiStackOnlyPointerValue(const NodeApiStackOnlyPointerValue &) = delete;+    NodeApiStackOnlyPointerValue &operator=(const NodeApiStackOnlyPointerValue &) = delete;++   private:+    napi_value value_{};+    NodeApiPointerValueKind pointerKind_{NodeApiPointerValueKind::Object};+  };++  // NodeApiRefCountedPointerValue is a ref counted implementation of PointerValue that is allocated in the heap.+  // It expects to have three different types of smart pointers:+  // - jsi::Pointer derived classes uses clone() and invalidate() methods to increment and decrement the ref count.+  //   The invalidate() method can be called from any thread.+  // - NodeApiStackValueHolder points to NodeApiRefCountedPointerValue when it has associated napi_value.+  // - NodeApiRefHolder points to NodeApiRefCountedPointerValue when it has associated napi_ref.+  //+  // Using three types of smart pointers ensures that we always destroy napi_ref from the right thread.+  // The invalidate() method can destroy NodeApiRefCountedPointerValue from a different thread only when there are+  // no napi_value or napi_ref present in the class. Otherwise, the destruction will happen when we remove+  // either napi_value or napi_ref reference.+  //+  // Some NodeApiRefCountedPointerValue are created with napi_value and may never get napi_ref.+  // When stack scope is closed we see if there any jsi::Pointer references. If such references still exist, then+  // we ensure that it has an associated napi_ref or we create one.+  //+  // In addition to the scope closure, there are two cases when we collect NodeApiRefCountedPointerValue+  // without jsi::Pointer references:+  // - When we grow the NodeApiJsiRuntime::stackValues_ vector and we reached current capacity.+  // - When we grow the NodeApiJsiRuntime::refs_ vector and we reached current capacity.+  class NodeApiRefCountedPointerValue final : public NodeApiPointerValue {+   public:+    // Creates new NodeApiRefCountedPointerValue and adds it to the NodeApiJsiRuntime::stackValues_. The ref count+    // usually starts with 2: one for NodeApiJsiRuntime::stackValues_ reference and another for the targeting+    // jsi::Pointer.+    static NodeApiRefCountedPointerValue *make(+        NodeApiJsiRuntime &runtime,+        napi_value value,+        NodeApiPointerValueKind pointerKind,+        int32_t initialRefCount = 2);++    // Creates new NodeApiRefCountedPointerValue and adds it to the NodeApiJsiRuntime::stackValues_. Then, it creates+    // the napi_ref. The ref count usually starts with 2: one for NodeApiJsiRuntime::stackValues_ reference and another+    // for the targeting NodeApiRefHolder.+    static NodeApiRefCountedPointerValue *makeNodeApiRef(+        NodeApiJsiRuntime &runtime,+        napi_value value,+        NodeApiPointerValueKind pointerKind,+        int32_t initialRefCount = 2);++    void invalidate() noexcept override;+    NodeApiRefCountedPointerValue *clone(NodeApiJsiRuntime &runtime) const override;+    napi_value getValue(NodeApiJsiRuntime &runtime) noexcept override;+    NodeApiPointerValueKind getKind() const noexcept override;++    // Returns true if the ref count is bigger than if we would have only references for napi_value and napi_ref.+    static bool usedByJsiPointer(NodeApiRefCountedPointerValue *ptr) noexcept;++    // Remove napi_value field.+    static void deleteStackValue(NodeApiRefCountedPointerValue *ptr) noexcept;++    // Removes napi_value field and ensures existence of napi_ref field.+    // Returns true if napi_ref was created and ref count did not change.+    // Otherwise, the napi_ref was already there and the ref count was decremented.+    void convertToNodeApiRef(NodeApiJsiRuntime &runtime) noexcept;++    // Removes napi_ref pointer and decrements ref count. The napi_ref is not deleted.+    // It is useful for napi_env shutdown when napi_env deletes napi_ref and we must not do it.+    static void deleteNodeApiRef(NodeApiRefCountedPointerValue *ptr) noexcept;++    // Deletes napi_ref, removes napi_ref pointer, and decrements ref count.+    // This method must be used in all scenarios except for napi_env shutdown.+    static void deleteNodeApiRef(NodeApiRefCountedPointerValue *ptr, NodeApiJsiRuntime &runtime) noexcept;++    NodeApiRefCountedPointerValue(const NodeApiRefCountedPointerValue &) = delete;+    NodeApiRefCountedPointerValue &operator=(const NodeApiRefCountedPointerValue &) = delete;++   private:+    NodeApiRefCountedPointerValue(+        napi_value value,+        NodeApiPointerValueKind pointerKind,+        int32_t initialRefCount) noexcept;++    void incRefCount() const noexcept;++    // Decrements ref count. Delete this instance if ref count is zero.+    void decRefCount() const noexcept;++    NodeApiRefCountedPointerValue *createNodeApiRef(NodeApiJsiRuntime &runtime);++   private:+    napi_value value_{};+    napi_ref ref_{};+    mutable std::atomic<int32_t> refCount_{};+    const NodeApiPointerValueKind pointerKind_{NodeApiPointerValueKind::Object};++    static constexpr char kPrimitivePropertyName[] = "X";+  };++  using NodeApiPointerValueDeleter = void(NodeApiRefCountedPointerValue *);++  template <NodeApiPointerValueDeleter *deleter>+  class NodeApiPointerValueHolder {+   public:+    NodeApiPointerValueHolder() = default;+    explicit NodeApiPointerValueHolder(NodeApiRefCountedPointerValue *ptr) : ptr_(ptr) {}++    NodeApiPointerValueHolder(NodeApiPointerValueHolder &&other) : ptr_(std::exchange(other.ptr_, nullptr)) {}++    NodeApiPointerValueHolder &operator=(NodeApiPointerValueHolder &&other) {+      if (this != &other) {+        NodeApiPointerValueHolder temp(std::move(*this));+        ptr_ = std::exchange(other.ptr_, nullptr);+      }+      return *this;+    }++    ~NodeApiPointerValueHolder() {+      if (NodeApiRefCountedPointerValue *ptr = std::exchange(ptr_, nullptr)) {+        deleter(ptr);+      }+    }++    NodeApiRefCountedPointerValue *operator->() const {+      return ptr_;+    }++    NodeApiRefCountedPointerValue *get() const {+      return ptr_;+    }++    explicit operator bool() const {+      return ptr_ != nullptr;+    }++    NodeApiPointerValueHolder(NodeApiPointerValueHolder const &) = delete;+    NodeApiPointerValueHolder &operator=(NodeApiPointerValueHolder const &) = delete;++   private:+    NodeApiRefCountedPointerValue *ptr_{};+  };++  using NodeApiStackValueHolder = NodeApiPointerValueHolder<&NodeApiRefCountedPointerValue::deleteStackValue>;+  using NodeApiRefHolder = NodeApiPointerValueHolder<&NodeApiRefCountedPointerValue::deleteNodeApiRef>;++  // SmallBuffer keeps InplaceSize elements in place in the class, and uses heap memory for more elements.+  template <typename T, size_t InplaceSize>+  class SmallBuffer {+   public:+    SmallBuffer(size_t size) noexcept;++    T *data() noexcept;+    size_t size() const noexcept;++    SmallBuffer(const SmallBuffer &) = delete;+    SmallBuffer &operator=(const SmallBuffer &) = delete;++   private:+    size_t size_{};+    std::array<T, InplaceSize> stackData_{};+    std::unique_ptr<T[]> heapData_{};+  };++  // The number of arguments that we keep on stack. We use heap if we have more arguments.+  constexpr static size_t MaxStackArgCount = 8;++  // NodeApiValueArgs helps optimize passing arguments to NAPI functions.+  // If number of arguments is below or equal to MaxStackArgCount, they are kept on the call stack,+  // otherwise arguments are allocated on the heap.+  class NodeApiValueArgs {+   public:+    NodeApiValueArgs(NodeApiJsiRuntime &runtime, span<const jsi::Value> args);+    operator span<napi_value>();++    NodeApiValueArgs(const NodeApiValueArgs &) = delete;+    NodeApiValueArgs &operator=(const NodeApiValueArgs &) = delete;++   private:+    SmallBuffer<napi_value, MaxStackArgCount> args_;+  };++  // Helps to use the stack storage for a temporary conversion from napi_value to jsi::Value.+  // It also helps to avoid a conversion to a relatively expensive napi_ext_ref.+  class JsiValueView {+   public:+    union StoreType {+      NodeApiStackOnlyPointerValue value_;+      std::array<std::byte, sizeof(NodeApiStackOnlyPointerValue)> bytes_;+      StoreType() {}+      ~StoreType() {}+    };++   public:+    JsiValueView(NodeApiJsiRuntime *runtime, napi_value jsValue);+    operator const jsi::Value &() const noexcept;++    static jsi::Value initValue(NodeApiJsiRuntime *runtime, napi_value jsValue, StoreType *store);++    JsiValueView(const JsiValueView &) = delete;+    JsiValueView &operator=(const JsiValueView &) = delete;++   private:+    StoreType pointerStore_;+    jsi::Value value_{};+  };++  // Helps to use stack storage for passing arguments that must be temporarily converted+  // from napi_value to jsi::Value.+  // It helps to avoid conversion to a relatively expensive napi_ext_ref.+  class JsiValueViewArgs {+   public:+    JsiValueViewArgs(NodeApiJsiRuntime *runtime, span<napi_value> args) noexcept;+    const jsi::Value *data() noexcept;+    size_t size() const noexcept;++    JsiValueViewArgs(const JsiValueViewArgs &);+    JsiValueViewArgs &operator=(const JsiValueViewArgs &);++   private:+    using StoreType = JsiValueView::StoreType;+    SmallBuffer<StoreType, MaxStackArgCount> pointerStore_{0};+    SmallBuffer<jsi::Value, MaxStackArgCount> args_{0};+  };++  // Helps to use stack storage for a temporary conversion from napi_value to jsi::PropNameID.+  // It helps to avoid conversions to a relatively expensive napi_ext_ref.+  class PropNameIDView {+   public:+    PropNameIDView(NodeApiJsiRuntime *runtime, napi_value propertyId) noexcept;+    operator const jsi::PropNameID &() const noexcept;++    PropNameIDView(const PropNameIDView &);+    PropNameIDView &operator=(const PropNameIDView &);++   private:+    using StoreType = JsiValueView::StoreType;+    StoreType pointerStore_{};+    jsi::PropNameID const propertyId_;+  };++  // Wraps up the jsi::HostFunctionType along with the NodeApiJsiRuntime.+  class HostFunctionWrapper {+   public:+    HostFunctionWrapper(jsi::HostFunctionType &&hostFunction, NodeApiJsiRuntime &runtime);++    jsi::HostFunctionType &hostFunction() noexcept;+    NodeApiJsiRuntime &runtime() noexcept;++    HostFunctionWrapper(const HostFunctionWrapper &) = delete;+    HostFunctionWrapper &operator=(const HostFunctionWrapper &) = delete;++   private:+    jsi::HostFunctionType hostFunction_;+    NodeApiJsiRuntime &runtime_;+  };++  // Wraps up the napi_ext_prepared_script.+  class NodeApiPreparedJavaScript final : public jsi::PreparedJavaScript {+   public:+    NodeApiPreparedJavaScript(napi_env env, napi_ext_prepared_script script, std::string sourceURL)+        : env_(env), script_(script), sourceURL_(std::move(sourceURL)) {}++    ~NodeApiPreparedJavaScript() override {+      NodeApi::current()->napi_ext_delete_prepared_script(env_, script_);+    }++    napi_ext_prepared_script getScript() const {+      return script_;+    }++    const std::string &sourceURL() const {+      return sourceURL_;+    }++    NodeApiPreparedJavaScript(const NodeApiPreparedJavaScript &) = delete;+    NodeApiPreparedJavaScript &operator=(const NodeApiPreparedJavaScript &) = delete;++   private:+    napi_env env_;+    napi_ext_prepared_script script_;+    std::string sourceURL_;+  };++ private: // Error-handling utility methods+  template <typename... Args>+  jsi::JSError makeJSError(Args &&...args);+  [[noreturn]] void throwJSException(napi_status errorCode) const;+  [[noreturn]] void throwNativeException(char const *errorMessage) const;+  void rewriteErrorMessage(napi_value jsError) const;+  template <typename TLambda>+  auto runInMethodContext(char const *methodName, TLambda lambda);+  template <typename TLambda>+  napi_value handleCallbackExceptions(TLambda lambda) const noexcept;+  bool setException(napi_value error) const noexcept;+  bool setException(std::string_view message) const noexcept;++ private: // Shared NAPI call helpers+  napi_valuetype typeOf(napi_value value) const;+  bool strictEquals(napi_value left, napi_value right) const;+  napi_value getUndefined() const;+  napi_value getNull() const;+  napi_value getGlobal() const;+  napi_value getBoolean(bool value) const;+  bool getValueBool(napi_value value) const;+  napi_value createInt32(int32_t value) const;+  napi_value createUInt32(uint32_t value) const;+  napi_value createDouble(double value) const;+  double getValueDouble(napi_value value) const;+  napi_value createStringLatin1(std::string_view value) const;+  napi_value createStringUtf8(std::string_view value) const;+  napi_value createStringUtf8(const uint8_t *data, size_t length) const;+  std::string stringToStdString(napi_value stringValue) const;+  napi_value getPropertyIdFromName(std::string_view value) const;+  napi_value getPropertyIdFromName(const uint8_t *data, size_t length) const;+  napi_value getPropertyIdFromName(napi_value str) const;+  napi_value getPropertyIdFromSymbol(napi_value sym) const;+  std::string propertyIdToStdString(napi_value propertyId);+  napi_value createSymbol(std::string_view symbolDescription) const;+  std::string symbolToStdString(napi_value symbolValue);+  napi_value callFunction(napi_value thisArg, napi_value function, span<napi_value> args = {}) const;+  napi_value constructObject(napi_value constructor, span<napi_value> args = {}) const;+  bool instanceOf(napi_value object, napi_value constructor) const;+  napi_value createNodeApiObject() const;+  bool hasProperty(napi_value object, napi_value propertyId) const;+  napi_value getProperty(napi_value object, napi_value propertyId) const;+  void setProperty(napi_value object, napi_value propertyId, napi_value value) const;+  void setProperty(napi_value object, napi_value propertyId, napi_value value, napi_property_attributes attrs) const;+  napi_value createNodeApiArray(size_t length) const;+  bool isArray(napi_value value) const;+  size_t getArrayLength(napi_value value) const;+  napi_value getElement(napi_value arr, size_t index) const;+  void setElement(napi_value array, uint32_t index, napi_value value) const;+  static napi_value __cdecl jsiHostFunctionCallback(napi_env env, napi_callback_info info) noexcept;+  napi_value createExternalFunction(napi_value name, int32_t paramCount, napi_callback callback, void *callbackData);+  napi_value createExternalObject(void *data, napi_finalize finalizeCallback) const;+  template <typename T>+  napi_value createExternalObject(std::unique_ptr<T> &&data) const;+  void *getExternalData(napi_value object) const;+  const std::shared_ptr<jsi::HostObject> &getJsiHostObject(napi_value obj);+  napi_value getHostObjectProxyHandler();+  template <napi_value (NodeApiJsiRuntime::*trapMethod)(span<napi_value>), size_t argCount>+  void setProxyTrap(napi_value handler, napi_value propertyName);+  napi_value hostObjectHasTrap(span<napi_value> args);+  napi_value hostObjectGetTrap(span<napi_value> args);+  napi_value hostObjectSetTrap(span<napi_value> args);+  napi_value hostObjectOwnKeysTrap(span<napi_value> args);+  napi_value hostObjectGetOwnPropertyDescriptorTrap(span<napi_value> args);++ private: // Miscellaneous utility methods+  span<const uint8_t> toSpan(const jsi::Buffer &buffer);+  jsi::Value toJsiValue(napi_value value) const;+  napi_value getNodeApiValue(const jsi::Value &value) const;+  napi_value getNodeApiValue(const jsi::Pointer &ptr) const;+  napi_value getNodeApiValue(const NodeApiRefHolder &ref) const;+  NodeApiRefCountedPointerValue *cloneNodeApiPointerValue(const PointerValue *pointerValue);+  std::optional<uint32_t> toArrayIndex(std::string::const_iterator first, std::string::const_iterator last);++  template <typename T, std::enable_if_t<std::is_same_v<jsi::Object, T>, int> = 0>+  T makeJsiPointer(napi_value value) const;+  template <typename T, std::enable_if_t<std::is_same_v<jsi::String, T>, int> = 0>+  T makeJsiPointer(napi_value value) const;+  template <typename T, std::enable_if_t<std::is_same_v<jsi::Symbol, T>, int> = 0>+  T makeJsiPointer(napi_value value) const;+  template <typename T, std::enable_if_t<std::is_same_v<jsi::BigInt, T>, int> = 0>+  T makeJsiPointer(napi_value value) const;+  template <+      typename TTo,+      typename TFrom,+      std::enable_if_t<std::is_base_of_v<jsi::Pointer, TTo>, int> = 0,+      std::enable_if_t<std::is_base_of_v<jsi::Pointer, TFrom>, int> = 0>+  TTo cloneAs(const TFrom &pointer) const;+  NodeApiRefHolder makeNodeApiRef(napi_value value, NodeApiPointerValueKind pointerKind, int32_t initialRefCount = 2);++  void addStackValue(NodeApiStackValueHolder &&pointerHolder);+  void addRef(NodeApiRefHolder &&refHolder);+  void pushPointerValueScope() noexcept;+  void popPointerValueScope() noexcept;+  void collectUnusedStackValues();+  void collectUnusedRefs() noexcept;++  napi_env getEnv() const noexcept {+    return env_;+  }++ private: // Fields+  napi_env env_{};+  NodeApi *nodeApi_;+  std::function<void()> onDelete_;+  std::string sourceURL_;++  // Property ID cache to improve execution speed.+  struct PropertyId {+    NodeApiRefHolder Error;+    NodeApiRefHolder Object;+    NodeApiRefHolder Proxy;+    NodeApiRefHolder Symbol;+    NodeApiRefHolder byteLength;+    NodeApiRefHolder configurable;+    NodeApiRefHolder enumerable;+    NodeApiRefHolder get;+    NodeApiRefHolder getOwnPropertyDescriptor;+    NodeApiRefHolder has;+    NodeApiRefHolder hostFunctionSymbol;+    NodeApiRefHolder hostObjectSymbol;+    NodeApiRefHolder length;+    NodeApiRefHolder message;+    NodeApiRefHolder ownKeys;+    NodeApiRefHolder propertyIsEnumerable;+    NodeApiRefHolder prototype;+    NodeApiRefHolder set;+    NodeApiRefHolder stack;+    NodeApiRefHolder toString;+    NodeApiRefHolder value;+    NodeApiRefHolder writable;+  } propertyId_;++  // Cache of commonly used values.+  struct CachedValue {+    NodeApiRefHolder Error;+    NodeApiRefHolder Global;+    NodeApiRefHolder HostObjectProxyHandler;+    NodeApiRefHolder ProxyConstructor;+    NodeApiRefHolder SymbolToString;+  } cachedValue_;++  bool hasPendingJSError_{false};++  std::vector<size_t> stackScopes_;+  std::vector<NodeApiStackValueHolder> stackValues_;+  std::vector<NodeApiRefHolder> refs_;++  // TODO: implement GC for propNameIDs_+  std::unordered_map<StringKey, NodeApiRefHolder, StringKey::Hash, StringKey::EqualTo> propNameIDs_;++  NodeApiJsiRuntime &runtime{*this};+};++//=====================================================================================================================+// StringKey implementation+//=====================================================================================================================++StringKey::StringKey(std::string &&string) noexcept+    : string_(std::move(string)), type_(Type::String), hash_(std::hash<std::string_view>{}(string_)) {}++StringKey::StringKey(std::string_view view) noexcept+    : view_(view), type_(Type::View), hash_(std::hash<std::string_view>{}(view_)) {}++StringKey::StringKey(const char *data, size_t length) noexcept+    : view_(data, length), type_(Type::View), hash_(std::hash<std::string_view>{}(view_)) {}++StringKey::StringKey(StringKey &&other) noexcept : type_(other.type_), hash_(std::exchange(other.hash_, 0)) {+  if (type_ == Type::String) {+    ::new (std::addressof(string_)) std::string(std::move(other.string_));+  } else {+    ::new (std::addressof(view_)) std::string_view(std::exchange(other.view_, std::string_view()));+  }+}++StringKey &StringKey::operator=(StringKey &&other) noexcept {+  if (this != &other) {+    this->~StringKey();+    ::new (this) StringKey(std::move(other));+  }+  return *this;+}++StringKey::~StringKey() {+  if (type_ == Type::String) {+    std::addressof(string_)->~basic_string();+  } else {+    std::addressof(view_)->~basic_string_view();+  }+}++std::string_view StringKey::getStringView() const {+  return (type_ == Type::String) ? std::string_view(string_) : view_;+}++bool StringKey::equalTo(const StringKey &other) const {+  return getStringView().compare(other.getStringView()) == 0;+}++size_t StringKey::hash() const {+  return hash_;+}++size_t StringKey::Hash::operator()(const StringKey &key) const {+  return key.hash();+}++bool StringKey::EqualTo::operator()(const StringKey &left, const StringKey &right) const {+  return left.equalTo(right);+}++//=====================================================================================================================+// NodeApiJsiRuntime implementation+//=====================================================================================================================++NodeApiJsiRuntime::NodeApiJsiRuntime(napi_env env, NodeApi *nodeApi, std::function<void()> onDelete) noexcept+    : env_(env), nodeApi_(nodeApi), onDelete_(std::move(onDelete)) {+  NodeApiScope scope{*this};+  propertyId_.Error = makeNodeApiRef(getPropertyIdFromName("Error"), NodeApiPointerValueKind::String);+  propertyId_.Object = makeNodeApiRef(getPropertyIdFromName("Object"), NodeApiPointerValueKind::String);+  propertyId_.Proxy = makeNodeApiRef(getPropertyIdFromName("Proxy"), NodeApiPointerValueKind::String);+  propertyId_.Symbol = makeNodeApiRef(getPropertyIdFromName("Symbol"), NodeApiPointerValueKind::String);+  propertyId_.byteLength = makeNodeApiRef(getPropertyIdFromName("byteLength"), NodeApiPointerValueKind::String);+  propertyId_.configurable = makeNodeApiRef(getPropertyIdFromName("configurable"), NodeApiPointerValueKind::String);+  propertyId_.enumerable = makeNodeApiRef(getPropertyIdFromName("enumerable"), NodeApiPointerValueKind::String);+  propertyId_.get = makeNodeApiRef(getPropertyIdFromName("get"), NodeApiPointerValueKind::String);+  propertyId_.getOwnPropertyDescriptor =+      makeNodeApiRef(getPropertyIdFromName("getOwnPropertyDescriptor"), NodeApiPointerValueKind::String);+  propertyId_.has = makeNodeApiRef(getPropertyIdFromName("has"), NodeApiPointerValueKind::String);+  propertyId_.hostFunctionSymbol = makeNodeApiRef(createSymbol("hostFunctionSymbol"), NodeApiPointerValueKind::Symbol);+  propertyId_.hostObjectSymbol = makeNodeApiRef(createSymbol("hostObjectSymbol"), NodeApiPointerValueKind::Symbol);+  propertyId_.length = makeNodeApiRef(getPropertyIdFromName("length"), NodeApiPointerValueKind::String);+  propertyId_.message = makeNodeApiRef(getPropertyIdFromName("message"), NodeApiPointerValueKind::String);+  propertyId_.ownKeys = makeNodeApiRef(getPropertyIdFromName("ownKeys"), NodeApiPointerValueKind::String);+  propertyId_.propertyIsEnumerable =+      makeNodeApiRef(getPropertyIdFromName("propertyIsEnumerable"), NodeApiPointerValueKind::String);+  propertyId_.prototype = makeNodeApiRef(getPropertyIdFromName("prototype"), NodeApiPointerValueKind::String);+  propertyId_.set = makeNodeApiRef(getPropertyIdFromName("set"), NodeApiPointerValueKind::String);+  propertyId_.stack = makeNodeApiRef(getPropertyIdFromName("stack"), NodeApiPointerValueKind::String);+  propertyId_.toString = makeNodeApiRef(getPropertyIdFromName("toString"), NodeApiPointerValueKind::String);+  propertyId_.value = makeNodeApiRef(getPropertyIdFromName("value"), NodeApiPointerValueKind::String);+  propertyId_.writable = makeNodeApiRef(getPropertyIdFromName("writable"), NodeApiPointerValueKind::String);++  cachedValue_.Global = makeNodeApiRef(getGlobal(), NodeApiPointerValueKind::Object);+  cachedValue_.Error = makeNodeApiRef(+      getProperty(getNodeApiValue(cachedValue_.Global), getNodeApiValue(propertyId_.Error)),+      NodeApiPointerValueKind::Object);+}++NodeApiJsiRuntime::~NodeApiJsiRuntime() {+  if (onDelete_) {+    onDelete_();+  }+}++jsi::Value NodeApiJsiRuntime::evaluateJavaScript(+    const std::shared_ptr<const jsi::Buffer> &buffer,+    const std::string &sourceURL) {+  return evaluatePreparedJavaScript(prepareJavaScript(buffer, sourceURL));+}++std::shared_ptr<const jsi::PreparedJavaScript> NodeApiJsiRuntime::prepareJavaScript(+    const std::shared_ptr<const jsi::Buffer> &sourceBuffer,+    std::string sourceURL) {+  NodeApiScope scope{*this};+  napi_ext_prepared_script script{};+  napi_status status = nodeApi_->napi_ext_create_prepared_script(+      env_,+      const_cast<uint8_t *>(sourceBuffer->data()),+      sourceBuffer->size(),+      [](napi_env /*env*/, void * /*data*/, void *finalizeHint) {+        delete reinterpret_cast<std::shared_ptr<const jsi::Buffer> *>(finalizeHint);+      },+      new std::shared_ptr<const jsi::Buffer>(sourceBuffer),+      sourceURL.c_str(),+      &script);+  CHECK_NAPI(status); // Not for the call to keep better automated formatting.+  return std::make_shared<NodeApiPreparedJavaScript>(env_, script, std::move(sourceURL));+}++jsi::Value NodeApiJsiRuntime::evaluatePreparedJavaScript(const std::shared_ptr<const jsi::PreparedJavaScript> &js) {+  NodeApiScope scope{*this};+  auto preparedScript = static_cast<const NodeApiPreparedJavaScript *>(js.get());+  AutoRestore<std::string> sourceURLScope{sourceURL_, preparedScript->sourceURL()};+  napi_value result{};+  CHECK_NAPI(nodeApi_->napi_ext_prepared_script_run(env_, preparedScript->getScript(), &result));+  return toJsiValue(result);+}++bool NodeApiJsiRuntime::drainMicrotasks(int maxMicrotasksHint) {+  bool result{};+  CHECK_NAPI(nodeApi_->napi_ext_drain_microtasks(env_, maxMicrotasksHint, &result));+  return result;+}++jsi::Object NodeApiJsiRuntime::global() {+  return make<jsi::Object>(cachedValue_.Global->clone(*this));+}++std::string NodeApiJsiRuntime::description() {+  size_t length{};+  CHECK_NAPI(nodeApi_->napi_ext_get_description(env_, nullptr, 0, &length));+  std::string desc(length, '\0');+  CHECK_NAPI(nodeApi_->napi_ext_get_description(env_, &desc[0], length + 1, nullptr));+  return desc;+}++bool NodeApiJsiRuntime::isInspectable() {+  bool result{};+  CHECK_NAPI(nodeApi_->napi_ext_is_inspectable(env_, &result));+  return result;+}++jsi::Runtime::PointerValue *NodeApiJsiRuntime::cloneSymbol(const jsi::Runtime::PointerValue *pointerValue) {+  return cloneNodeApiPointerValue(pointerValue);+}++jsi::Runtime::PointerValue *NodeApiJsiRuntime::cloneBigInt(const jsi::Runtime::PointerValue *pointerValue) {+  return cloneNodeApiPointerValue(pointerValue);+}++jsi::Runtime::PointerValue *NodeApiJsiRuntime::cloneString(const jsi::Runtime::PointerValue *pointerValue) {+  return cloneNodeApiPointerValue(pointerValue);+}++jsi::Runtime::PointerValue *NodeApiJsiRuntime::cloneObject(const jsi::Runtime::PointerValue *pointerValue) {+  return cloneNodeApiPointerValue(pointerValue);+}++jsi::Runtime::PointerValue *NodeApiJsiRuntime::clonePropNameID(const jsi::Runtime::PointerValue *pointerValue) {+  return cloneNodeApiPointerValue(pointerValue);+}++jsi::PropNameID NodeApiJsiRuntime::createPropNameIDFromAscii(const char *str, size_t length) {+  StringKey keyName{str, length};+  auto it = propNameIDs_.find(keyName);+  if (it != propNameIDs_.end()) {+    return make<jsi::PropNameID>(it->second->clone(*this));+  }++  napi_value obj = createNodeApiObject();+  napi_value propName{};+  CHECK_NAPI(nodeApi_->napi_create_string_latin1(env_, str, length, &propName));+  CHECK_NAPI(nodeApi_->napi_set_property(env_, obj, propName, getUndefined()));+  napi_value props{};+  CHECK_NAPI(nodeApi_->napi_get_all_property_names(+      env_, obj, napi_key_own_only, napi_key_skip_symbols, napi_key_numbers_to_strings, &props));+  napi_value propNameId = getElement(props, 0);+  NodeApiRefHolder propNameRef = makeNodeApiRef(propNameId, NodeApiPointerValueKind::StringPropNameID, 3);+  jsi::PropNameID result = make<jsi::PropNameID>(propNameRef.get());+  propNameIDs_.try_emplace(StringKey(std::string(keyName.getStringView())), std::move(propNameRef));+  return result;+}++jsi::PropNameID NodeApiJsiRuntime::createPropNameIDFromUtf8(const uint8_t *utf8, size_t length) {+  StringKey keyName{reinterpret_cast<const char *>(utf8), length};+  auto it = propNameIDs_.find(keyName);+  if (it != propNameIDs_.end()) {+    return make<jsi::PropNameID>(it->second->clone(*this));+  }++  napi_value obj = createNodeApiObject();+  napi_value propName{};+  CHECK_NAPI(nodeApi_->napi_create_string_utf8(env_, reinterpret_cast<const char *>(utf8), length, &propName));+  CHECK_NAPI(nodeApi_->napi_set_property(env_, obj, propName, getUndefined()));+  napi_value props{};+  CHECK_NAPI(nodeApi_->napi_get_all_property_names(+      env_, obj, napi_key_own_only, napi_key_skip_symbols, napi_key_numbers_to_strings, &props));+  napi_value propNameId = getElement(props, 0);+  NodeApiRefHolder propNameRef = makeNodeApiRef(propNameId, NodeApiPointerValueKind::StringPropNameID, 3);+  jsi::PropNameID result = make<jsi::PropNameID>(propNameRef.get());+  propNameIDs_.try_emplace(StringKey(std::string(keyName.getStringView())), std::move(propNameRef));+  return result;+}++jsi::PropNameID NodeApiJsiRuntime::createPropNameIDFromString(const jsi::String &str) {+  const NodeApiPointerValue *pv = static_cast<const NodeApiPointerValue *>(getPointerValue(str));+  if (pv->getKind() == NodeApiPointerValueKind::StringPropNameID) {+    return make<jsi::PropNameID>(pv->clone(*this));+  }++  StringKey keyName(utf8(str));+  auto it = propNameIDs_.find(keyName);+  if (it != propNameIDs_.end()) {+    return make<jsi::PropNameID>(it->second->clone(*this));+  }++  napi_value napiStr = const_cast<NodeApiPointerValue *>(pv)->getValue(*this);++  napi_value obj = createNodeApiObject();+  setProperty(obj, napiStr, getUndefined());+  napi_value props{};+  CHECK_NAPI(nodeApi_->napi_get_all_property_names(+      env_, obj, napi_key_own_only, napi_key_skip_symbols, napi_key_numbers_to_strings, &props));+  napi_value propNameId = getElement(props, 0);+  NodeApiRefHolder propNameRef = makeNodeApiRef(propNameId, NodeApiPointerValueKind::StringPropNameID, 3);+  jsi::PropNameID result = make<jsi::PropNameID>(propNameRef.get());+  propNameIDs_.try_emplace(StringKey(std::string(keyName.getStringView())), std::move(propNameRef));+  return result;+}++jsi::PropNameID NodeApiJsiRuntime::createPropNameIDFromSymbol(const jsi::Symbol &sym) {+  // TODO: Should we ensure uniqueness of symbols?+  return cloneAs<jsi::PropNameID>(sym);+}++std::string NodeApiJsiRuntime::utf8(const jsi::PropNameID &id) {+  return propertyIdToStdString(getNodeApiValue(id));+}++bool NodeApiJsiRuntime::compare(const jsi::PropNameID &lhs, const jsi::PropNameID &rhs) {+  return getPointerValue(lhs) == getPointerValue(rhs) || strictEquals(getNodeApiValue(lhs), getNodeApiValue(rhs));+}++std::string NodeApiJsiRuntime::symbolToString(const jsi::Symbol &sym) {+  return symbolToStdString(getNodeApiValue(sym));+}++jsi::BigInt NodeApiJsiRuntime::createBigIntFromInt64(int64_t val) {+  napi_value bigint{};+  CHECK_NAPI(nodeApi_->napi_create_bigint_int64(env_, val, &bigint));+  return makeJsiPointer<jsi::BigInt>(bigint);+}++jsi::BigInt NodeApiJsiRuntime::createBigIntFromUint64(uint64_t val) {+  napi_value bigint{};+  CHECK_NAPI(nodeApi_->napi_create_bigint_uint64(env_, val, &bigint));+  return makeJsiPointer<jsi::BigInt>(bigint);+}++bool NodeApiJsiRuntime::bigintIsInt64(const jsi::BigInt &bigint) {+  napi_value value = getNodeApiValue(bigint);+  bool lossless{false};+  int64_t result{};+  CHECK_NAPI(nodeApi_->napi_get_value_bigint_int64(env_, value, &result, &lossless));+  return lossless;+}++bool NodeApiJsiRuntime::bigintIsUint64(const jsi::BigInt &bigint) {+  napi_value value = getNodeApiValue(bigint);+  bool lossless{false};+  uint64_t result{};+  CHECK_NAPI(nodeApi_->napi_get_value_bigint_uint64(env_, value, &result, &lossless));+  return lossless;+}++uint64_t NodeApiJsiRuntime::truncate(const jsi::BigInt &bigint) {+  napi_value value = getNodeApiValue(bigint);+  bool lossless{false};+  uint64_t result{};+  CHECK_NAPI(nodeApi_->napi_get_value_bigint_uint64(env_, value, &result, &lossless));+  return result;+}++inline uint32_t constexpr maxCharsPerDigitInRadix(int32_t radix) {+  // To compute the lower bound of bits in a BigIntDigitType "covered" by a+  // char. For power of 2 radixes, it is known (exactly) that each character+  // covers log2(radix) bits. For non-power of 2 radixes, a lower bound is+  // log2(greatest power of 2 that is less than radix).+  uint32_t minNumBitsPerChar = radix < 4 ? 1 : radix < 8 ? 2 : radix < 16 ? 3 : radix < 32 ? 4 : 5;++  // With minNumBitsPerChar being the lower bound estimate of how many bits each+  // char can represent, the upper bound of how many chars "fit" in a bigint+  // digit is ceil(sizeofInBits(bigint digit) / minNumBitsPerChar).+  uint32_t numCharsPerDigits = sizeof(uint64_t) / (1 << minNumBitsPerChar);++  return numCharsPerDigits;+}++// Return the high 32 bits of a 64 bit value.+constexpr inline uint32_t Hi_32(uint64_t Value) {+  return static_cast<uint32_t>(Value >> 32);+}++// Return the low 32 bits of a 64 bit value.+constexpr inline uint32_t Lo_32(uint64_t Value) {+  return static_cast<uint32_t>(Value);+}++// Make a 64-bit integer from a high / low pair of 32-bit integers.+constexpr inline uint64_t Make_64(uint32_t High, uint32_t Low) {+  return ((uint64_t)High << 32) | (uint64_t)Low;+}++jsi::String NodeApiJsiRuntime::bigintToString(const jsi::BigInt &bigint, int32_t radix) {+  if (radix < 2 || radix > 36) {+    throw makeJSError("Invalid radix ", radix, " to BigInt.toString");+  }++  napi_value value = getNodeApiValue(bigint);+  size_t wordCount{};+  CHECK_NAPI(nodeApi_->napi_get_value_bigint_words(env_, value, nullptr, &wordCount, nullptr));+  uint64_t stackWords[8];+  std::unique_ptr<uint64_t[]> heapWords;+  uint64_t *words = stackWords;+  if (wordCount > std::size(stackWords)) {+    heapWords = std::unique_ptr<uint64_t[]>(new uint64_t[wordCount]);+    words = heapWords.get();+  }+  int32_t signBit{};+  CHECK_NAPI(nodeApi_->napi_get_value_bigint_words(env_, value, &signBit, &wordCount, words));++  if (signBit) {+    // negate negative numbers, and then add a "-" to the output.+    // a. flip all bits+    for (size_t i = 0; i < wordCount; ++i) {+      words[i] = ~words[i];+    }+    // b. add 1+    for (size_t i = 0; i < wordCount; ++i) {+      if (++words[i] >= 1) {+        break; // No need to carry so exit early.+      }+    }+  }++  if (wordCount == 0) {+    return createStringFromAscii("0", 1);+  }++  // avoid trashing the heap by pre-allocating the largest possible string+  // returned by this function. The "1" below is to account for a possible "-"+  // sign.+  std::string digits;+  digits.reserve(1 + wordCount * maxCharsPerDigitInRadix(radix));++  // Use 32-bit values for calculations to get 64-bit results.+  // For the little-endian machines we just cast the words array.+  // TODO: Add support for big-endian.+  uint32_t *halfWords = reinterpret_cast<uint32_t *>(words);+  size_t count = wordCount * 2;+  for (size_t i = count; i > 0 && halfWords[i - 1] == 0; --i) {+    --count;+  }++  uint32_t divisor = static_cast<uint32_t>(radix);+  uint32_t remainder = 0;+  uint64_t word0 = words[0];++  do {+    // We rewrite the halfWords array as we divide it by radix.+    if (count <= 2) {+      remainder = word0 % divisor;+      word0 = word0 / divisor;+    } else {+      for (size_t i = count; i > 0; --i) {+        uint64_t partialDividend = Make_64(remainder, halfWords[i - 1]);+        if (partialDividend == 0) {+          halfWords[i] = 0;+          remainder = 0;+          if (i == count) {+            if (--count == 2) {+              word0 = words[0];+            }+          }+        } else if (partialDividend < divisor) {+          halfWords[i] = 0;+          remainder = Lo_32(partialDividend);+          if (i == count) {+            if (--count == 2) {+              word0 = words[0];+            }+          }+        } else if (partialDividend == divisor) {+          halfWords[i] = 1;+          remainder = 0;+        } else {+          halfWords[i] = Lo_32(partialDividend / divisor);+          remainder = Lo_32(partialDividend % divisor);+        }+      }+    }++    if (remainder < 10) {+      digits.push_back('0' + remainder);+    } else {+      digits.push_back('a' + remainder - 10);+    }+  } while (count > 2 || word0 != 0);++  if (signBit) {+    digits.push_back('-');+  }++  std::reverse(digits.begin(), digits.end());+  return createStringFromAscii(digits.data(), digits.size());+}++jsi::String NodeApiJsiRuntime::createStringFromAscii(const char *str, size_t length) {+  return makeJsiPointer<jsi::String>(createStringLatin1({str, length}));+}++jsi::String NodeApiJsiRuntime::createStringFromUtf8(const uint8_t *str, size_t length) {+  return makeJsiPointer<jsi::String>(createStringUtf8(str, length));+}++std::string NodeApiJsiRuntime::utf8(const jsi::String &str) {+  return stringToStdString(getNodeApiValue(str));+}++jsi::Object NodeApiJsiRuntime::createObject() {+  return makeJsiPointer<jsi::Object>(createNodeApiObject());+}++jsi::Object NodeApiJsiRuntime::createObject(std::shared_ptr<jsi::HostObject> hostObject) {+  // The hostObjectHolder keeps the hostObject as external data.+  // Then, the hostObjectHolder is wrapped up by a Proxy object to provide access+  // to the hostObject's get, set, and getPropertyNames methods.+  // There is a special symbol property ID, 'hostObjectSymbol', used to access the hostObjectWrapper from the Proxy.+  napi_value hostObjectHolder =+      createExternalObject(std::make_unique<std::shared_ptr<jsi::HostObject>>(std::move(hostObject)));+  napi_value obj = createNodeApiObject();+  setProperty(obj, getNodeApiValue(propertyId_.hostObjectSymbol), hostObjectHolder);+  if (!cachedValue_.ProxyConstructor) {+    cachedValue_.ProxyConstructor = makeNodeApiRef(+        getProperty(getNodeApiValue(cachedValue_.Global), getNodeApiValue(propertyId_.Proxy)),+        NodeApiPointerValueKind::Object);+  }+  napi_value proxy =+      constructObject(getNodeApiValue(cachedValue_.ProxyConstructor), {obj, getHostObjectProxyHandler()});+  return makeJsiPointer<jsi::Object>(proxy);+}++std::shared_ptr<jsi::HostObject> NodeApiJsiRuntime::getHostObject(const jsi::Object &obj) {+  return getJsiHostObject(getNodeApiValue(obj));+}++jsi::HostFunctionType &NodeApiJsiRuntime::getHostFunction(const jsi::Function &func) {+  napi_value hostFunctionHolder = getProperty(getNodeApiValue(func), getNodeApiValue((propertyId_.hostFunctionSymbol)));+  if (typeOf(hostFunctionHolder) == napi_valuetype::napi_external) {+    return static_cast<HostFunctionWrapper *>(getExternalData(hostFunctionHolder))->hostFunction();+  } else {+    throw jsi::JSINativeException("getHostFunction() can only be called with HostFunction.");+  }+}++bool NodeApiJsiRuntime::hasNativeState(const jsi::Object &obj) {+  void *nativeState{};+  napi_status status = nodeApi_->napi_unwrap(env_, getNodeApiValue(obj), &nativeState);+  return status == napi_ok && nativeState != nullptr;+}++std::shared_ptr<jsi::NativeState> NodeApiJsiRuntime::getNativeState(const jsi::Object &obj) {+  void *nativeState{};+  CHECK_NAPI(nodeApi_->napi_unwrap(env_, getNodeApiValue(obj), &nativeState));+  if (nativeState != nullptr) {+    return *reinterpret_cast<std::shared_ptr<jsi::NativeState> *>(nativeState);+  } else {+    return std::shared_ptr<jsi::NativeState>();+  }+}++void NodeApiJsiRuntime::setNativeState(const jsi::Object &obj, std::shared_ptr<jsi::NativeState> state) {+  if (hasNativeState(obj)) {+    void *nativeState{};+    CHECK_NAPI(nodeApi_->napi_remove_wrap(env_, getNodeApiValue(obj), &nativeState));+    if (nativeState != nullptr) {+      std::shared_ptr<jsi::NativeState> oldState{+          std::move(*reinterpret_cast<std::shared_ptr<jsi::NativeState> *>(nativeState))};+    }+  }++  if (state) {+    CHECK_NAPI(nodeApi_->napi_wrap(+        env_,+        getNodeApiValue(obj),+        new std::shared_ptr<jsi::NativeState>(std::move(state)),+        [](napi_env env, void *data, void * /*finalize_hint*/) {+          std::shared_ptr<jsi::NativeState> oldState{+              std::move(*reinterpret_cast<std::shared_ptr<jsi::NativeState> *>(data))};+        },+        nullptr,+        nullptr));+  }+}++jsi::Value NodeApiJsiRuntime::getProperty(const jsi::Object &obj, const jsi::PropNameID &name) {+  return toJsiValue(getProperty(getNodeApiValue(obj), getNodeApiValue(name)));+}++jsi::Value NodeApiJsiRuntime::getProperty(const jsi::Object &obj, const jsi::String &name) {+  return toJsiValue(getProperty(getNodeApiValue(obj), getNodeApiValue(name)));+}++bool NodeApiJsiRuntime::hasProperty(const jsi::Object &obj, const jsi::PropNameID &name) {+  return hasProperty(getNodeApiValue(obj), getNodeApiValue(name));+}++bool NodeApiJsiRuntime::hasProperty(const jsi::Object &obj, const jsi::String &name) {+  return hasProperty(getNodeApiValue(obj), getNodeApiValue(name));+}++void NodeApiJsiRuntime::setPropertyValue(const jsi::Object &obj, const jsi::PropNameID &name, const jsi::Value &value) {+  setProperty(getNodeApiValue(obj), getNodeApiValue(name), getNodeApiValue(value));+}++void NodeApiJsiRuntime::setPropertyValue(const jsi::Object &obj, const jsi::String &name, const jsi::Value &value) {+  setProperty(getNodeApiValue(obj), getNodeApiValue(name), getNodeApiValue(value));+}++bool NodeApiJsiRuntime::isArray(const jsi::Object &obj) const {+  return isArray(getNodeApiValue(obj));+}++bool NodeApiJsiRuntime::isArrayBuffer(const jsi::Object &obj) const {+  bool result{};+  CHECK_NAPI(nodeApi_->napi_is_arraybuffer(env_, getNodeApiValue(obj), &result));+  return result;+}++bool NodeApiJsiRuntime::isFunction(const jsi::Object &obj) const {+  return typeOf(getNodeApiValue(obj)) == napi_valuetype::napi_function;+}++bool NodeApiJsiRuntime::isHostObject(const jsi::Object &obj) const {+  napi_value hostObjectHolder = getProperty(getNodeApiValue(obj), getNodeApiValue(propertyId_.hostObjectSymbol));+  if (typeOf(hostObjectHolder) == napi_valuetype::napi_external) {+    return getExternalData(hostObjectHolder) != nullptr;+  } else {+    return false;+  }+}++bool NodeApiJsiRuntime::isHostFunction(const jsi::Function &func) const {+  napi_value hostFunctionHolder = getProperty(getNodeApiValue(func), getNodeApiValue(propertyId_.hostFunctionSymbol));+  if (typeOf(hostFunctionHolder) == napi_valuetype::napi_external) {+    return getExternalData(hostFunctionHolder) != nullptr;+  } else {+    return false;+  }+}++jsi::Array NodeApiJsiRuntime::getPropertyNames(const jsi::Object &obj) {+  napi_value properties;+  CHECK_NAPI(nodeApi_->napi_get_all_property_names(+      env_,+      getNodeApiValue(obj),+      napi_key_collection_mode::napi_key_include_prototypes,+      napi_key_filter(napi_key_enumerable | napi_key_skip_symbols),+      napi_key_numbers_to_strings,+      &properties));+  return makeJsiPointer<jsi::Object>(properties).asArray(*this);+}++jsi::WeakObject NodeApiJsiRuntime::createWeakObject(const jsi::Object &obj) {+  return make<jsi::WeakObject>(NodeApiRefCountedPointerValue::make(+      *const_cast<NodeApiJsiRuntime *>(this), getNodeApiValue(obj), NodeApiPointerValueKind::WeakObject));+}++jsi::Value NodeApiJsiRuntime::lockWeakObject(const jsi::WeakObject &weakObject) {+  napi_value value = getNodeApiValue(weakObject);+  if (value) {+    return toJsiValue(value);+  } else {+    return jsi::Value::undefined();+  }+}++jsi::Array NodeApiJsiRuntime::createArray(size_t length) {+  return makeJsiPointer<jsi::Object>(createNodeApiArray(length)).asArray(*this);+}++jsi::ArrayBuffer NodeApiJsiRuntime::createArrayBuffer(std::shared_ptr<jsi::MutableBuffer> buffer) {+  napi_value result{};+  void *data = buffer->data();+  size_t size = buffer->size();+  CHECK_NAPI(nodeApi_->napi_create_external_arraybuffer(+      env_,+      data,+      size,+      [](napi_env /*env*/, void * /*data*/, void *finalizeHint) {+        std::shared_ptr<jsi::MutableBuffer> buffer{+            std::move(*reinterpret_cast<std::shared_ptr<jsi::MutableBuffer> *>(finalizeHint))};+      },+      new std::shared_ptr<jsi::MutableBuffer>{std::move(buffer)},+      &result));+  return makeJsiPointer<jsi::Object>(result).getArrayBuffer(*this);+}++size_t NodeApiJsiRuntime::size(const jsi::Array &arr) {+  return getArrayLength(getNodeApiValue(arr));+}++size_t NodeApiJsiRuntime::size(const jsi::ArrayBuffer &arrBuf) {+  size_t result{};+  CHECK_NAPI(nodeApi_->napi_get_arraybuffer_info(env_, getNodeApiValue(arrBuf), nullptr, &result));+  return result;+}++uint8_t *NodeApiJsiRuntime::data(const jsi::ArrayBuffer &arrBuf) {+  uint8_t *result{};+  CHECK_NAPI(+      nodeApi_->napi_get_arraybuffer_info(env_, getNodeApiValue(arrBuf), reinterpret_cast<void **>(&result), nullptr));+  return result;+}++jsi::Value NodeApiJsiRuntime::getValueAtIndex(const jsi::Array &arr, size_t index) {+  return toJsiValue(getElement(getNodeApiValue(arr), index));+}++void NodeApiJsiRuntime::setValueAtIndexImpl(const jsi::Array &arr, size_t index, const jsi::Value &value) {+  setElement(getNodeApiValue(arr), static_cast<uint32_t>(index), getNodeApiValue(value));+}++jsi::Function NodeApiJsiRuntime::createFunctionFromHostFunction(+    const jsi::PropNameID &name,+    unsigned int paramCount,+    jsi::HostFunctionType func) {+  auto hostFunctionWrapper = std::make_unique<HostFunctionWrapper>(std::move(func), *this);+  napi_value function = createExternalFunction(+      getNodeApiValue(name), static_cast<int32_t>(paramCount), jsiHostFunctionCallback, hostFunctionWrapper.get());++  const napi_value hostFunctionHolder = createExternalObject(std::move(hostFunctionWrapper));+  setProperty(+      function,+      getNodeApiValue(propertyId_.hostFunctionSymbol),+      hostFunctionHolder,+      napi_property_attributes::napi_default);+  return makeJsiPointer<jsi::Object>(function).getFunction(*this);+}++jsi::Value+NodeApiJsiRuntime::call(const jsi::Function &func, const jsi::Value &jsThis, const jsi::Value *args, size_t count) {+  return toJsiValue(callFunction(+      getNodeApiValue(jsThis), getNodeApiValue(func), NodeApiValueArgs(*this, span<const jsi::Value>(args, count))));+}++jsi::Value NodeApiJsiRuntime::callAsConstructor(const jsi::Function &func, const jsi::Value *args, size_t count) {+  return toJsiValue(+      constructObject(getNodeApiValue(func), NodeApiValueArgs(*this, span<jsi::Value const>(args, count))));+}++jsi::Runtime::ScopeState *NodeApiJsiRuntime::pushScope() {+  napi_handle_scope result{};+  CHECK_NAPI(nodeApi_->napi_open_handle_scope(env_, &result));+  pushPointerValueScope();+  return reinterpret_cast<jsi::Runtime::ScopeState *>(result);+}++void NodeApiJsiRuntime::popScope(jsi::Runtime::ScopeState *state) {+  popPointerValueScope();+  CHECK_NAPI(nodeApi_->napi_close_handle_scope(env_, reinterpret_cast<napi_handle_scope>(state)));+}++bool NodeApiJsiRuntime::strictEquals(const jsi::Symbol &a, const jsi::Symbol &b) const {+  return strictEquals(getNodeApiValue(a), getNodeApiValue(b));+}++bool NodeApiJsiRuntime::strictEquals(const jsi::BigInt &a, const jsi::BigInt &b) const {+  return strictEquals(getNodeApiValue(a), getNodeApiValue(b));+}++bool NodeApiJsiRuntime::strictEquals(const jsi::String &a, const jsi::String &b) const {+  return strictEquals(getNodeApiValue(a), getNodeApiValue(b));+}++bool NodeApiJsiRuntime::strictEquals(const jsi::Object &a, const jsi::Object &b) const {+  return strictEquals(getNodeApiValue(a), getNodeApiValue(b));+}++bool NodeApiJsiRuntime::instanceOf(const jsi::Object &obj, const jsi::Function &func) {+  return instanceOf(getNodeApiValue(obj), getNodeApiValue(func));+}++//=====================================================================================================================+// NodeApiJsiRuntime::NodeApiScope implementation+//=====================================================================================================================++NodeApiJsiRuntime::NodeApiScope::NodeApiScope(NodeApiJsiRuntime &runtime) noexcept+    : runtime_(runtime), scopeState_(runtime_.pushScope()) {}++NodeApiJsiRuntime::NodeApiScope::~NodeApiScope() noexcept {+  runtime_.popScope(scopeState_);+}++//=====================================================================================================================+// NodeApiJsiRuntime::NodeApiPointerValueScope implementation+//=====================================================================================================================++NodeApiJsiRuntime::NodeApiPointerValueScope::NodeApiPointerValueScope(NodeApiJsiRuntime &runtime) noexcept+    : runtime_(runtime) {+  runtime_.pushPointerValueScope();+}++NodeApiJsiRuntime::NodeApiPointerValueScope::~NodeApiPointerValueScope() noexcept {+  runtime_.popPointerValueScope();+}++//=====================================================================================================================+// NodeApiJsiRuntime::AutoRestore implementation+//=====================================================================================================================++template <typename T>+NodeApiJsiRuntime::AutoRestore<T>::AutoRestore(T &varRef, T newValue)+    : varRef_{varRef}, oldValue_{std::exchange(varRef, newValue)} {}++template <typename T>+NodeApiJsiRuntime::AutoRestore<T>::~AutoRestore() {+  varRef_ = oldValue_;+}++//=====================================================================================================================+// NodeApiJsiRuntime::NodeApiStackOnlyPointerValue implementation+//=====================================================================================================================++NodeApiJsiRuntime::NodeApiStackOnlyPointerValue::NodeApiStackOnlyPointerValue(+    napi_value value,+    NodeApiPointerValueKind pointerKind) noexcept+    : value_(value), pointerKind_(pointerKind) {}++// Intentionally do nothing.+void NodeApiJsiRuntime::NodeApiStackOnlyPointerValue::invalidate() noexcept {}++NodeApiJsiRuntime::NodeApiRefCountedPointerValue *NodeApiJsiRuntime::NodeApiStackOnlyPointerValue::clone(+    NodeApiJsiRuntime &runtime) const {+  return NodeApiRefCountedPointerValue::make(runtime, value_, pointerKind_);+}++napi_value NodeApiJsiRuntime::NodeApiStackOnlyPointerValue::getValue(NodeApiJsiRuntime & /*runtime*/) noexcept {+  return value_;+}++NodeApiJsiRuntime::NodeApiPointerValueKind NodeApiJsiRuntime::NodeApiStackOnlyPointerValue::getKind() const noexcept {+  return pointerKind_;+}++//=====================================================================================================================+// NodeApiJsiRuntime::NodeApiPointerValue implementation+//=====================================================================================================================++NodeApiJsiRuntime::NodeApiRefCountedPointerValue::NodeApiRefCountedPointerValue(+    napi_value value,+    NodeApiJsiRuntime::NodeApiPointerValueKind pointerKind,+    int32_t initialRefCount) noexcept+    : value_(value), pointerKind_(pointerKind), refCount_(initialRefCount) {}++/*static*/ NodeApiJsiRuntime::NodeApiRefCountedPointerValue *NodeApiJsiRuntime::NodeApiRefCountedPointerValue::make(+    NodeApiJsiRuntime &runtime,+    napi_value value,+    NodeApiJsiRuntime::NodeApiPointerValueKind pointerKind,+    int32_t initialRefCount) {+  NodeApiRefCountedPointerValue *result = new NodeApiRefCountedPointerValue(value, pointerKind, initialRefCount);+  runtime.addStackValue(NodeApiStackValueHolder(result));+  return result;+}++/*static*/ NodeApiJsiRuntime::NodeApiRefCountedPointerValue *+NodeApiJsiRuntime::NodeApiRefCountedPointerValue::makeNodeApiRef(+    NodeApiJsiRuntime &runtime,+    napi_value value,+    NodeApiPointerValueKind pointerKind,+    int32_t initialRefCount) {+  return make(runtime, value, pointerKind, initialRefCount)->createNodeApiRef(runtime);+}++void NodeApiJsiRuntime::NodeApiRefCountedPointerValue::invalidate() noexcept {+  decRefCount();+}++NodeApiJsiRuntime::NodeApiRefCountedPointerValue *NodeApiJsiRuntime::NodeApiRefCountedPointerValue::clone(+    NodeApiJsiRuntime &runtime) const {+  incRefCount();+  return const_cast<NodeApiRefCountedPointerValue *>(this);+}++napi_value NodeApiJsiRuntime::NodeApiRefCountedPointerValue::getValue(NodeApiJsiRuntime &runtime) noexcept {+  if (value_ != nullptr) {+    return value_;+  }++  if (ref_ == nullptr) {+    return nullptr;+  }++  NodeApi *nodeApi = NodeApi::current();+  if (pointerKind_ == NodeApiPointerValueKind::Object || pointerKind_ == NodeApiPointerValueKind::WeakObject) {+    CHECK_NAPI_ELSE_CRASH(nodeApi->napi_get_reference_value(runtime.getEnv(), ref_, &value_));+  } else {+    napi_value obj{};+    CHECK_NAPI_ELSE_CRASH(nodeApi->napi_get_reference_value(runtime.getEnv(), ref_, &obj));+    CHECK_NAPI_ELSE_CRASH(nodeApi->napi_get_named_property(runtime.getEnv(), obj, kPrimitivePropertyName, &value_));+  }++  if (value_ != nullptr) {+    runtime.addStackValue(NodeApiStackValueHolder(this));+    incRefCount();+  }++  return value_;+}++NodeApiJsiRuntime::NodeApiPointerValueKind NodeApiJsiRuntime::NodeApiRefCountedPointerValue::getKind() const noexcept {+  return pointerKind_;+}++/*static*/ bool NodeApiJsiRuntime::NodeApiRefCountedPointerValue::usedByJsiPointer(+    NodeApiRefCountedPointerValue *ptr) noexcept {+  if (ptr == nullptr)+    return false;+  const int32_t internalRefCount = (ptr->value_ != nullptr ? 1 : 0) + (ptr->ref_ != nullptr ? 1 : 0);+  const int32_t refCount = ptr->refCount_.load(std::memory_order_acquire);+  return refCount > internalRefCount;+}++/*static*/ void NodeApiJsiRuntime::NodeApiRefCountedPointerValue::deleteStackValue(+    NodeApiRefCountedPointerValue *ptr) noexcept {+  if (ptr != nullptr && ptr->value_ != nullptr) {+    ptr->value_ = nullptr;+    ptr->decRefCount();+  }+}++void NodeApiJsiRuntime::NodeApiRefCountedPointerValue::convertToNodeApiRef(NodeApiJsiRuntime &runtime) noexcept {+  if (value_ == nullptr) {+    return;+  }++  if (ref_ == nullptr && usedByJsiPointer(this)) {+    createNodeApiRef(runtime);+    runtime.addRef(NodeApiRefHolder(this));+    value_ = nullptr;+    return;+  }++  value_ = nullptr;+  decRefCount();+  return;+}++/*static*/ void NodeApiJsiRuntime::NodeApiRefCountedPointerValue::deleteNodeApiRef(+    NodeApiRefCountedPointerValue *ptr) noexcept {+  if (ptr != nullptr && ptr->ref_ != nullptr) {+    ptr->ref_ = nullptr;+    ptr->decRefCount();+  }+}++/*static*/ void NodeApiJsiRuntime::NodeApiRefCountedPointerValue::deleteNodeApiRef(+    NodeApiRefCountedPointerValue *ptr,+    NodeApiJsiRuntime &runtime) noexcept {+  if (ptr != nullptr && ptr->ref_ != nullptr) {+    CHECK_NAPI_ELSE_CRASH(NodeApi::current()->napi_delete_reference(runtime.getEnv(), ptr->ref_));+    ptr->ref_ = nullptr;+    ptr->decRefCount();+  }+}++void NodeApiJsiRuntime::NodeApiRefCountedPointerValue::incRefCount() const noexcept {+  refCount_.fetch_add(1, std::memory_order_relaxed);+}++void NodeApiJsiRuntime::NodeApiRefCountedPointerValue::decRefCount() const noexcept {+  int32_t count = refCount_.fetch_sub(1, std::memory_order_release) - 1;+  if (count == 0) {+    std::atomic_thread_fence(std::memory_order_acquire);+    delete this;+  }+}++NodeApiJsiRuntime::NodeApiRefCountedPointerValue *NodeApiJsiRuntime::NodeApiRefCountedPointerValue::createNodeApiRef(+    NodeApiJsiRuntime &runtime) {+  NodeApi *nodeApi = NodeApi::current();+  CHECK_ELSE_CRASH(value_ != nullptr, "value_ must not be null");+  CHECK_ELSE_CRASH(ref_ == nullptr, "ref_ must be null");+  if (pointerKind_ == NodeApiPointerValueKind::Object) {+    CHECK_NAPI_ELSE_CRASH(nodeApi->napi_create_reference(runtime.getEnv(), value_, 1, &ref_));+  } else if (pointerKind_ != NodeApiPointerValueKind::WeakObject) {+    napi_value obj{};+    CHECK_NAPI_ELSE_CRASH(nodeApi->napi_create_object(runtime.getEnv(), &obj));+    CHECK_NAPI_ELSE_CRASH(nodeApi->napi_set_named_property(runtime.getEnv(), obj, kPrimitivePropertyName, value_));+    CHECK_NAPI_ELSE_CRASH(nodeApi->napi_create_reference(runtime.getEnv(), obj, 1, &ref_));+  } else {+    CHECK_NAPI_ELSE_CRASH(nodeApi->napi_create_reference(runtime.getEnv(), value_, 0, &ref_));+  }+  return this;+}++//=====================================================================================================================+// NodeApiJsiRuntime::SmallBuffer implementation+//=====================================================================================================================++template <typename T, size_t InplaceSize>+NodeApiJsiRuntime::SmallBuffer<T, InplaceSize>::SmallBuffer(size_t size) noexcept+    : size_{size}, heapData_{size_ > InplaceSize ? std::make_unique<T[]>(size_) : nullptr} {}++template <typename T, size_t InplaceSize>+T *NodeApiJsiRuntime::SmallBuffer<T, InplaceSize>::data() noexcept {+  return heapData_ ? heapData_.get() : stackData_.data();+}++template <typename T, size_t InplaceSize>+size_t NodeApiJsiRuntime::SmallBuffer<T, InplaceSize>::size() const noexcept {+  return size_;+}++//=====================================================================================================================+// NodeApiJsiRuntime::NodeApiValueArgs implementation+//=====================================================================================================================++NodeApiJsiRuntime::NodeApiValueArgs::NodeApiValueArgs(NodeApiJsiRuntime &runtime, span<const jsi::Value> args)+    : args_{args.size()} {+  napi_value *jsArgs = args_.data();+  for (size_t i = 0; i < args.size(); ++i) {+    jsArgs[i] = runtime.getNodeApiValue(args[i]);+  }+}++NodeApiJsiRuntime::NodeApiValueArgs::operator span<napi_value>() {+  return span<napi_value>{args_.data(), args_.size()};+}++//=====================================================================================================================+// NodeApiJsiRuntime::JsiValueView implementation+//=====================================================================================================================++NodeApiJsiRuntime::JsiValueView::JsiValueView(NodeApiJsiRuntime *runtime, napi_value jsValue)+    : value_{initValue(runtime, jsValue, std::addressof(pointerStore_))} {}++NodeApiJsiRuntime::JsiValueView::operator const jsi::Value &() const noexcept {+  return value_;+}++/*static*/ jsi::Value+NodeApiJsiRuntime::JsiValueView::initValue(NodeApiJsiRuntime *runtime, napi_value value, StoreType *store) {+  switch (runtime->typeOf(value)) {+    case napi_valuetype::napi_undefined:+      return jsi::Value::undefined();+    case napi_valuetype::napi_null:+      return jsi::Value::null();+    case napi_valuetype::napi_boolean:+      return jsi::Value{runtime->getValueBool(value)};+    case napi_valuetype::napi_number:+      return jsi::Value{runtime->getValueDouble(value)};+    case napi_valuetype::napi_string:+      return make<jsi::String>(new (store) NodeApiStackOnlyPointerValue(value, NodeApiPointerValueKind::String));+    case napi_valuetype::napi_symbol:+      return make<jsi::Symbol>(new (store) NodeApiStackOnlyPointerValue(value, NodeApiPointerValueKind::Symbol));+    case napi_valuetype::napi_object:+    case napi_valuetype::napi_function:+    case napi_valuetype::napi_external:+      return make<jsi::Object>(new (store) NodeApiStackOnlyPointerValue(value, NodeApiPointerValueKind::Object));+    case napi_valuetype::napi_bigint:+      return make<jsi::BigInt>(new (store) NodeApiStackOnlyPointerValue(value, NodeApiPointerValueKind::BigInt));+    default:+      throw jsi::JSINativeException("Unexpected value type");+  }+}++//=====================================================================================================================+// NodeApiJsiRuntime::JsiValueViewArgs implementation+//=====================================================================================================================++NodeApiJsiRuntime::JsiValueViewArgs::JsiValueViewArgs(NodeApiJsiRuntime *runtime, span<napi_value> args) noexcept+    : pointerStore_{args.size()}, args_{args.size()} {+  JsiValueView::StoreType *pointerStore = pointerStore_.data();+  jsi::Value *jsiArgs = args_.data();+  for (size_t i = 0; i < args_.size(); ++i) {+    jsiArgs[i] = JsiValueView::initValue(runtime, args[i], std::addressof(pointerStore[i]));+  }+}++jsi::Value const *NodeApiJsiRuntime::JsiValueViewArgs::data() noexcept {+  return args_.data();+}++size_t NodeApiJsiRuntime::JsiValueViewArgs::size() const noexcept {+  return args_.size();+}++//=====================================================================================================================+// NodeApiJsiRuntime::PropNameIDView implementation+//=====================================================================================================================++// TODO: account for symbol+NodeApiJsiRuntime::PropNameIDView::PropNameIDView(NodeApiJsiRuntime *runtime, napi_value propertyId) noexcept+    : propertyId_{make<jsi::PropNameID>(new(std::addressof(+          pointerStore_)) NodeApiStackOnlyPointerValue(propertyId, NodeApiPointerValueKind::StringPropNameID))} {}++NodeApiJsiRuntime::PropNameIDView::operator jsi::PropNameID const &() const noexcept {+  return propertyId_;+}++//=====================================================================================================================+// NodeApiJsiRuntime::HostFunctionWrapper implementation+//=====================================================================================================================++NodeApiJsiRuntime::HostFunctionWrapper::HostFunctionWrapper(jsi::HostFunctionType &&type, NodeApiJsiRuntime &runtime)+    : hostFunction_{std::move(type)}, runtime_{runtime} {}++jsi::HostFunctionType &NodeApiJsiRuntime::HostFunctionWrapper::hostFunction() noexcept {+  return hostFunction_;+}++NodeApiJsiRuntime &NodeApiJsiRuntime::HostFunctionWrapper::runtime() noexcept {+  return runtime_;+}++//=====================================================================================================================+// NodeApiJsiRuntime implementation+//=====================================================================================================================++template <typename... Args>+jsi::JSError NodeApiJsiRuntime::makeJSError(Args &&...args) {+  std::ostringstream errorStream;+  ((errorStream << std::forward<Args>(args)), ...);+  return jsi::JSError(*this, errorStream.str());+}++// Throws jsi::JSError or jsi::JSINativeException from NodeApi error.+[[noreturn]] void NodeApiJsiRuntime::throwJSException(napi_status status) const {+  napi_value jsError{};+  CHECK_NAPI_ELSE_CRASH(nodeApi_->napi_get_and_clear_last_exception(env_, &jsError));++  if (!hasPendingJSError_ &&+      (status == napi_pending_exception || instanceOf(jsError, getNodeApiValue(cachedValue_.Error)))) {+    AutoRestore<bool> setValue(const_cast<NodeApiJsiRuntime *>(this)->hasPendingJSError_, true);+    rewriteErrorMessage(jsError);+    throw jsi::JSError(*const_cast<NodeApiJsiRuntime *>(this), toJsiValue(jsError));+  } else {+    std::ostringstream errorStream;+    errorStream << "A call to NodeApi returned error code 0x" << std::hex << status << '.';+    throw jsi::JSINativeException(errorStream.str().c_str());+  }+}++// Throws jsi::JSINativeException with a message.+[[noreturn]] void NodeApiJsiRuntime::throwNativeException(char const *errorMessage) const {+  throw jsi::JSINativeException(errorMessage);+}++// Rewrites error messages to match the JSI unit test expectations.+void NodeApiJsiRuntime::rewriteErrorMessage(napi_value jsError) const {+  // The code below must work correctly even if the 'message' getter throws.+  // In case when it throws, we ignore that exception.+  napi_value message{};+  napi_status status = nodeApi_->napi_get_property(env_, jsError, getNodeApiValue(propertyId_.message), &message);+  if (status != napi_ok) {+    // If the 'message' property getter throws, then we clear the exception and ignore it.+    napi_value ignoreJSError{};+    nodeApi_->napi_get_and_clear_last_exception(env_, &ignoreJSError);+  } else if (typeOf(message) == napi_string) {+    // JSI unit tests expect V8- or JSC-like messages for the stack overflow.+    if (stringToStdString(message) == "Out of stack space") {+      setProperty(+          jsError,+          getNodeApiValue(propertyId_.message),+          createStringUtf8("RangeError : Maximum call stack size exceeded"sv));+    }+  }++  // Make sure that the call stack has the current URL+  if (!sourceURL_.empty()) {+    napi_value stack{};+    napi_status status = nodeApi_->napi_get_property(env_, jsError, getNodeApiValue(propertyId_.stack), &stack);+    if (status != napi_ok) {+      // If the 'stack' property getter throws, then we clear the exception and ignore it.+      napi_value ignoreJSError{};+      nodeApi_->napi_get_and_clear_last_exception(env_, &ignoreJSError);+    } else if (typeOf(message) == napi_string) {+      // JSI unit tests expect URL to be part of the call stack.+      std::string stackStr = stringToStdString(stack);+      if (stackStr.find(sourceURL_) == std::string::npos) {+        stackStr += sourceURL_ + '\n' + stackStr;+        setProperty(jsError, getNodeApiValue(propertyId_.stack), createStringUtf8(stackStr.c_str()));+      }+    }+  }+}++// Evaluates lambda and augments exception messages with the method's name.+template <typename TLambda>+auto NodeApiJsiRuntime::runInMethodContext(char const *methodName, TLambda lambda) {+  try {+    return lambda();+  } catch (jsi::JSError const &) {+    throw; // do not augment the JSError exceptions.+  } catch (std::exception const &ex) {+    throwNativeException((std::string{"Exception in "} + methodName + ": " + ex.what()).c_str());+  } catch (...) {+    throwNativeException((std::string{"Exception in "} + methodName + ": <unknown>").c_str());+  }+}++// Evaluates lambda and converts all exceptions to NAPI errors.+template <typename TLambda>+napi_value NodeApiJsiRuntime::handleCallbackExceptions(TLambda lambda) const noexcept {+  try {+    try {+      return lambda();+    } catch (jsi::JSError const &jsError) {+      // This block may throw exceptions+      setException(getNodeApiValue(jsError.value()));+    }+  } catch (std::exception const &ex) {+    setException(ex.what());+  } catch (...) {+    setException("Unexpected error");+  }++  return getUndefined();+}++// Throws JavaScript exception using NAPI.+bool NodeApiJsiRuntime::setException(napi_value error) const noexcept {+  // This method must not throw. We return false in case of error.+  return nodeApi_->napi_throw(env_, error) == napi_status::napi_ok;+}++// Throws JavaScript error exception with the provided message using NAPI.+bool NodeApiJsiRuntime::setException(std::string_view message) const noexcept {+  // This method must not throw. We return false in case of error.+  return nodeApi_->napi_throw_error(env_, "Unknown", message.data()) == napi_status::napi_ok;+}++// Gets type of the napi_value.+napi_valuetype NodeApiJsiRuntime::typeOf(napi_value value) const {+  napi_valuetype result{};+  CHECK_NAPI(nodeApi_->napi_typeof(env_, value, &result));+  return result;+}++// Returns true if two napi_values are strict equal per JavaScript rules.+bool NodeApiJsiRuntime::strictEquals(napi_value left, napi_value right) const {+  bool result{false};+  CHECK_NAPI(nodeApi_->napi_strict_equals(env_, left, right, &result));+  return result;+}++// Gets the napi_value for the JavaScript's undefined value.+napi_value NodeApiJsiRuntime::getUndefined() const {+  napi_value result{nullptr};+  CHECK_NAPI(nodeApi_->napi_get_undefined(env_, &result));+  return result;+}++// Gets the napi_value for the JavaScript's null value.+napi_value NodeApiJsiRuntime::getNull() const {+  napi_value result{};+  CHECK_NAPI(nodeApi_->napi_get_null(env_, &result));+  return result;+}++// Gets the napi_value for the JavaScript's global object.+napi_value NodeApiJsiRuntime::getGlobal() const {+  napi_value result{nullptr};+  CHECK_NAPI(nodeApi_->napi_get_global(env_, &result));+  return result;+}++// Gets the napi_value for the JavaScript's true and false values.+napi_value NodeApiJsiRuntime::getBoolean(bool value) const {+  napi_value result{nullptr};+  CHECK_NAPI(nodeApi_->napi_get_boolean(env_, value, &result));+  return result;+}++// Gets value of the Boolean napi_value.+bool NodeApiJsiRuntime::getValueBool(napi_value value) const {+  bool result{nullptr};+  CHECK_NAPI(nodeApi_->napi_get_value_bool(env_, value, &result));+  return result;+}++// Creates napi_value with an int32_t value.+napi_value NodeApiJsiRuntime::createInt32(int32_t value) const {+  napi_value result{};+  CHECK_NAPI(nodeApi_->napi_create_int32(env_, value, &result));+  return result;+}++// Creates napi_value with an int32_t value.+napi_value NodeApiJsiRuntime::createUInt32(uint32_t value) const {+  napi_value result{};+  CHECK_NAPI(nodeApi_->napi_create_uint32(env_, value, &result));+  return result;+}++// Creates napi_value with a double value.+napi_value NodeApiJsiRuntime::createDouble(double value) const {+  napi_value result{};+  CHECK_NAPI(nodeApi_->napi_create_double(env_, value, &result));+  return result;+}++// Gets value of the Double napi_value.+double NodeApiJsiRuntime::getValueDouble(napi_value value) const {+  double result{0};+  CHECK_NAPI(nodeApi_->napi_get_value_double(env_, value, &result));+  return result;+}++// Creates a napi_value string from the extended ASCII symbols that correspond to the Latin1 encoding.+// Each character is a byte-sized value from 0 to 255.+napi_value NodeApiJsiRuntime::createStringLatin1(std::string_view value) const {+  CHECK_ELSE_THROW(value.data(), "Cannot convert a nullptr to a JS string.");+  napi_value result{};+  CHECK_NAPI(nodeApi_->napi_create_string_latin1(env_, value.data(), value.size(), &result));+  return result;+}++// Creates a napi_value string from a UTF-8 string.+napi_value NodeApiJsiRuntime::createStringUtf8(std::string_view value) const {+  CHECK_ELSE_THROW(value.data(), "Cannot convert a nullptr to a JS string.");+  napi_value result{};+  CHECK_NAPI(nodeApi_->napi_create_string_utf8(env_, value.data(), value.size(), &result));+  return result;+}++// Creates a napi_value string from a UTF-8 string. Use data and length instead of string_view.+napi_value NodeApiJsiRuntime::createStringUtf8(const uint8_t *data, size_t length) const {+  return createStringUtf8({reinterpret_cast<const char *>(data), length});+}++// Gets std::string from the napi_value string.+std::string NodeApiJsiRuntime::stringToStdString(napi_value stringValue) const {+  std::string result;+  CHECK_ELSE_THROW(+      typeOf(stringValue) == napi_valuetype::napi_string,+      "Cannot convert a non JS string NodeApi Value to a std::string.");+  size_t strLength{};+  CHECK_NAPI(nodeApi_->napi_get_value_string_utf8(env_, stringValue, nullptr, 0, &strLength));+  result.assign(strLength, '\0');+  size_t copiedLength{};+  CHECK_NAPI(nodeApi_->napi_get_value_string_utf8(env_, stringValue, &result[0], result.length() + 1, &copiedLength));+  CHECK_ELSE_THROW(result.length() == copiedLength, "Unexpected string length");+  return result;+}++// Gets or creates a unique string value from an UTF-8 string_view.+napi_value NodeApiJsiRuntime::getPropertyIdFromName(std::string_view value) const {+  napi_value result{};+  CHECK_NAPI(nodeApi_->napi_create_string_utf8(env_, value.data(), value.size(), &result));+  return result;+}++// Gets or creates a unique string value from an UTF-8 data/length range.+napi_value NodeApiJsiRuntime::getPropertyIdFromName(const uint8_t *data, size_t length) const {+  return getPropertyIdFromName({reinterpret_cast<const char *>(data), length});+}++// Gets or creates a unique string value from napi_value string.+napi_value NodeApiJsiRuntime::getPropertyIdFromName(napi_value str) const {+  return str;+}++// Gets or creates a unique string value from napi_value symbol.+napi_value NodeApiJsiRuntime::getPropertyIdFromSymbol(napi_value sym) const {+  return sym;+}++// Converts property id value to std::string.+std::string NodeApiJsiRuntime::propertyIdToStdString(napi_value propertyId) {+  if (typeOf(propertyId) == napi_symbol) {+    return symbolToStdString(propertyId);+  }++  return stringToStdString(propertyId);+}++// Creates a JavaScript symbol napi_value.+napi_value NodeApiJsiRuntime::createSymbol(std::string_view symbolDescription) const {+  napi_value result{};+  napi_value description = createStringUtf8(symbolDescription);+  CHECK_NAPI(nodeApi_->napi_create_symbol(env_, description, &result));+  return result;+}++// Calls Symbol.toString() and returns it as std::string.+std::string NodeApiJsiRuntime::symbolToStdString(napi_value symbolValue) {+  if (!cachedValue_.SymbolToString) {+    napi_value symbolCtor = getProperty(getNodeApiValue(cachedValue_.Global), getNodeApiValue(propertyId_.Symbol));+    napi_value symbolPrototype = getProperty(symbolCtor, getNodeApiValue(propertyId_.prototype));+    cachedValue_.SymbolToString = makeNodeApiRef(+        getProperty(symbolPrototype, getNodeApiValue(propertyId_.toString)), NodeApiPointerValueKind::Object);+  }+  napi_value jsString = callFunction(symbolValue, getNodeApiValue(cachedValue_.SymbolToString), {});+  return stringToStdString(jsString);+}++// Calls a JavaScript function.+napi_value NodeApiJsiRuntime::callFunction(napi_value thisArg, napi_value function, span<napi_value> args) const {+  napi_value result{};+  CHECK_NAPI(nodeApi_->napi_call_function(env_, thisArg, function, args.size(), args.begin(), &result));+  return result;+}++// Constructs a new JavaScript Object using a constructor function.+napi_value NodeApiJsiRuntime::constructObject(napi_value constructor, span<napi_value> args) const {+  napi_value result{};+  CHECK_NAPI(nodeApi_->napi_new_instance(env_, constructor, args.size(), args.begin(), &result));+  return result;+}++// Returns true if object was constructed using the provided constructor.+bool NodeApiJsiRuntime::instanceOf(napi_value object, napi_value constructor) const {+  bool result{false};+  CHECK_NAPI(nodeApi_->napi_instanceof(env_, object, constructor, &result));+  return result;+}++// Creates new JavaScript Object.+napi_value NodeApiJsiRuntime::createNodeApiObject() const {+  napi_value result{};+  CHECK_NAPI(nodeApi_->napi_create_object(env_, &result));+  return result;+}++// Returns true if the object has a property with the provided property ID.+bool NodeApiJsiRuntime::hasProperty(napi_value object, napi_value propertyId) const {+  bool result{};+  CHECK_NAPI(nodeApi_->napi_has_property(env_, object, propertyId, &result));+  return result;+}++// Gets object property value.+napi_value NodeApiJsiRuntime::getProperty(napi_value object, napi_value propertyId) const {+  napi_value result{};+  CHECK_NAPI(nodeApi_->napi_get_property(env_, object, propertyId, &result));+  return result;+}++// Sets object property value.+void NodeApiJsiRuntime::setProperty(napi_value object, napi_value propertyId, napi_value value) const {+  CHECK_NAPI(nodeApi_->napi_set_property(env_, object, propertyId, value));+}++// Sets object property value with the provided property accessibility attributes.+void NodeApiJsiRuntime::setProperty(+    napi_value object,+    napi_value propertyId,+    napi_value value,+    napi_property_attributes attrs) const {+  napi_property_descriptor descriptor{};+  descriptor.name = propertyId;+  descriptor.value = value;+  descriptor.attributes = attrs;+  CHECK_NAPI(nodeApi_->napi_define_properties(env_, object, 1, &descriptor));+}++// Creates a new JavaScript Array with the provided length.+napi_value NodeApiJsiRuntime::createNodeApiArray(size_t length) const {+  napi_value result{};+  CHECK_NAPI(nodeApi_->napi_create_array_with_length(env_, length, &result));+  return result;+}++bool NodeApiJsiRuntime::isArray(napi_value value) const {+  bool result{};+  CHECK_NAPI(nodeApi_->napi_is_array(env_, value, &result));+  return result;+}++size_t NodeApiJsiRuntime::getArrayLength(napi_value value) const {+  uint32_t result{};+  CHECK_NAPI(nodeApi_->napi_get_array_length(env_, value, &result));+  return result;+}++napi_value NodeApiJsiRuntime::getElement(napi_value arr, size_t index) const {+  napi_value result{};+  CHECK_NAPI(nodeApi_->napi_get_element(env_, arr, static_cast<int32_t>(index), &result));+  return result;+}++// Sets array element.+void NodeApiJsiRuntime::setElement(napi_value array, uint32_t index, napi_value value) const {+  CHECK_NAPI(nodeApi_->napi_set_element(env_, array, index, value));+}++// The NAPI external function callback used for the JSI host function implementation.+/*static*/ napi_value __cdecl NodeApiJsiRuntime::jsiHostFunctionCallback(+    napi_env env,+    napi_callback_info info) noexcept {+  HostFunctionWrapper *hostFuncWrapper{};+  size_t argc{};+  CHECK_NAPI_ELSE_CRASH(NodeApi::current()->napi_get_cb_info(+      env, info, &argc, nullptr, nullptr, reinterpret_cast<void **>(&hostFuncWrapper)));+  CHECK_ELSE_CRASH(hostFuncWrapper, "Cannot find the host function");+  NodeApiJsiRuntime &runtime = hostFuncWrapper->runtime();+  NodeApiPointerValueScope scope{runtime};++  return runtime.handleCallbackExceptions([&env, &info, &argc, &runtime, &hostFuncWrapper]() {+    SmallBuffer<napi_value, MaxStackArgCount> napiArgs(argc);+    napi_value thisArg{};+    CHECK_NAPI_ELSE_CRASH(NodeApi::current()->napi_get_cb_info(env, info, &argc, napiArgs.data(), &thisArg, nullptr));+    CHECK_ELSE_CRASH(napiArgs.size() == argc, "Wrong argument count");+    const JsiValueView jsiThisArg{&runtime, thisArg};+    JsiValueViewArgs jsiArgs(&runtime, span<napi_value>(napiArgs.data(), napiArgs.size()));++    const jsi::HostFunctionType &hostFunc = hostFuncWrapper->hostFunction();+    return runtime.runInMethodContext("HostFunction", [&hostFunc, &runtime, &jsiThisArg, &jsiArgs]() {+      return runtime.getNodeApiValue(hostFunc(runtime, jsiThisArg, jsiArgs.data(), jsiArgs.size()));+    });+  });+}++// Creates an external function.+napi_value NodeApiJsiRuntime::createExternalFunction(+    napi_value name,+    int32_t paramCount,+    napi_callback callback,+    void *callbackData) {+  std::string funcName = stringToStdString(name);+  napi_value function{};+  CHECK_NAPI(+      nodeApi_->napi_create_function(env_, funcName.data(), funcName.length(), callback, callbackData, &function));+  setProperty(+      function, getNodeApiValue(propertyId_.length), createInt32(paramCount), napi_property_attributes::napi_default);++  return function;+}++// Creates an object that wraps up external data.+napi_value NodeApiJsiRuntime::createExternalObject(void *data, napi_finalize finalizeCallback) const {+  napi_value result{};+  CHECK_NAPI(nodeApi_->napi_create_external(env_, data, finalizeCallback, nullptr, &result));+  return result;+}++// Wraps up std::unique_ptr as an external object.+template <typename T>+napi_value NodeApiJsiRuntime::createExternalObject(std::unique_ptr<T> &&data) const {+  napi_finalize finalize = [](napi_env /*env*/, void *dataToDestroy, void * /*finalizerHint*/) {+    // We wrap dataToDestroy in a unique_ptr to avoid calling delete explicitly.+    std::unique_ptr<T> dataDeleter{static_cast<T *>(dataToDestroy)};+  };+  napi_value object = createExternalObject(data.get(), finalize);++  // We only call data.release() after the createExternalObject succeeds.+  // Otherwise, when createExternalObject fails and an exception is thrown,+  // the memory that data used to own will be leaked.+  data.release();+  return object;+}++// Gets external data wrapped by an external object.+void *NodeApiJsiRuntime::getExternalData(napi_value object) const {+  void *result{};+  CHECK_NAPI(nodeApi_->napi_get_value_external(env_, object, &result));+  return result;+}++// Gets JSI host object wrapped into a napi_value object.+const std::shared_ptr<jsi::HostObject> &NodeApiJsiRuntime::getJsiHostObject(napi_value obj) {+  const napi_value hostObjectHolder = getProperty(obj, getNodeApiValue(propertyId_.hostObjectSymbol));++  if (typeOf(hostObjectHolder) == napi_valuetype::napi_external) {+    if (void *data = getExternalData(hostObjectHolder)) {+      return *static_cast<std::shared_ptr<jsi::HostObject> *>(data);+    }+  }++  throw jsi::JSINativeException("Cannot get HostObjects.");+}++// Gets cached or creates Proxy handler to implement the JSI host object.+napi_value NodeApiJsiRuntime::getHostObjectProxyHandler() {+  if (!cachedValue_.HostObjectProxyHandler) {+    const napi_value handler = createNodeApiObject();+    setProxyTrap<&NodeApiJsiRuntime::hostObjectHasTrap, 2>(handler, getNodeApiValue(propertyId_.has));+    setProxyTrap<&NodeApiJsiRuntime::hostObjectGetTrap, 3>(handler, getNodeApiValue(propertyId_.get));+    setProxyTrap<&NodeApiJsiRuntime::hostObjectSetTrap, 4>(handler, getNodeApiValue(propertyId_.set));+    setProxyTrap<&NodeApiJsiRuntime::hostObjectOwnKeysTrap, 1>(handler, getNodeApiValue(propertyId_.ownKeys));+    setProxyTrap<&NodeApiJsiRuntime::hostObjectGetOwnPropertyDescriptorTrap, 2>(+        handler, getNodeApiValue(propertyId_.getOwnPropertyDescriptor));+    cachedValue_.HostObjectProxyHandler = makeNodeApiRef(handler, NodeApiPointerValueKind::Object);+  }++  return getNodeApiValue(cachedValue_.HostObjectProxyHandler);+}++// Sets Proxy trap method as a pointer to NodeApiJsiRuntime instance method.+template <napi_value (NodeApiJsiRuntime::*trapMethod)(span<napi_value>), size_t argCount>+void NodeApiJsiRuntime::setProxyTrap(napi_value handler, napi_value propertyName) {+  napi_callback proxyTrap = [](napi_env env, napi_callback_info info) noexcept {+    NodeApiJsiRuntime *runtime{};+    napi_value args[argCount]{};+    size_t actualArgCount{argCount};+    CHECK_NAPI_ELSE_CRASH(NodeApi::current()->napi_get_cb_info(+        env, info, &actualArgCount, args, nullptr, reinterpret_cast<void **>(&runtime)));+    CHECK_ELSE_CRASH(actualArgCount == argCount, "proxy trap requires argCount arguments.");+    NodeApiPointerValueScope scope{*runtime};+    return runtime->handleCallbackExceptions(+        [&runtime, &args]() { return (runtime->*trapMethod)(span<napi_value>(args, argCount)); });+  };++  setProperty(handler, propertyName, createExternalFunction(propertyName, argCount, proxyTrap, this));+}++// The host object Proxy 'has' trap implementation.+napi_value NodeApiJsiRuntime::hostObjectHasTrap(span<napi_value> args) {+  // args[0] - the Proxy target object.+  // args[1] - the name of the property to check.+  napi_value propertyName = args[1];+  const auto &hostObject = getJsiHostObject(args[0]);+  return runInMethodContext("HostObject::has", [&hostObject, &propertyName, this]() {+    // std::vector<jsi::PropNameID> ownKeys = hostObject->getPropertyNames(*this);+    // for (jsi::PropNameID &ownKey : ownKeys) {+    //   if (strictEquals(propertyName, getNodeApiValue(ownKey))) {+    return getBoolean(true);+    //   }+    // }+    // return getBoolean(false);+  });+}++// The host object Proxy 'get' trap implementation.+napi_value NodeApiJsiRuntime::hostObjectGetTrap(span<napi_value> args) {+  // args[0] - the Proxy target object.+  // args[1] - the name of the property to set.+  // args[2] - the Proxy object (unused).+  napi_value target = args[0];+  napi_value propertyName = args[1];+  bool isTargetOwnProp{};+  CHECK_NAPI(nodeApi_->napi_has_own_property(env_, target, propertyName, &isTargetOwnProp));+  if (isTargetOwnProp) {+    return getProperty(target, propertyName);+  }+  const auto &hostObject = getJsiHostObject(args[0]);+  PropNameIDView propertyId{this, propertyName};+  return runInMethodContext("HostObject::get", [&hostObject, &propertyId, this]() {+    return getNodeApiValue(hostObject->get(*this, propertyId));+  });+}++// The host object Proxy 'set' trap implementation.+napi_value NodeApiJsiRuntime::hostObjectSetTrap(span<napi_value> args) {+  // args[0] - the Proxy target object.+  // args[1] - the name of the property to set.+  // args[2] - the new value of the property to set.+  // args[3] - the Proxy object (unused).+  const auto &hostObject = getJsiHostObject(args[0]);+  PropNameIDView propertyId{this, args[1]};+  JsiValueView value{this, args[2]};+  runInMethodContext(+      "HostObject::set", [&hostObject, &propertyId, &value, this]() { hostObject->set(*this, propertyId, value); });+  return getUndefined();+}++// The host object Proxy 'ownKeys' trap implementation.+napi_value NodeApiJsiRuntime::hostObjectOwnKeysTrap(span<napi_value> args) {+  // args[0] - the Proxy target object.+  napi_value target = args[0];++  napi_value targetOwnKeys{};+  CHECK_NAPI(nodeApi_->napi_get_all_property_names(+      env_, target, napi_key_own_only, napi_key_all_properties, napi_key_numbers_to_strings, &targetOwnKeys));+  CHECK_ELSE_THROW(isArray(targetOwnKeys), "Expected an array");+  size_t targetOwnKeysLength = getArrayLength(targetOwnKeys);++  const auto &hostObject = getJsiHostObject(target);+  std::vector<jsi::PropNameID> hostOwnKeys = runInMethodContext(+      "HostObject::getPropertyNames", [&hostObject, this]() { return hostObject->getPropertyNames(*this); });++  std::vector<jsi::PropNameID> ownKeys;+  std::unordered_set<const PointerValue *> uniqueOwnKeys;+  // Minus one hostObjectSymbol key.+  ownKeys.reserve(targetOwnKeysLength - 1 + hostOwnKeys.size());+  uniqueOwnKeys.reserve(targetOwnKeysLength - 1 + hostOwnKeys.size());++  // Read all target own keys.+  if (targetOwnKeysLength > 1) {+    auto addPropNameId = [this, &uniqueOwnKeys, &ownKeys](jsi::PropNameID &&propNameId) {+      const PointerValue *pv = getPointerValue(propNameId);+      auto inserted = uniqueOwnKeys.insert(pv);+      CHECK_ELSE_THROW(inserted.second, "Target has non-unique keys");+      ownKeys.push_back(std::move(propNameId));+    };+    for (size_t i = 0; i < targetOwnKeysLength; ++i) {+      napi_value key = getElement(targetOwnKeys, i);+      napi_valuetype keyType = typeOf(key);+      if (keyType == napi_string) {+        addPropNameId(createPropNameIDFromString(makeJsiPointer<jsi::String>(key)));+      } else if (keyType == napi_symbol) {+        if (strictEquals(key, getNodeApiValue(propertyId_.hostObjectSymbol))) {+          continue;+        }+        addPropNameId(createPropNameIDFromSymbol(makeJsiPointer<jsi::Symbol>(key)));+      } else {+        throwNativeException("Unexpected key type");+      }+    }+  }++  // Read all unique host own keys.+  for (jsi::PropNameID &propNameId : hostOwnKeys) {+    const PointerValue *pv = getPointerValue(propNameId);+    auto inserted = uniqueOwnKeys.insert(pv);+    if (inserted.second) {+      ownKeys.push_back(std::move(propNameId));+    }+  }++  // Put indexed properties before named ones.+  struct Index {+    uint32_t index;+    napi_value value;+  };+  std::vector<Index> indexKeys;+  std::vector<napi_value> nonIndexKeys;+  nonIndexKeys.reserve(ownKeys.size());+  for (const jsi::PropNameID &key : ownKeys) {+    napi_value napiKey = getNodeApiValue(key);+    napi_valuetype valueType = typeOf(napiKey);+    if (valueType == napi_string) {+      std::string keyStr = stringToStdString(napiKey);+      std::optional<uint32_t> indexKey = toArrayIndex(keyStr.begin(), keyStr.end());+      if (indexKey.has_value()) {+        indexKeys.push_back(Index{indexKey.value(), napiKey});+        continue;+      }+    }+    nonIndexKeys.push_back(napiKey);+  }++  std::sort(indexKeys.begin(), indexKeys.end(), [](const Index &left, const Index &right) {+    return left.index < right.index;+  });++  napi_value ownKeyArray = createNodeApiArray(0);+  uint32_t index = 0;+  for (const Index &indexKey : indexKeys) {+    setElement(ownKeyArray, index++, indexKey.value);+  }+  for (napi_value napiKey : nonIndexKeys) {+    setElement(ownKeyArray, index++, napiKey);+  }++  return ownKeyArray;+}++// The host object Proxy 'getOwnPropertyDescriptor' trap implementation.+napi_value NodeApiJsiRuntime::hostObjectGetOwnPropertyDescriptorTrap(span<napi_value> args) {+  // args[0] - the Proxy target object.+  // args[1] - the property+  const auto &hostObject = getJsiHostObject(args[0]);+  PropNameIDView propertyId{this, args[1]};++  return runInMethodContext("HostObject::getOwnPropertyDescriptor", [&hostObject, &propertyId, this]() {+    auto getPropDescriptor = [](napi_value name, napi_value value) {+      return napi_property_descriptor{+          nullptr, name, nullptr, nullptr, nullptr, value, napi_default_jsproperty, nullptr};+    };+    napi_value trueValue = getBoolean(true);+    napi_property_descriptor properties[]{+        getPropDescriptor(getNodeApiValue(propertyId_.value), getNodeApiValue(hostObject->get(*this, propertyId))),+        getPropDescriptor(getNodeApiValue(propertyId_.writable), trueValue),+        getPropDescriptor(getNodeApiValue(propertyId_.enumerable), trueValue),+        getPropDescriptor(getNodeApiValue(propertyId_.configurable), trueValue)};+    napi_value descriptor = createNodeApiObject();+    CHECK_NAPI(nodeApi_->napi_define_properties(env_, descriptor, std::size(properties), properties));+    return descriptor;+  });+}++// Converts jsi::Bufer to span.+span<const uint8_t> NodeApiJsiRuntime::toSpan(const jsi::Buffer &buffer) {+  return span<const uint8_t>(buffer.data(), buffer.size());+}++// Creates jsi::Value from napi_value.+jsi::Value NodeApiJsiRuntime::toJsiValue(napi_value value) const {+  switch (typeOf(value)) {+    case napi_valuetype::napi_undefined:+      return jsi::Value::undefined();+    case napi_valuetype::napi_null:+      return jsi::Value::null();+    case napi_valuetype::napi_boolean:+      return jsi::Value{getValueBool(value)};+    case napi_valuetype::napi_number:+      return jsi::Value{getValueDouble(value)};+    case napi_valuetype::napi_string:+      return jsi::Value{makeJsiPointer<jsi::String>(value)};+    case napi_valuetype::napi_symbol:+      return jsi::Value{makeJsiPointer<jsi::Symbol>(value)};+    case napi_valuetype::napi_object:+    case napi_valuetype::napi_function:+    case napi_valuetype::napi_external:+      return jsi::Value{makeJsiPointer<jsi::Object>(value)};+    case napi_valuetype::napi_bigint:+      return jsi::Value{makeJsiPointer<jsi::BigInt>(value)};+    default:+      throw jsi::JSINativeException("Unexpected value type");+  }+}++napi_value NodeApiJsiRuntime::getNodeApiValue(const jsi::Value &value) const {+  if (value.isUndefined()) {+    return getUndefined();+  } else if (value.isNull()) {+    return getNull();+  } else if (value.isBool()) {+    return getBoolean(value.getBool());+  } else if (value.isNumber()) {+    return createDouble(value.getNumber());+  } else if (value.isSymbol()) {+    return getNodeApiValue(value.getSymbol(*const_cast<NodeApiJsiRuntime *>(this)));+  } else if (value.isString()) {+    return getNodeApiValue(value.getString(*const_cast<NodeApiJsiRuntime *>(this)));+  } else if (value.isObject()) {+    return getNodeApiValue(value.getObject(*const_cast<NodeApiJsiRuntime *>(this)));+  } else if (value.isBigInt()) {+    return getNodeApiValue(value.getBigInt(*const_cast<NodeApiJsiRuntime *>(this)));+  } else {+    throw jsi::JSINativeException("Unexpected jsi::Value type");+  }+}++napi_value NodeApiJsiRuntime::getNodeApiValue(const jsi::Pointer &ptr) const {+  return const_cast<NodeApiPointerValue *>(static_cast<const NodeApiPointerValue *>(getPointerValue(ptr)))+      ->getValue(*const_cast<NodeApiJsiRuntime *>(this));+}++napi_value NodeApiJsiRuntime::getNodeApiValue(const NodeApiRefHolder &ref) const {+  return ref->getValue(*const_cast<NodeApiJsiRuntime *>(this));+}++NodeApiJsiRuntime::NodeApiRefCountedPointerValue *NodeApiJsiRuntime::cloneNodeApiPointerValue(+    const PointerValue *pointerValue) {+  return static_cast<const NodeApiPointerValue *>(pointerValue)->clone(*this);+}++// Adopted from Hermes code.+std::optional<uint32_t> NodeApiJsiRuntime::toArrayIndex(+    std::string::const_iterator first,+    std::string::const_iterator last) {+  // Empty string is invalid.+  if (first == last)+    return std::nullopt;++  // Leading 0 is special.+  if (*first == '0') {+    ++first;+    // Just "0"?+    if (first == last)+      return 0;+    // Leading 0 is invalid otherwise.+    return std::nullopt;+  }++  uint32_t res = 0;+  do {+    auto ch = *first;+    if (ch < '0' || ch > '9')+      return std::nullopt;+    uint64_t tmp = (uint64_t)res * 10 + (ch - '0');+    // Check for overflow.+    if (tmp & ((uint64_t)0xFFFFFFFFu << 32))+      return std::nullopt;+    res = (uint32_t)tmp;+  } while (++first != last);++  // 0xFFFFFFFF is not a valid array index.+  if (res == 0xFFFFFFFFu)+    return std::nullopt;++  return res;+}++template <typename T, std::enable_if_t<std::is_same_v<jsi::Object, T>, int>>+T NodeApiJsiRuntime::makeJsiPointer(napi_value value) const {+  return make<T>(NodeApiRefCountedPointerValue::make(+      *const_cast<NodeApiJsiRuntime *>(this), value, NodeApiPointerValueKind::Object));+}++template <typename T, std::enable_if_t<std::is_same_v<jsi::String, T>, int>>+T NodeApiJsiRuntime::makeJsiPointer(napi_value value) const {+  return make<T>(NodeApiRefCountedPointerValue::make(+      *const_cast<NodeApiJsiRuntime *>(this), value, NodeApiPointerValueKind::String));+}++template <typename T, std::enable_if_t<std::is_same_v<jsi::Symbol, T>, int>>+T NodeApiJsiRuntime::makeJsiPointer(napi_value value) const {+  return make<T>(NodeApiRefCountedPointerValue::make(+      *const_cast<NodeApiJsiRuntime *>(this), value, NodeApiPointerValueKind::Symbol));+}++template <typename T, std::enable_if_t<std::is_same_v<jsi::BigInt, T>, int>>+T NodeApiJsiRuntime::makeJsiPointer(napi_value value) const {+  return make<T>(NodeApiRefCountedPointerValue::make(+      *const_cast<NodeApiJsiRuntime *>(this), value, NodeApiPointerValueKind::BigInt));+}++template <+    typename TTo,+    typename TFrom,+    std::enable_if_t<std::is_base_of_v<jsi::Pointer, TTo>, int>,+    std::enable_if_t<std::is_base_of_v<jsi::Pointer, TFrom>, int>>+TTo NodeApiJsiRuntime::cloneAs(const TFrom &pointer) const {+  return make<TTo>(static_cast<const NodeApiRefCountedPointerValue *>(getPointerValue(pointer))+                       ->clone(*const_cast<NodeApiJsiRuntime *>(this)));+}++NodeApiJsiRuntime::NodeApiRefHolder NodeApiJsiRuntime::makeNodeApiRef(+    napi_value value,+    NodeApiJsiRuntime::NodeApiPointerValueKind pointerKind,+    int32_t initialRefCount) {+  return NodeApiRefHolder(NodeApiRefCountedPointerValue::makeNodeApiRef(*this, value, pointerKind, initialRefCount));+}++void NodeApiJsiRuntime::addStackValue(NodeApiStackValueHolder &&pointerHolder) {+  if (stackValues_.size() == stackValues_.capacity()) {+    collectUnusedStackValues();+  }+  stackValues_.push_back(std::move(pointerHolder));+}++void NodeApiJsiRuntime::addRef(NodeApiRefHolder &&refHolder) {+  if (refs_.size() == refs_.capacity()) {+    collectUnusedRefs();+  }+  refs_.push_back(std::move(refHolder));+}++void NodeApiJsiRuntime::pushPointerValueScope() noexcept {+  stackScopes_.push_back(stackValues_.size());+}++void NodeApiJsiRuntime::popPointerValueScope() noexcept {+  CHECK_ELSE_CRASH(!stackScopes_.empty(), "There are no scopes to pop");++  size_t newStackSize = stackScopes_.back();+  auto beginIterator = stackValues_.begin() + newStackSize;+  stackScopes_.pop_back();+  std::for_each(beginIterator, stackValues_.end(), [this](NodeApiStackValueHolder &holder) {+    holder->convertToNodeApiRef(*this);+  });+  stackValues_.resize(newStackSize);+}++void NodeApiJsiRuntime::collectUnusedStackValues() {+  auto usedByJsiPointer = [](NodeApiStackValueHolder &holder) {+    return NodeApiRefCountedPointerValue::usedByJsiPointer(holder.get());+  };+  auto beginIterator = stackValues_.begin();+  for (size_t &scope : stackScopes_) {+    auto endIterator = stackValues_.begin() + scope;+    beginIterator = std::partition(beginIterator, endIterator, usedByJsiPointer);+    scope -= endIterator - beginIterator;+  }+  beginIterator = std::partition(beginIterator, stackValues_.end(), usedByJsiPointer);+  stackValues_.resize(beginIterator - stackValues_.begin());+}++void NodeApiJsiRuntime::collectUnusedRefs() noexcept {+  auto usedByJsiPointer = [](NodeApiRefHolder &holder) {+    return NodeApiRefCountedPointerValue::usedByJsiPointer(holder.get());+  };+  auto beginIterator = std::partition(refs_.begin(), refs_.end(), usedByJsiPointer);+  std::for_each(beginIterator, refs_.end(), [this](NodeApiRefHolder &holder) {+    NodeApiRefCountedPointerValue::deleteNodeApiRef(holder.get(), *this);+  });+  refs_.resize(beginIterator - refs_.begin());+}++} // namespace++std::unique_ptr<jsi::Runtime>+makeNodeApiJsiRuntime(napi_env env, NodeApi *nodeApi, std::function<void()> onDelete) noexcept {+  return std::make_unique<NodeApiJsiRuntime>(env, nodeApi, std::move(onDelete));+}++} // namespace Microsoft::NodeApiJsi++EXTERN_C_START++// Default implementation of napi_ext_get_description if it is not provided by JS engine.+// It returns "NodeApiJsiRuntime" string.+napi_status NAPI_CDECL default_napi_ext_get_description(napi_env /*env*/, char *buf, size_t bufsize, size_t *result) {+  constexpr const char description[] = "NodeApiJsiRuntime";+  const size_t len = sizeof(description) - 1;+  if (buf == nullptr) {+    if (result == nullptr) {+      return napi_invalid_arg;+    }+    *result = len;+  } else if (bufsize > 0) {+    const size_t copied = std::min(bufsize - 1, len);+    std::char_traits<char>::copy(buf, description, std::min(bufsize - 1, len));+    buf[copied] = '\0';+    if (result != nullptr) {+      *result = copied;+    }+  } else if (result != nullptr) {+    *result = 0;+  }+  return napi_ok;+}++// Default implementation of napi_ext_drain_microtasks if it is not provided by JS engine.+// It does nothing+napi_status NAPI_CDECL default_napi_ext_drain_microtasks(napi_env /*env*/, int32_t /*max_count_hint*/, bool *result) {+  if (result != nullptr) {+    *result = true; // All tasks are drained+  }+  return napi_ok;+}++// Default implementation of napi_ext_is_inspectable if it is not provided by JS engine.+// It always returns false.+napi_status NAPI_CDECL default_napi_ext_is_inspectable(napi_env /*env*/, bool *result) {+  if (result != nullptr) {+    *result = false;+  }+  return napi_ok;+}++// TODO: Ensure that we either load all three functions or use their default versions and never mix and match.++// Default implementation of napi_ext_create_prepared_script if it is not provided by JS engine.+// It return napi_ref as a napi_ext_prepared_script that wraps up an object with a "script" property string.+napi_status NAPI_CDECL default_napi_ext_create_prepared_script(+    napi_env env,+    uint8_t *script_data,+    size_t script_length,+    napi_finalize finalize_cb,+    void *finalize_hint,+    const char * /*source_url*/,+    napi_ext_prepared_script *result) {+  Microsoft::NodeApiJsi::NodeApi *nodeApi = Microsoft::NodeApiJsi::NodeApi::current();+  napi_value script{}, obj{};+  // Do not use NAPI_CALL - we must finalize the buffer right after we attempted the string creation.+  napi_status status =+      nodeApi->napi_create_string_utf8(env, reinterpret_cast<char *>(script_data), script_length, &script);+  finalize_cb(env, script_data, finalize_hint);+  NAPI_CALL(status);+  NAPI_CALL(nodeApi->napi_create_object(env, &obj));+  NAPI_CALL(nodeApi->napi_set_named_property(env, obj, "script", script));+  return nodeApi->napi_create_reference(env, obj, 1, reinterpret_cast<napi_ref *>(result));+}++// Default implementation of napi_ext_delete_prepared_script if it is not provided by JS engine.+// It deletes prepared_script as a napi_ref.+napi_status NAPI_CDECL default_napi_ext_delete_prepared_script(napi_env env, napi_ext_prepared_script prepared_script) {+  Microsoft::NodeApiJsi::NodeApi *nodeApi = Microsoft::NodeApiJsi::NodeApi::current();+  return nodeApi->napi_delete_reference(env, reinterpret_cast<napi_ref>(prepared_script));+}++// Default implementation of napi_ext_prepared_script_run if it is not provided by JS engine.+// It interprets prepared_script as a napi_ref to an object with a "script" property string.+napi_status NAPI_CDECL+default_napi_ext_prepared_script_run(napi_env env, napi_ext_prepared_script prepared_script, napi_value *result) {+  Microsoft::NodeApiJsi::NodeApi *nodeApi = Microsoft::NodeApiJsi::NodeApi::current();+  napi_value obj{}, script{};+  NAPI_CALL(nodeApi->napi_get_reference_value(env, reinterpret_cast<napi_ref>(prepared_script), &obj));+  NAPI_CALL(nodeApi->napi_get_named_property(env, obj, "script", &script));+  return nodeApi->napi_run_script(env, script, result);+}++EXTERN_C_END\ No newline at end of file

src/NodeApiJsiRuntime.h

new file mode 100644@@ -0,0 +1,20 @@+// Copyright (c) Microsoft Corporation.+// Licensed under the MIT License.++#pragma once+#ifndef SRC_NODEAPIJSIRUNTIME_H_+#define SRC_NODEAPIJSIRUNTIME_H_++#include <jsi/jsi.h>+#include <napi/js_native_ext_api.h>+#include <functional>+#include "NodeApi.h"++namespace Microsoft::NodeApiJsi {++std::unique_ptr<facebook::jsi::Runtime>+makeNodeApiJsiRuntime(napi_env env, NodeApi *nodeApi, std::function<void()> onDelete) noexcept;++} // namespace Microsoft::NodeApiJsi++#endif // !SRC_NODEAPIJSIRUNTIME_H_

src/NodeApi_posix.cpp

new file mode 100644@@ -0,0 +1,16 @@+// Copyright (c) Microsoft Corporation.+// Licensed under the MIT License.++#include "LibLoader.h"++namespace Microsoft::NodeApiJsi {++LibHandle LibLoader::loadLib(const char *libName) {+  // TODO: implement+}++FuncPtr LibLoader::getFuncPtr(LibHandle libHandle, const char *funcName) {+  // TODO: implement+}++} // namespace Microsoft::NodeApiJsi

src/NodeApi_win.cpp

new file mode 100644@@ -0,0 +1,19 @@+// Copyright (c) Microsoft Corporation.+// Licensed under the MIT License.++#include "NodeApi.h"+#define WIN32_LEAN_AND_MEAN+#define NOMINMAX+#include <windows.h>++namespace Microsoft::NodeApiJsi {++LibHandle LibLoader::loadLib(const char *libName) {+  return reinterpret_cast<LibHandle>(LoadLibraryA(libName));+}++FuncPtr LibLoader::getFuncPtr(LibHandle libHandle, const char *funcName) {+  return reinterpret_cast<FuncPtr>(GetProcAddress(reinterpret_cast<HMODULE>(libHandle), funcName));+}++} // namespace Microsoft::NodeApiJsi

tests/CMakeLists.txt

new file mode 100644@@ -0,0 +1,48 @@+# Copyright (c) Microsoft Corporation.+# Licensed under the MIT License.++add_executable(jsi_tests+  "../jsi/test/testlib.h"+  "../jsi/test/testlib.cpp"+  "../jsi/test/testlib_ext.cpp"+  "../jsi/decorator.h"+  "../jsi/instrumentation.h"+  "../jsi/jsi-inl.h"+  "../jsi/jsi.cpp"+  "../jsi/jsi.h"+  "../jsi/jsilib-posix.cpp"+  "../jsi/jsilib-windows.cpp"+  "../jsi/jsilib.h"+  "../jsi/threadsafe.h"+  "../src/HermesApi.cpp"+  "../src/HermesApi.h"+  "../src/NodeApi_win.cpp"+  "../src/NodeApi.cpp"+  "../src/NodeApi.h"+  "../src/NodeApiJsiRuntime.cpp"+  "../src/NodeApiJsiRuntime.h"+  "JsiRuntimeTests.cpp"+)++target_include_directories(jsi_tests PUBLIC .. ../src)+target_link_libraries(jsi_tests PUBLIC gtest_main)++find_program(NUGET_EXE NAMES nuget)+if(NOT NUGET_EXE)+  message("NUGET.EXE not found.")+  message(FATAL_ERROR "Please install this executable, and run CMake again.")+endif()++exec_program(${NUGET_EXE}+  ARGS+    install "Microsoft.JavaScript.Hermes"+    -Version 0.1.5+    -ExcludeVersion+    -OutputDirectory ${CMAKE_BINARY_DIR}/packages)++target_link_libraries(jsi_tests PRIVATE ${CMAKE_BINARY_DIR}/packages/Microsoft.JavaScript.Hermes/build/native/Microsoft.JavaScript.Hermes.targets)++add_test(+  NAME jsi_tests+  COMMAND $<TARGET_FILE:jsi_tests>+)\ No newline at end of file

tests/JsiRuntimeTests.cpp

new file mode 100644@@ -0,0 +1,28 @@+// Copyright (c) Microsoft Corporation.+// Licensed under the MIT License.++#include <HermesApi.h>+#include <NodeApiJsiRuntime.h>+#include <napi/hermes_api.h>+#include <vector>+#include "../jsi/test/testlib.h"++using namespace Microsoft::NodeApiJsi;++namespace facebook::jsi {+std::vector<RuntimeFactory> runtimeGenerators() {+  return {RuntimeFactory([]() {+    HermesApi *hermesApi = HermesApi::fromLib();+    HermesApi::setCurrent(hermesApi);++    hermes_config config{};+    hermes_runtime runtime{};+    napi_env env{};+    hermesApi->hermes_create_config(&config);+    hermesApi->hermes_create_runtime(config, &runtime);+    hermesApi->hermes_get_node_api_env(runtime, &env);++    return makeNodeApiJsiRuntime(env, hermesApi, [runtime]() { HermesApi::current()->hermes_delete_runtime(runtime); });+  })};+}+} // namespace facebook::jsi

tests/NuGet.config

new file mode 100644@@ -0,0 +1,11 @@+<?xml version="1.0" encoding="utf-8"?>+<configuration>+  <packageSources>+    <clear />+    <add key="local" value="../../out/pkg" />+    <add key="react-native" value="https://pkgs.dev.azure.com/ms/react-native/_packaging/react-native-public/nuget/v3/index.json" />+  </packageSources>+  <disabledPackageSources>+    <clear />+  </disabledPackageSources>+</configuration>