Skip to content

Commit 147aa88

Browse files
feat: JSWorker for all runtimes (not just V8) + Node transfers
JSC and QuickJS now implement the V8 Serializer API in order to handle serialization of standard JS types intermingled with Godot variants. On the web things are a bit more complex. We try take advantage of the web's built-in postMessage functionality as much as possible. The browser's postMessage handles structured clone for us i.e. will serialize plain old data, as well as more advanced types like Set, Map etc. However, it can't handle Godot types. So before calling postMessage we descend through the message data structure and replace Godot objects with markers which are delivered via the transfer list (WASM side channel). If a message contains no Godot objects then process is particularly performant because neither the sender or the receiver need to cross the WASM bridge at all when sending/receiving the message. QuickJS-NG has been updated to the latest version. Also, implemented a test matrix for each of our runtimes: - V8 - QuickJS (NG) - JavaScriptCore - Web - QuickJS WASM on Web
1 parent ffbade0 commit 147aa88

138 files changed

Lines changed: 48599 additions & 87119 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.changeset/lemon-candies-hide.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@godot-js/editor": minor
3+
---
4+
5+
feat: JSWorker for all runtimes (not just V8) + Node transfers

.github/workflows/build_ios.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,7 @@ jobs:
3636
strategy:
3737
fail-fast: false
3838
matrix:
39-
# JSC isn't working at the moment
40-
# engine: [ v8, qjs-ng, jsc ]
41-
engine: [ v8, qjs-ng ]
39+
engine: [ v8, qjs-ng, jsc ]
4240
target: ${{ inputs.release && fromJSON('["template_release","template_debug"]') || fromJSON('["template_debug"]') }}
4341
steps:
4442
- name: Checkout Godot
@@ -80,6 +78,7 @@ jobs:
8078
scons-flags: >-
8179
${{ env.SCONS_FLAGS }}
8280
${{ matrix.engine == 'qjs-ng' && 'use_quickjs_ng=yes' || '' }}
81+
${{ matrix.engine == 'jsc' && 'use_jsc=yes' || '' }}
8382
platform: ios
8483
target: ${{ matrix.target }}
8584

.github/workflows/build_macos.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ jobs:
3535
strategy:
3636
fail-fast: false
3737
matrix:
38-
engine: [v8, qjs-ng]
38+
engine: [v8, qjs-ng, jsc]
3939
target: ${{ inputs.release && fromJSON('["editor","template_release","template_debug"]') || fromJSON('["editor"]') }}
4040
steps:
4141
# From https://github.com/actions/runner-images/blob/main/images/macos
@@ -122,6 +122,7 @@ jobs:
122122
${{ env.SCONS_FLAGS }}
123123
${{ matrix.target != 'editor' && 'debug_symbols=no' || '' }}
124124
${{ matrix.engine == 'qjs-ng' && 'use_quickjs_ng=yes' || '' }}
125+
${{ matrix.engine == 'jsc' && 'use_jsc=yes' || '' }}
125126
arch=x86_64
126127
vulkan=${{ steps.vulkan-sdk.outputs.VULKAN_ENABLED }}
127128
${{ !inputs.release && matrix.target == 'editor' && 'tests=yes' || '' }}
@@ -135,6 +136,7 @@ jobs:
135136
${{ env.SCONS_FLAGS }}
136137
${{ matrix.target != 'editor' && 'debug_symbols=no' || '' }}
137138
${{ matrix.engine == 'qjs-ng' && 'use_quickjs_ng=yes' || '' }}
139+
${{ matrix.engine == 'jsc' && 'use_jsc=yes' || '' }}
138140
arch=arm64
139141
vulkan=${{ steps.vulkan-sdk.outputs.VULKAN_ENABLED }}
140142
${{ !inputs.release && matrix.target == 'editor' && 'tests=yes' || '' }}
@@ -181,7 +183,7 @@ jobs:
181183
strategy:
182184
fail-fast: false
183185
matrix:
184-
engine: [v8, qjs-ng]
186+
engine: [v8, qjs-ng, jsc]
185187
steps:
186188
- name: Checkout Godot
187189
uses: actions/checkout@v6

