@ 261103.1

240422.1 · 5c4cb3f889156fd378268063fb9fbc6ecd1617f9

Add support for JSI queue microtask (#9)

reading declarations

jsi/jsi/README.md

@@ -11,6 +11,8 @@ https://github.com/facebook/hermes repo.  | Version | Commit Hash                                | Commit Description |--------:|:-------------------------------------------|------------------------------------------------------+|      12 | `de9dfe408bc3f8715aef161c5fcec291fc6dacb2` | Add queueMicrotask method to JSI+|         | `a699e87f995bc2f7193990ff36a57ec9cad940e5` | Make queueMicrotask pure virtual |      11 | `a1c168705f609c8f1ae800c60d88eb199154264b` | Add JSI method for setting external memory size |      10 | `b81666598672cb5f8b365fe6548d3273f216322e` | Clarify const-ness of JSI references |       9 | `e6d887ae96bef5c71032f11ed1a9fb9fecec7b46` | Add external ArrayBuffers to JSI

jsi/jsi/decorator.h

@@ -126,6 +126,11 @@ class RuntimeDecorator : public Base, private jsi::Instrumentation {       const std::shared_ptr<const PreparedJavaScript>& js) override {     return plain().evaluatePreparedJavaScript(js);   }+#if JSI_VERSION >= 12+  void queueMicrotask(const jsi::Function& callback) override {+    return plain().queueMicrotask(callback);+  }+#endif #if JSI_VERSION >= 4   bool drainMicrotasks(int maxMicrotasksHint) override {     return plain().drainMicrotasks(maxMicrotasksHint);

jsi/jsi/jsi.h

@@ -30,7 +30,7 @@ // 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 11+#define JSI_VERSION 12 #endif  #if JSI_VERSION >= 3@@ -233,6 +233,14 @@ class JSI_EXPORT Runtime {   virtual Value evaluatePreparedJavaScript(       const std::shared_ptr<const PreparedJavaScript>& js) = 0; +#if JSI_VERSION >= 12+  /// Queues a microtask in the JavaScript VM internal Microtask (a.k.a. Job in+  /// ECMA262) queue, to be executed when the host drains microtasks in+  /// its event loop implementation.+  ///+  /// \param callback a function to be executed as a microtask.+  virtual void queueMicrotask(const jsi::Function& callback) = 0;+#endif #if JSI_VERSION >= 4   /// Drain the JavaScript VM internal Microtask (a.k.a. Job in ECMA262) queue.   ///

jsi/jsi/test/testlib.cpp

@@ -1358,6 +1358,40 @@ TEST_P(JSITest, JSErrorTest) {       JSIException); } +#if defined(JSI_SUPPORT_MICROTASKS) && JSI_VERSION >= 12+TEST_P(JSITest, MicrotasksTest) {+  try {+    rt.global().setProperty(rt, "globalValue", String::createFromAscii(rt, ""));++    auto microtask1 =+        function("function microtask1() { globalValue += 'hello'; }");+    auto microtask2 =+        function("function microtask2() { globalValue += ' world' }");++    rt.queueMicrotask(microtask1);+    rt.queueMicrotask(microtask2);++    EXPECT_EQ(+        rt.global().getProperty(rt, "globalValue").asString(rt).utf8(rt), "");++    rt.drainMicrotasks();++    EXPECT_EQ(+        rt.global().getProperty(rt, "globalValue").asString(rt).utf8(rt),+        "hello world");++    // Microtasks shouldn't run again+    rt.drainMicrotasks();++    EXPECT_EQ(+        rt.global().getProperty(rt, "globalValue").asString(rt).utf8(rt),+        "hello world");+  } catch (const JSINativeException& ex) {+    // queueMicrotask() is unimplemented by some runtimes, ignore such failures.+  }+}+#endif+ //---------------------------------------------------------------------- // Test that multiple levels of delegation in DecoratedHostObjects works. 

node-api/js_runtime_api.h

