-
Notifications
You must be signed in to change notification settings - Fork 23
Quickjs #132
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
CedricGuillemet
wants to merge
30
commits into
BabylonJS:main
Choose a base branch
from
CedricGuillemet:quickjs
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Quickjs #132
Changes from all commits
Commits
Show all changes
30 commits
Select commit
Hold shift + click to select a range
23a3130
QuickJS experiment
CedricGuillemet 2e63f18
removed js
CedricGuillemet 61f1658
cleanup cmake scripts
CedricGuillemet 139a172
warnings
CedricGuillemet 67cacb0
fix prototype
CedricGuillemet 2523e68
forced quickjs promise continuation
CedricGuillemet 3c14c00
fix performance
CedricGuillemet f32eda9
fix some leaks
CedricGuillemet af92089
newer quickjs-ng
CedricGuillemet 0f14750
Add QuickJS CI jobs for Win32, Linux, and Android
CedricGuillemet 19be006
Fix multiple bugs in QuickJS NAPI bindings
CedricGuillemet 2f07daf
Move QuickJS microtask processing out of shared code
CedricGuillemet 7d7d077
Upgrade C++ standard from 17 to 20
CedricGuillemet 8657c54
quickjs on android emulator test
CedricGuillemet fe26c61
Fix JSValue leak in Detach causing JS_FreeRuntime assert on Android
CedricGuillemet f9e10bb
Fix QuickJS NAPI weak-reference and ExternalCallback self-cycle leaks
CedricGuillemet a98aabf
quickjs napi: track napi_refs in env and release at Detach
CedricGuillemet bf4183d
cmake: define NDEBUG on the QuickJS library target
CedricGuillemet 4502c2c
Revert "cmake: define NDEBUG on the QuickJS library target"
CedricGuillemet 5d1fd76
QuickJS: skip newTarget self-dup for non-constructor functions
CedricGuillemet f370cab
Merge branch 'main' of https://github.com/babylonjs/JsRuntimeHost int…
CedricGuillemet ca61e84
Merge BabylonJS/JsRuntimeHost main into quickjs
web-flow 3ae53f8
fix(quickjs): suppress -Wshorten-64-to-32 on Apple Clang
web-flow 1b9b298
Merge remote-tracking branch 'upstream/main' into quickjs
CedricGuillemet 5d8c346
up quickjs
CedricGuillemet 758257f
leak fix iteration
CedricGuillemet fd593f1
websocket ref
CedricGuillemet cd76f5d
unref env
CedricGuillemet 172340e
PR feedback
CedricGuillemet e30c746
feedback
CedricGuillemet File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| #include "AppRuntime.h" | ||
| #include <napi/env.h> | ||
|
|
||
| #ifdef _WIN32 | ||
| #pragma warning(push) | ||
| // cast from int64 to int32 | ||
| #pragma warning(disable : 4244) | ||
| #endif | ||
| #if defined(__clang__) | ||
| #pragma clang diagnostic push | ||
| #pragma clang diagnostic ignored "-Wshorten-64-to-32" | ||
| #endif | ||
| #include <quickjs.h> | ||
| #if defined(__clang__) | ||
| #pragma clang diagnostic pop | ||
| #endif | ||
| #ifdef _WIN32 | ||
| #pragma warning(pop) | ||
| #endif | ||
|
|
||
| namespace Babylon | ||
| { | ||
| void AppRuntime::RunEnvironmentTier(const char* /*executablePath*/) | ||
| { | ||
| // Create the runtime. | ||
| JSRuntime* runtime = JS_NewRuntime(); | ||
| if (!runtime) | ||
| { | ||
| throw std::runtime_error{"Failed to create QuickJS runtime"}; | ||
| } | ||
|
|
||
| // Create the context. | ||
| JSContext* context = JS_NewContext(runtime); | ||
| if (!context) | ||
| { | ||
| JS_FreeRuntime(runtime); | ||
| throw std::runtime_error{"Failed to create QuickJS context"}; | ||
| } | ||
|
|
||
| // Use the context within a scope. | ||
| { | ||
| Napi::Env env = Napi::Attach(context); | ||
|
|
||
| Run(env); | ||
|
|
||
| Napi::Detach(env); | ||
| } | ||
|
|
||
| // Destroy the context and runtime. | ||
| JS_FreeContext(context); | ||
| JS_FreeRuntime(runtime); | ||
| } | ||
|
|
||
| void AppRuntime::DrainMicrotasks(Napi::Env env) | ||
| { | ||
| // QuickJS does not auto-drain its job queue. Promise continuations, | ||
| // queueMicrotask callbacks, etc. are queued as "pending jobs" and only | ||
| // run when explicitly pumped. We drain them here, after each user | ||
| // callback, so async code observes the same "between turns" semantics | ||
| // it gets on the auto-draining engines (V8/Chakra/JSC). | ||
| JSRuntime* runtime = JS_GetRuntime(Napi::GetContext(env)); | ||
| JSContext* pending_ctx; | ||
| while (JS_ExecutePendingJob(runtime, &pending_ctx) > 0) | ||
| { | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| #pragma once | ||
|
|
||
| #include <napi/napi.h> | ||
| struct JSContext; | ||
|
|
||
| namespace Napi | ||
| { | ||
| Napi::Env Attach(JSContext* context); | ||
|
|
||
| void Detach(Napi::Env); | ||
|
|
||
| Napi::Value Eval(Napi::Env env, const char* source, const char* sourceUrl); | ||
|
|
||
| JSContext* GetContext(Napi::Env); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,145 @@ | ||
| #include <napi/env.h> | ||
| #include "js_native_api_quickjs.h" | ||
| #include <stdexcept> | ||
| #if defined(__clang__) | ||
| #pragma clang diagnostic push | ||
| #pragma clang diagnostic ignored "-Wshorten-64-to-32" | ||
| #endif | ||
| #include <quickjs.h> | ||
| #if defined(__clang__) | ||
| #pragma clang diagnostic pop | ||
| #endif | ||
|
|
||
| namespace Napi | ||
| { | ||
| Env Attach(JSContext* context) | ||
| { | ||
| napi_env env_ptr{new napi_env__}; | ||
| env_ptr->context = context; | ||
| env_ptr->current_context = env_ptr->context; | ||
|
|
||
| // Cache Object.prototype.hasOwnProperty for napi_has_own_property's | ||
| // fast path. These lookups are fundamental to any valid context, so a | ||
| // failure here signals a broken context: throw rather than silently | ||
| // leaving has_own_property_function undefined (matching how the other | ||
| // engines fail Attach loudly). | ||
| JSValue global = JS_GetGlobalObject(context); | ||
| JSValue object = JS_GetPropertyStr(context, global, "Object"); | ||
| JS_FreeValue(context, global); | ||
| if (JS_IsException(object) || !JS_IsObject(object)) | ||
| { | ||
| JS_FreeValue(context, object); | ||
| delete env_ptr; | ||
| throw std::runtime_error{"Napi::Attach: failed to resolve the global 'Object' constructor"}; | ||
| } | ||
|
|
||
| // Use the constructor's "prototype" property to get Object.prototype. | ||
| // JS_GetPrototype(object) would return the Object *constructor's* | ||
| // [[Prototype]] (Function.prototype), from which hasOwnProperty is only | ||
| // reachable by inheritance - correct by luck, but not by intent. | ||
| JSValue prototype = JS_GetPropertyStr(context, object, "prototype"); | ||
| JS_FreeValue(context, object); | ||
| if (JS_IsException(prototype) || !JS_IsObject(prototype)) | ||
| { | ||
| JS_FreeValue(context, prototype); | ||
| delete env_ptr; | ||
| throw std::runtime_error{"Napi::Attach: failed to resolve Object.prototype"}; | ||
| } | ||
|
|
||
| JSValue hasOwnProperty = JS_GetPropertyStr(context, prototype, "hasOwnProperty"); | ||
| JS_FreeValue(context, prototype); | ||
| if (JS_IsException(hasOwnProperty) || !JS_IsFunction(context, hasOwnProperty)) | ||
| { | ||
| JS_FreeValue(context, hasOwnProperty); | ||
| delete env_ptr; | ||
| throw std::runtime_error{"Napi::Attach: failed to resolve Object.prototype.hasOwnProperty"}; | ||
| } | ||
|
|
||
| env_ptr->has_own_property_function = hasOwnProperty; | ||
|
|
||
| return {env_ptr}; | ||
| } | ||
|
|
||
| void Detach(Env env) | ||
| { | ||
| napi_env env_ptr{env}; | ||
| if (env_ptr) | ||
| { | ||
| // Release every strong napi_ref still outstanding. This mirrors | ||
| // the V8 impl (napi_env__::DeleteMe) and is essential on QuickJS: | ||
| // any surviving strong ref pins a JS value from outside the GC | ||
| // graph, which prevents the teardown cascade in JS_FreeContext | ||
| // from running and triggers list_empty(gc_obj_list) assert in | ||
| // JS_FreeRuntime. | ||
| // | ||
| // Freeing a value can synchronously run a napi_wrap finalizer | ||
| // whose C++ destructor releases *other* embedded napi_refs (e.g. | ||
| // an AbortController destroying its AbortSignal ObjectReference). | ||
| // Those nested napi_delete_reference calls must not perform a real | ||
| // JS_FreeValue - otherwise a value can be freed twice - and must | ||
| // not mutate refs_list while we iterate it. So we first neutralize | ||
| // every ref (count/value zeroed, list cleared) and only then free | ||
| // the snapshotted values. Any finalizer-driven | ||
| // napi_delete_reference then sees count == 0 and is a safe no-op. | ||
| std::vector<JSValue> strongValues; | ||
| strongValues.reserve(env_ptr->refs_list.size()); | ||
| for (void* p : env_ptr->refs_list) | ||
| { | ||
| auto* info = reinterpret_cast<RefInfo*>(p); | ||
| if (info->count > 0) | ||
| { | ||
| strongValues.push_back(info->value); | ||
| } | ||
| info->count = 0; | ||
| info->value = JS_UNDEFINED; | ||
| } | ||
| env_ptr->refs_list.clear(); | ||
| env_ptr->detached = true; | ||
|
|
||
| for (JSValue value : strongValues) | ||
| { | ||
| JS_FreeValue(env_ptr->context, value); | ||
| } | ||
|
|
||
| if (!JS_IsUndefined(env_ptr->has_own_property_function)) | ||
| { | ||
| JS_FreeValue(env_ptr->context, env_ptr->has_own_property_function); | ||
| env_ptr->has_own_property_function = JS_UNDEFINED; | ||
| } | ||
|
|
||
| // Free all remaining JSValues in the handle scope stack | ||
| for (auto& ptr : env_ptr->handle_scope_stack) | ||
| { | ||
| JS_FreeValue(env_ptr->context, *ptr); | ||
| } | ||
| env_ptr->handle_scope_stack.clear(); | ||
|
|
||
| // Run the cycle collector so napi_wrap finalizers (which | ||
| // destroy C++ wrapper objects and release any embedded | ||
| // napi_refs) get a chance to execute while the env is still | ||
| // valid. A second pass picks up anything unpinned by the | ||
| // first pass's finalizers. | ||
| JSRuntime* rt = JS_GetRuntime(env_ptr->context); | ||
| JS_RunGC(rt); | ||
| JS_RunGC(rt); | ||
|
|
||
| // Drop the initial owner reference taken in Attach. If every | ||
| // ExternalData finalizer already ran during the GC passes above, | ||
| // this deletes the env now. If some are deferred to the engine's | ||
| // JS_FreeContext/JS_FreeRuntime teardown cascade (the common case), | ||
| // each still holds a count, so the env survives until the last such | ||
| // finalizer completes and drops the final count - deleting the env | ||
| // exactly once, after its last use, with no leak. | ||
| // | ||
| // NOTE: env_ptr must not be touched after this point; it may have | ||
| // already been deleted. | ||
| env_ptr->Unref(); | ||
| } | ||
| } | ||
|
|
||
| JSContext* GetContext(Env env) | ||
| { | ||
| napi_env env_ptr{env}; | ||
| return env_ptr->context; | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.