.github/workflows/misc_upload_assets.yml

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,18 @@ jobs:
1616
strategy:
1717
fail-fast: false
1818
matrix:
19-
engine: [ v8, qjs-ng ]
19+
engine: [ v8, qjs-ng, jsc ]
2020
os: [ linux, macos, windows ]
21-
target: [ template_release, template_debug, editor ]
22-
include:
21+
target: [ template_release, template_debug, editor, editor-app, template-app ]
22+
exclude:
23+
- os: linux
24+
engine: jsc
25+
- os: windows
26+
engine: jsc
2327
- target: editor-app
24-
os: macos
25-
engine: v8
28+
os: linux
2629
- target: template-app
27-
os: macos
28-
engine: v8
30+
os: windows
2931
steps:
3032
- name: ⏬ Checkout repo
3133
uses: actions/checkout@v6
@@ -46,9 +48,12 @@ jobs:
4648
strategy:
4749
fail-fast: false
4850
matrix:
49-
engine: [ v8, qjs-ng ]
51+
engine: [ v8, qjs-ng, jsc ]
5052
os: [ android, ios ]
5153
target: [ template_release, template_debug ]
54+
exclude:
55+
- os: android
56+
engine: jsc
5257
steps:
5358
- name: ⏬ Checkout repo
5459
uses: actions/checkout@v6

.github/workflows/runner.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,10 @@ jobs:
4747
version_ref: ${{ matrix.version_ref }}
4848
release: ${{ github.event_name == 'release' }}
4949

50-
test-project:
51-
name: 🔬 Test project
50+
runtime-matrix:
51+
name: 🧪 Runtime matrix
5252
needs: [engine-version-build]
53-
uses: ./.github/workflows/test_project.yml
53+
uses: ./.github/workflows/test_runtime_matrix.yml
5454
strategy:
5555
fail-fast: false
5656
matrix:
@@ -62,7 +62,7 @@ jobs:
6262

