diff --git a/Core/Node-API/Source/env_hermes.cc b/Core/Node-API/Source/env_hermes.cc index c3a97db0..1db67572 100644 --- a/Core/Node-API/Source/env_hermes.cc +++ b/Core/Node-API/Source/env_hermes.cc @@ -164,6 +164,8 @@ namespace Napi // exceptions thrown FROM user callbacks are already handled in // AppRuntime::Dispatch's try/catch. (void)runtime->drainJobs(); + // WeakRef targets are kept alive only until the current job finishes. + runtime->clearKeptObjects(); } Napi::Value Eval(Napi::Env env, const char* source, const char* sourceUrl) diff --git a/Tests/UnitTests/CMakeLists.txt b/Tests/UnitTests/CMakeLists.txt index 6f6f88a6..4171ff13 100644 --- a/Tests/UnitTests/CMakeLists.txt +++ b/Tests/UnitTests/CMakeLists.txt @@ -65,6 +65,8 @@ endif() if(NAPI_JAVASCRIPT_ENGINE STREQUAL "Chakra") set(SOURCES ${SOURCES} "Source/Tests.NodeApi.Chakra.cpp") +elseif(NAPI_JAVASCRIPT_ENGINE STREQUAL "Hermes") + set(SOURCES ${SOURCES} "Source/Tests.NodeApi.Hermes.cpp") elseif(NAPI_JAVASCRIPT_ENGINE STREQUAL "JavaScriptCore") set(SOURCES ${SOURCES} "Source/Tests.NodeApi.JavaScriptCore.cpp") endif() diff --git a/Tests/UnitTests/Source/Scripts/tests.scheduling.ts b/Tests/UnitTests/Source/Scripts/tests.scheduling.ts index 8b813f10..4d1cef55 100644 --- a/Tests/UnitTests/Source/Scripts/tests.scheduling.ts +++ b/Tests/UnitTests/Source/Scripts/tests.scheduling.ts @@ -14,10 +14,10 @@ describe("setTimeout", function () { }); it("should call the given function after the given delay", function (done) { - const startTime = new Date().getTime(); + const startTime = performance.now(); setTimeout(() => { try { - expect(new Date().getTime() - startTime).to.be.at.least(10); + expect(performance.now() - startTime).to.be.at.least(10); done(); } catch (e) { @@ -27,11 +27,11 @@ describe("setTimeout", function () { }); it("should call the given nested function after the given delay", function (done) { - const startTime = new Date().getTime(); + const startTime = performance.now(); setTimeout(() => { setTimeout(() => { try { - expect(new Date().getTime() - startTime).to.be.at.least(20); + expect(performance.now() - startTime).to.be.at.least(20); done(); } catch (e) { @@ -42,10 +42,10 @@ describe("setTimeout", function () { }); it("should call the given function after the given delay when the delay is a string representing a valid number", function (done) { - const startTime = new Date().getTime(); + const startTime = performance.now(); setTimeout(() => { try { - expect(new Date().getTime() - startTime).to.be.at.least(10); + expect(performance.now() - startTime).to.be.at.least(10); done(); } catch (e) { @@ -143,12 +143,12 @@ describe("setInterval", function () { }); it("should call the given function at the given interval", function (done) { - let startTime = new Date().getTime(); + const startTime = performance.now(); let tickCount = 0; const id = setInterval(() => { try { tickCount++; - expect(new Date().getTime() - startTime).to.be.at.least(tickCount * 10); + expect(performance.now() - startTime).to.be.at.least(tickCount * 10); if (tickCount > 2) { clearInterval(id); done(); diff --git a/Tests/UnitTests/Source/Tests.NodeApi.Hermes.cpp b/Tests/UnitTests/Source/Tests.NodeApi.Hermes.cpp new file mode 100644 index 00000000..820f8473 --- /dev/null +++ b/Tests/UnitTests/Source/Tests.NodeApi.Hermes.cpp @@ -0,0 +1,36 @@ +#include +#include +#include + +#include +#include +#include + +TEST(NodeApi, HermesDrainJobsReleasesWeakRefTargets) +{ + std::promise externalBytes; + auto completed = externalBytes.get_future(); + Babylon::AppRuntime runtime{}; + runtime.Dispatch([&](Napi::Env env) { + try + { + for (size_t i = 0; i < 256; ++i) + { + { + Napi::HandleScope scope{env}; + Napi::Eval(env, "new WeakRef(new Uint8Array(1024 * 1024));", "weakref-job.js"); + } + Napi::DrainJobs(env); + } + externalBytes.set_value(Napi::Eval(env, + "HermesInternal.getInstrumentedStats().js_externalBytes", + "weakref-heap.js").As().DoubleValue()); + } + catch (const Napi::Error& error) + { + externalBytes.set_exception(std::make_exception_ptr(std::runtime_error{error.Message()})); + } + }); + ASSERT_EQ(completed.wait_for(std::chrono::seconds{30}), std::future_status::ready); + EXPECT_LT(completed.get(), 64 * 1024 * 1024); +}