Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions Core/Node-API/Include/Engine/Hermes/napi/env.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,10 @@ namespace Napi
void Detach(Napi::Env env);

// Compile and execute UTF-8 source on the current Hermes runtime.
// `sourceUrl` is attached to stack traces. Unlike the other engines
// we don't go through `Env::RunScript` because Hermes's standard
// `napi_run_script` is the canonical 3-argument signature, while the
// shared header carries a Babylon-specific 4-argument variant with a
// `source_url` parameter that Hermes doesn't provide. Instead we call
// Hermes's `hermes_run_script` directly inside the engine TU.
// `sourceUrl` is attached to stack traces. This calls Hermes's
// `hermes_run_script` with an owned source copy; `Env::RunScript` works
// too, through the source-URL overload of `napi_run_script` that
// env_hermes.cc defines.
Napi::Value Eval(Napi::Env env, const char* source, const char* sourceUrl);

// Pump Hermes's job queue (drains microtasks and pending finalizers).
Expand Down
15 changes: 13 additions & 2 deletions Core/Node-API/Include/Shared/napi/js_native_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -490,10 +490,8 @@ NAPI_EXTERN napi_status NAPI_CDECL napi_is_promise(napi_env env,
bool* is_promise);

// Running a script
// [BABYLON-NATIVE-ADDITION]
NAPI_EXTERN napi_status NAPI_CDECL napi_run_script(napi_env env,
napi_value script,
const char* source_url,
napi_value* result);

// Memory management
Expand Down Expand Up @@ -631,4 +629,17 @@ NAPI_EXTERN napi_status NAPI_CDECL napi_object_seal(napi_env env,

EXTERN_C_END

#ifdef __cplusplus
// [BABYLON-NATIVE-ADDITION]
// Runs a script with a source URL for stack traces and diagnostics. This is a C++ overload on
// purpose: engines that ship their own Node-API implementation (Hermes) export the standard
// three-argument napi_run_script above with C linkage, and declaring this four-argument form
// with C linkage made Env::RunScript bind to that symbol and pass the URL where the engine
// expected the result pointer.
NAPI_EXTERN napi_status NAPI_CDECL napi_run_script(napi_env env,
napi_value script,
const char* source_url,
napi_value* result);
#endif

#endif // SRC_JS_NATIVE_API_H_
45 changes: 40 additions & 5 deletions Core/Node-API/Source/env_hermes.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,9 @@
// `<napi/env.h>` chain FIRST so:
// * the Napi:: C++ wrappers (`Napi::Env`, `Napi::Value`, `Napi::Error`)
// line up with the rest of the project,
// * the Babylon-extended 4-arg `napi_run_script` declaration matches the
// inline `Env::RunScript` body in napi-inl.h (which we never actually
// call in this engine — Napi::Eval below routes through
// `hermes_run_script` directly — so the linker is never asked to find
// the 4-arg symbol).
// * the Babylon source-URL overload of `napi_run_script` (a C++ overload in
// the shared header) resolves to the definition below, which runs through
// `hermes_run_script`; the standard 3-arg C entry point stays Hermes's own.
//
// We keep `NAPI_VERSION` at the shared default (5) so the inline wrappers
// in napi-inl.h that target newer NAPI revisions (e.g.
Expand Down Expand Up @@ -97,6 +95,43 @@ namespace
}
}

// The source-URL overload of napi_run_script (a C++ overload, see js_native_api.h). Hermes
// exports only the standard three-argument entry point, so Env::RunScript(script, url) lands here
// and runs through hermes_run_script like Napi::Eval below; the standard form binds to Hermes's own
// symbol. Before the overload had C++ linkage, that call bound to the three-argument symbol and
// passed the URL where Hermes expected the result pointer.
napi_status napi_run_script(napi_env env, napi_value script, const char* source_url, napi_value* result)
{
if (env == nullptr || script == nullptr || result == nullptr)
{
return napi_invalid_arg;
}

size_t length = 0;
napi_status status = napi_get_value_string_utf8(env, script, nullptr, 0, &length);
if (status != napi_ok)
{
return status;
}

// hermes_run_script takes ownership of the buffer and wants the terminator counted in `size`.
const size_t size = length + 1;
auto* source = new uint8_t[size];
status = napi_get_value_string_utf8(env, script, reinterpret_cast<char*>(source), size, &length);
if (status != napi_ok)
{
delete[] source;
return status;
}

hermes_run_script_flags flags{};
flags.struct_size = sizeof(flags);
const auto finalize = [](const uint8_t* data, size_t /*size*/, void* /*hint*/) {
delete[] data;
};
return hermes_run_script(env, source, size, finalize, /*finalize_hint=*/nullptr, source_url, &flags, result);
}

namespace Napi
{
Napi::Env Attach()
Expand Down
18 changes: 18 additions & 0 deletions Tests/UnitTests/Shared/Shared.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -829,6 +829,24 @@ TEST(NodeApi, AdjacentEscapableScopesEscapeIndependently)

#endif

#if !defined(JSRUNTIMEHOST_NAPI_ENGINE_JSI)
TEST(NodeApi, RunScriptWithSourceUrl)
{
// Regression: Env::RunScript(script, url) calls the source-URL overload of napi_run_script.
// On Hermes that call used to bind to the engine's standard three-argument C symbol and hand
// it the URL where it expected the result pointer (an access violation on every host).
Babylon::AppRuntime runtime{};

std::promise<bool> evaluated;
runtime.Dispatch([&evaluated](Napi::Env env) {
const auto sum = env.RunScript("40 + 2", "run-script-with-url.js");
evaluated.set_value(sum.IsNumber() && sum.As<Napi::Number>().Int32Value() == 42);
});

EXPECT_TRUE(evaluated.get_future().get());
}
#endif

int RunTests()
{
testing::InitGoogleTest();
Expand Down