6363
builds-done:
6464
name: 🏁 Builds Done
65-
needs: [test-project]
65+
needs: [runtime-matrix]
6666
runs-on: ubuntu-latest
6767
steps:
6868
- name: 🏁 Check if all builds are done
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
name: 🧪 Runtime Matrix
2+
on:
3+
workflow_call:
4+
inputs:
5+
version:
6+
required: true
7+
type: string
8+
9+
jobs:
10+
runtime-matrix:
11+
strategy:
12+
fail-fast: false
13+
matrix:
14+
runtime: [host-v8, host-qjs, host-jsc, web-browser, web-qjs]
15+
runs-on: ${{ matrix.runtime == 'host-jsc' && 'macos-latest' || 'ubuntu-22.04' }}
16+
name: Runtime (${{ matrix.runtime }})
17+
steps:
18+
- name: ⏬ Checkout repository
19+
uses: actions/checkout@v6
20+
21+
- name: 🛠️ pnpm and build
22+
uses: ./.github/actions/pnpm-build
23+
24+
- name: ⏬ Download host-v8 artifact
25+
if: matrix.runtime == 'host-v8' || matrix.runtime == 'web-browser' || matrix.runtime == 'web-qjs'
26+
uses: actions/download-artifact@v4
27+
with:
28+
name: linux-editor-${{ inputs.version }}-v8
29+
path: runtime-artifacts/host-v8
30+
31+
- name: ⏬ Download host-qjs artifact
32+
if: matrix.runtime == 'host-qjs'
33+
uses: actions/download-artifact@v4
34+
with:
35+
name: linux-editor-${{ inputs.version }}-qjs-ng
36+
path: runtime-artifacts/host-qjs
37+
38+
- name: ⏬ Download host-jsc artifact
39+
if: matrix.runtime == 'host-jsc'
40+
uses: actions/download-artifact@v4
41+
with:
42+
name: macos-editor-${{ inputs.version }}-jsc
43+
path: runtime-artifacts/host-jsc
44+
45+
- name: ⏬ Download web-browser template artifact
46+
if: matrix.runtime == 'web-browser'
47+
uses: actions/download-artifact@v4
48+
with:
49+
name: web-template_debug-${{ inputs.version }}-browser
50+
path: runtime-artifacts/web-browser
51+
52+
- name: ⏬ Download web-qjs template artifact
53+
if: matrix.runtime == 'web-qjs'
54+
uses: actions/download-artifact@v4
55+
with:
56+
name: web-template_debug-${{ inputs.version }}-qjs-ng
57+
path: runtime-artifacts/web-qjs
58+
59+
- name: 📦 Stage runtime assets for run-runtime-matrix
60+
shell: bash
61+
run: |
62+
set -euo pipefail
63+
fail_missing() {
64+
echo "::error::missing required runtime artifact for $1 (version ${{ inputs.version }})"
65+
exit 1
66+
}
67+
68+
pick_one() {
69+
local dir="$1"
70+
local name_pattern="$2"
71+
local value
72+
value="$(find "$dir" -type f -name "$name_pattern" 2>/dev/null | head -n1 || true)"
73+
if [ -z "$value" ]; then
74+
return 1
75+
fi
76+
echo "$value"
77+
}
78+
79+
mkdir -p tests/bin
80+
case "${{ matrix.runtime }}" in
81+
host-v8)
82+
src="$(pick_one runtime-artifacts/host-v8 'godot.linuxbsd.editor.*')" || fail_missing "host-v8 binary"
83+
cp "$src" tests/bin/godot-host-v8
84+
chmod +x tests/bin/godot-host-v8
85+
;;
86+
host-qjs)
87+
src="$(pick_one runtime-artifacts/host-qjs 'godot.linuxbsd.editor.*')" || fail_missing "host-qjs binary"
88+
cp "$src" tests/bin/godot-host-qjs
89+
chmod +x tests/bin/godot-host-qjs
90+
;;
91+
host-jsc)
92+
src="$(pick_one runtime-artifacts/host-jsc 'godot.macos.editor.*')" || fail_missing "host-jsc binary"
93+
cp "$src" tests/bin/godot-host-jsc
94+
chmod +x tests/bin/godot-host-jsc
95+
;;
96+
web-browser)
97+
host_src="$(pick_one runtime-artifacts/host-v8 'godot.linuxbsd.editor.*')" || fail_missing "web-browser host-v8 binary"
98+
web_src="$(pick_one runtime-artifacts/web-browser '*.zip')" || fail_missing "web-browser template zip"
99+
cp "$host_src" tests/bin/godot-host-v8
100+
cp "$web_src" tests/bin/godot-web-browser-template_debug.zip
101+
chmod +x tests/bin/godot-host-v8
102+
;;
103+
web-qjs)
104+
host_src="$(pick_one runtime-artifacts/host-v8 'godot.linuxbsd.editor.*')" || fail_missing "web-qjs host-v8 binary"
105+
web_src="$(pick_one runtime-artifacts/web-qjs '*.zip')" || fail_missing "web-qjs template zip"
106+
cp "$host_src" tests/bin/godot-host-v8
107+
cp "$web_src" tests/bin/godot-web-qjs-template_debug.zip
108+
chmod +x tests/bin/godot-host-v8
109+
;;
110+
esac
111+
112+
- name: ▶️ Run selected runtime
113+
run: |
114+
pnpm test -- --runtimes ${{ matrix.runtime }} --skip-builds

.gitignore

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,13 @@ __MACOSX
3232
.vscode
3333

3434
# IntelliJ IDEA project files
35-
/.idea/
35+
.idea/
3636

3737
# Node modules
38-
/node_modules/
38+
node_modules/
3939

4040
# pnpm store
4141
.pnpm-store
42+
43+
# local test artifacts
44+
/tests/bin/