@@ -127,6 +127,10 @@ JSR_API jsr_close_napi_env_scope(napi_env env, jsr_napi_env_scope scope); // To implement JSI description() JSR_API jsr_get_description(napi_env env, const char** result); +// To implement JSI queueMicrotask()+JSR_API+jsr_queue_microtask(napi_env env, napi_value callback);+ // To implement JSI drainMicrotasks() JSR_API jsr_drain_microtasks(napi_env env, int32_t max_count_hint, bool* result);

src/ApiLoaders/JSRuntimeApi.cpp

@@ -9,6 +9,7 @@ EXTERN_C_START extern napi_status NAPI_CDECL default_jsr_open_napi_env_scope(napi_env env, jsr_napi_env_scope *scope); extern napi_status NAPI_CDECL default_jsr_close_napi_env_scope(napi_env env, jsr_napi_env_scope scope); extern napi_status NAPI_CDECL default_jsr_get_description(napi_env env, const char **result);+extern napi_status NAPI_CDECL default_jsr_queue_microtask(napi_env env, napi_value callback); extern napi_status NAPI_CDECL default_jsr_drain_microtasks(napi_env env, int32_t max_count_hint, bool *result); extern napi_status NAPI_CDECL default_jsr_is_inspectable(napi_env env, bool *result); 

src/ApiLoaders/JSRuntimeApi.inc

@@ -33,6 +33,7 @@ JSR_FUNC(jsr_runtime_get_node_api_env)  // The JS runtime functions needed for JSI. JSR_JSI_FUNC(jsr_close_napi_env_scope)+JSR_JSI_FUNC(jsr_queue_microtask) JSR_JSI_FUNC(jsr_drain_microtasks) JSR_JSI_FUNC(jsr_get_description) JSR_JSI_FUNC(jsr_is_inspectable)

src/NodeApiJsiRuntime.cpp

@@ -188,6 +188,9 @@ class NodeApiJsiRuntime : public jsi::Runtime {       const std::shared_ptr<const jsi::Buffer> &buffer,       std::string sourceURL) override;   jsi::Value evaluatePreparedJavaScript(const std::shared_ptr<const jsi::PreparedJavaScript> &js) override;+#if JSI_VERSION >= 12+  void queueMicrotask(const jsi::Function& callback) override;+#endif #if JSI_VERSION >= 4   bool drainMicrotasks(int maxMicrotasksHint = -1) override; #endif@@ -940,6 +943,14 @@ jsi::Value NodeApiJsiRuntime::evaluatePreparedJavaScript(const std::shared_ptr<c   return toJsiValue(result); } +#if JSI_VERSION >= 12+void NodeApiJsiRuntime::queueMicrotask(const jsi::Function& callback) {+  NodeApiScope scope{*this};+  napi_value callbackValue = getNodeApiValue(callback);+  CHECK_NAPI(jsrApi_->jsr_queue_microtask(env_, callbackValue));+}+#endif+ #if JSI_VERSION >= 4 bool NodeApiJsiRuntime::drainMicrotasks(int maxMicrotasksHint) {   bool result{};@@ -1573,7 +1584,7 @@ bool NodeApiJsiRuntime::instanceOf(const jsi::Object &obj, const jsi::Function & }  #if JSI_VERSION >= 11-void NodeApiJsiRuntime::setExternalMemoryPressure(const jsi::Object &/*obj*/, size_t /*amount*/) {+void NodeApiJsiRuntime::setExternalMemoryPressure(const jsi::Object & /*obj*/, size_t /*amount*/) {   // TODO: implement } #endif@@ -2803,8 +2814,14 @@ napi_status NAPI_CDECL default_jsr_get_description(napi_env /*env*/, const char   return napi_ok; } +// Default implementation of jsr_queue_microtask if it is not provided by JS engine.+// It does nothing.+napi_status NAPI_CDECL default_jsr_queue_microtask(napi_env /*env*/, napi_value /*callback*/) {+  return napi_generic_failure;+}+ // Default implementation of jsr_drain_microtasks if it is not provided by JS engine.-// It does nothing+// It does nothing. napi_status NAPI_CDECL default_jsr_drain_microtasks(napi_env /*env*/, int32_t /*max_count_hint*/, bool *result) {   if (result != nullptr) {     *result = true; // All tasks are drained