SCsub

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ lws_prebuilt_libs = LibraryDescriptor("lws", \
9191
])
9292
quickjs_src_descs = ThirdPartyDescriptor("quickjs", \
9393
[
94-
ThirdPartyDetails("quickjs-ng", ["cutils.c", "libbf.c", "libregexp.c", "libunicode.c", "quickjs.c"]),
94+
ThirdPartyDetails("quickjs-ng", ["dtoa.c", "libregexp.c", "libunicode.c", "quickjs.c"]),
9595
ThirdPartyDetails("quickjs", ["cutils.c", "libbf.c", "libregexp.c", "libunicode.c", "quickjs.c"]),
9696
])
9797

@@ -183,7 +183,7 @@ if quickjs_support is None and jsc_support is None and is_library_supported(v8_p
183183
if is_library_supported(lws_prebuilt_libs):
184184
download_dependency("lws", deps_lws_version, "lws")
185185

186-
v8_support = validate_library_support(v8_prebuilt_libs) if quickjs_support is None else None
186+
v8_support = validate_library_support(v8_prebuilt_libs) if quickjs_support is None and jsc_support is None else None
187187
lws_support = validate_library_support(lws_prebuilt_libs) if v8_support is not None else None
188188

189189
jsb_defines = [
@@ -202,7 +202,6 @@ jsb_defines = [
202202
"Use the host browser's javascript engine.",
203203
"Be aware that all script sources are evaluated in the host browser, which can be fully debugged with the browser's developer tools.",
204204
"It's recommended to obfuscate the script sources before deploying the project in a production environment.",
205-
"NOTE: web.impl is experimental and may not work as expected.",
206205
"NOTE: Obfuscation may not work in the current version. It'll be improved in the future versions. Report an issue if it breaks.",
207206
]),
208207
CompileDefines("JSB_WITH_JAVASCRIPTCORE", 1 if jsc_support is not None else 0, [
@@ -574,8 +573,6 @@ if quickjs_support is not None:
574573
if is_defined("JSB_WITH_WEB"):
575574
check(os.path.exists("impl/web/js"), "impl/web/js (GodotJS web bridge) has not been built")
576575

577-
print_warning("web.impl is experimental and may not work as expected")
578-
579576
env_jsb.add_source_files(module_obj, "impl/web/*.cpp")
580577

581578
# this script is compiled from impl/web/bridge typescript source

bridge/jsb_bridge_module_loader.cpp

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,28 @@ namespace jsb
2727
info.GetReturnValue().Set(impl::Helper::to_array_buffer(isolate, var));
2828
}
2929

30+
String _describe_export_name(
31+
v8::Isolate* isolate,
32+
const v8::Local<v8::Context>& context,
33+
Environment* environment,
34+
const v8::Local<v8::Object>& object)
35+
{
36+
v8::Local<v8::Value> name_value;
37+
if (!object->Get(context, jsb_name(environment, name)).ToLocal(&name_value))
38+
{
39+
return String("<name:get-failed>");
40+
}
41+
42+
if (name_value->IsString())
43+
{
44+
return impl::Helper::to_string(isolate, name_value);
45+
}
46+
47+
// Keep this coercion-safe: do not call generic JS value->string conversion
48+
// on arbitrary objects. js_debug_typeof provides structural categorization.
49+
return jsb_format("<%s>", TypeConvert::js_debug_typeof(isolate, name_value));
50+
}
51+
3052
// construct a callable object
3153
// [js] function callable(fn: Function): godot.Callable;
3254
// [js] function callable(thiz: godot.Object, fn: Function): godot.Callable;
@@ -387,8 +409,8 @@ namespace jsb
387409

388410
collection->Set(context, index, details).Check();
389411
JSB_LOG(VeryVerbose, "script %s define property(export) %s",
390-
impl::Helper::to_string_opt(isolate, target->Get(context, jsb_name(environment, name))),
391-
impl::Helper::to_string_opt(isolate, details->Get(context, jsb_name(environment, name))));
412+
_describe_export_name(isolate, context, environment, target),
413+
_describe_export_name(isolate, context, environment, details));
392414
}
393415

394416
// TODO: Cache for our cache functions?:

0 commit comments

Comments
 (0)