Skip to content

Playground: bundle babylonjs-addons + polyfill ES2019/ES2022 gaps on Chakra#1700

Open
bkaradzic-microsoft wants to merge 6 commits into
BabylonJS:masterfrom
bkaradzic-microsoft:fix-bn-playground-polyfills
Open

Playground: bundle babylonjs-addons + polyfill ES2019/ES2022 gaps on Chakra#1700
bkaradzic-microsoft wants to merge 6 commits into
BabylonJS:masterfrom
bkaradzic-microsoft:fix-bn-playground-polyfills

Conversation

@bkaradzic-microsoft
Copy link
Copy Markdown
Contributor

Three small, self-contained Playground-side fixes:

1. ES2022 Error(message, options) polyfill for Chakra

Chakra's built-in Error constructor predates ES2022 and treats its second
argument as part of the message: new Error("hello", { cause: 42 }) produces
an Error whose .message is "[object Object]" and whose .cause is
undefined. Babylon.js's shader-compile error rethrow uses this signature,
so on every Chakra-based BN backend (the Win32 default) real
glslang/SPIRV-Cross diagnostics surface in stdout as
Error: [object Object] with no further detail, hiding the actual error.

Apps/Playground/Scripts/error_polyfill.js is loaded before any other
script. It self-detects ES2022 support (no-op on V8 / modern JSC) and on
Chakra patches Error plus the six standard subclasses (TypeError,
RangeError, SyntaxError, ReferenceError, URIError, EvalError)
with a thin wrapper that forwards the message string and attaches
cause via Object.defineProperty. Uses Reflect.construct to preserve
new.target, so class X extends Error { ... } keeps the correct
prototype chain.

Verified end-to-end on Chakra (build/win32 = NAPI_JAVASCRIPT_ENGINE=Chakra):
On a known shader-compile failure scenario the captured error transforms from
BJS - Error: [object Object] to
BJS - Error: SHADER ERROR\nERROR: 2 compilation errors. No code generated.
i.e. the actual glslang diagnostic now reaches stdout.

2. ES2019 String.prototype.trimEnd polyfill for Chakra

Chakra predates ES2019 and only implements the older trimLeft / trimRight.
Babylon.js's ShaderProcessor and NodeMaterial paths call trimEnd, so
on Chakra those paths throw TypeError: Object doesn't support property or method 'trimEnd'.

Apps/Playground/Scripts/string_polyfill.js aliases
String.prototype.trimEnd -> trimRight and trimStart -> trimLeft when the
ES2019 names are missing. Both pairs are spec-standard aliases, so engines
that already provide the modern names are untouched.

Verified: Merge Meshes with submeshes and Bones and morphs computation order now validate; the 'trimEnd' error is gone from the other three
known affected tests (which now fail for unrelated reasons tracked elsewhere).

3. Bundle babylonjs-addons in Playground

Babylon Native ships babylonjs, babylonjs-gui, babylonjs-loaders,
babylonjs-materials, and babylonjs-serializers alongside its Playground
app but not babylonjs-addons. The addons package hosts newer Babylon.js
components (Atmosphere, LiquidRenderingSceneComponent, ...) under the
global ADDONS namespace, so any Playground snippet that uses them throws
ReferenceError: 'ADDONS' is not defined.

Add the npm dependency, ship babylonjs.addons.js as a Playground resource
via BABYLON_SCRIPTS, and load it right after babylon.max.js so the
addons initialization sees a fully constructed BABYLON global.

Verified: all four Atmosphere snippets (Sunset, Night,
Night (Planet Origin), Space View) no longer throw the ADDONS
ReferenceError. They still fail on a separate Texture layers are not supported in Babylon Native (3D-texture / NativeEngine gap, tracked
separately) but the ADDONS-bundling fix is independently complete.

Risk

  • Polyfills are no-ops on engines that already implement the standard
    behaviour (V8 / modern JSC), so the V8 / JSC / iOS / macOS / Android paths
    are unchanged.
  • babylonjs-addons only adds one more LoadScript call; the npm package
    is in the same release line (^9.3.4) as the other Babylon scripts the
    Playground already ships.
  • All three changes are in the Playground app only; no engine / plugin code
    is touched.

Smoke regression check

Five known-passing indices (0, 39, 47, 165, 200) produce the same
exit code / pass count as upstream/master after this branch.

Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com

Chakra's built-in Error constructor predates ES2022. When called with
`new Error("msg", { cause: e })` it stringifies the options bag into
.message ("[object Object]") and never sets .cause. Babylon.js v6+
uses this signature in its shader-compile error rethrow, so on every
Chakra-based BN backend (the Win32 default) a real glslang/SPIRV-Cross
error surfaces in stdout as `Error: [object Object]` with no further
detail, hiding the actual diagnostic.

Add a small JS polyfill loaded before any other script that:
- Probes ES2022 support via `new Error("x", {cause: 42})` and returns
  early on engines that already implement the spec (V8, modern JSC).
- Replaces Error and the six standard subclasses with a thin wrapper
  that forwards the message string and attaches `cause` from the
  options bag via Object.defineProperty (non-enumerable, matches V8
  semantics).
- Uses Reflect.construct to preserve `new.target` so subclassing via
  `class X extends Error { ... }` keeps the correct prototype chain.
Chakra predates ES2019 and only implements the older trimLeft / trimRight
names. Babylon.js's ShaderProcessor and NodeMaterial code paths use the
newer trimEnd name, so on Chakra-based BN backends those paths fail with
`TypeError: Object doesn't support property or method 'trimEnd'`.

Add a tiny shim loaded right after error_polyfill.js that aliases
String.prototype.trimEnd -> trimRight and trimStart -> trimLeft when the
ES2019 names are missing. Both pairs are standard aliases in the spec,
so engines that already provide the modern names are not touched.
Babylon Native ships babylonjs, babylonjs-gui, babylonjs-loaders,
babylonjs-materials, and babylonjs-serializers alongside its Playground
app but not babylonjs-addons. The addons package hosts newer Babylon
.js components (Atmosphere, LiquidRenderingSceneComponent, ...) under
the global `ADDONS` namespace, so any Playground snippet that uses
them throws `ReferenceError: 'ADDONS' is not defined`.

Add the npm dependency, ship babylonjs.addons.js as a Playground
resource via BABYLON_SCRIPTS, and load it right after babylon.max.js
so the addons-side initialization sees a fully constructed BABYLON
global.
Copilot AI review requested due to automatic review settings May 16, 2026 00:31
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the Babylon Native Playground to improve compatibility with Chakra-based runtimes and to ship the babylonjs-addons bundle so Playground snippets using the ADDONS global can run.

Changes:

  • Load early JS polyfills for ES2022 Error(message, options) and ES2019 String.prototype.trimStart/trimEnd before Babylon scripts.
  • Add babylonjs-addons to the shipped Playground scripts and load it after babylon.max.js.
  • Add the new polyfill scripts to Playground build packaging and add the npm dependency.

Reviewed changes

Copilot reviewed 5 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
Apps/Playground/Shared/AppContext.cpp Loads the new polyfill scripts first and adds babylonjs.addons.js to the script load order.
Apps/Playground/Scripts/string_polyfill.js Adds Chakra-targeted trimStart/trimEnd alias polyfill.
Apps/Playground/Scripts/error_polyfill.js Adds Chakra-targeted Error(message, options) / cause polyfill for Error and standard subclasses.
Apps/Playground/CMakeLists.txt Packages the new polyfill scripts and adds babylonjs.addons.js to copied/embedded script resources.
Apps/package.json Adds babylonjs-addons dependency.
Apps/package-lock.json Records the resolved babylonjs-addons dependency (currently introducing an additional Babylon.js version).
Files not reviewed (1)
  • Apps/package-lock.json: Language not supported
Comments suppressed due to low confidence (2)

Apps/package.json:18

  • Using ^9.3.4 for babylonjs-addons allows it to resolve to a newer 9.x than the rest of the Babylon packages (and the bundled babylon.max.js). To avoid ABI/API mismatches and duplicate installs, pin babylonjs-addons to the same exact version as babylonjs (or update all Babylon packages together).
  "dependencies": {
    "babylonjs": "^9.3.4",
    "babylonjs-addons": "^9.3.4",
    "babylonjs-gltf2interface": "^9.3.4",
    "babylonjs-gui": "^9.3.4",
    "babylonjs-loaders": "^9.3.4",
    "babylonjs-materials": "^9.3.4",
    "babylonjs-serializers": "^9.3.4",

Apps/package-lock.json:2619

  • This nested node_modules/babylonjs-addons/node_modules/babylonjs entry indicates babylonjs-addons pulled in its own Babylon.js version (9.7.0). If the Playground loads babylon.max.js from the top-level install, this duplication is unnecessary at best and can become a runtime mismatch at worst; aligning package versions should eliminate this nested dependency.

Comment thread Polyfills/ErrorCause/Source/ErrorCause.cpp
Comment thread Apps/package-lock.json Outdated
Copy link
Copy Markdown
Contributor

@bghgary bghgary left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Reviewed by Copilot on behalf of @bghgary]

The addons bundling is fine, but the Error and String polyfills should not be JS scripts loaded by the Playground app — that only fixes it for the Playground. Any other app embedding Babylon Native with Chakra would hit the same failures and have to independently discover and ship the same workaround. These should be proper C++ polyfills (like Console, TextDecoder, etc.) with an Initialize(Napi::Env) entry point so every consumer gets them automatically.

Comment thread Apps/Playground/Scripts/error_polyfill.js Outdated
Comment thread Apps/Playground/Scripts/string_polyfill.js Outdated
Comment thread Apps/package.json
Addresses bghgary's review of PR BabylonJS#1700:

The Error(message, options) and String.prototype.trimEnd/trimStart
polyfills were originally loaded as JS scripts by the Playground app.
That only fixed Chakra in the Playground - every other app embedding
Babylon Native with Chakra would have had to independently discover
and ship the same workaround.

Move both into proper C++ polyfills under Polyfills/ with an
Initialize(Napi::Env) entry point, matching the Window / Canvas /
TextDecoder pattern:

  - Polyfills/ErrorCause/    (BABYLON_NATIVE_POLYFILL_ERRORCAUSE)
  - Polyfills/StringTrim/    (BABYLON_NATIVE_POLYFILL_STRINGTRIM)

Each polyfill embeds the verbatim ES-compliant JS shim as a raw string
and injects it via napi env.RunScript() inside Initialize(). The shims
remain no-ops on engines that already implement the relevant spec
(V8, modern JavaScriptCore).

AppContext now calls Babylon::Polyfills::ErrorCause::Initialize(env)
and Babylon::Polyfills::StringTrim::Initialize(env) inside the runtime
Dispatch lambda, before any other JS loads, and the two JS files plus
their ScriptLoader entries are removed.

Also addresses bghgary's second comment: babylonjs-addons resolved to
9.7.0 in the lockfile while the other Babylon packages were pinned at
9.3.4, opening a drift window where addons-side code could reference
APIs added after 9.3.4 that are not in the shipped babylon.max.js. Pin
the lockfile entry to 9.3.4 to match the rest.

Smoke-tested locally (Win32_x64_Sanitizers RelWithDebInfo): EXR Loader
and tests 0-5 all pass under ASAN+UBSan, no TypeError on trimEnd and
no Error: [object Object] surfaces.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
bkaradzic-microsoft and others added 2 commits May 27, 2026 14:24
CI on PR BabylonJS#1700 surfaced two distinct problems:

1. JSI N-API variant has no Napi::Env::RunScript(...). It exposes
   script evaluation through a free function Napi::Eval(env, source,
   url) declared in <napi/env.h>, which only exists in the JSI variant.
   Use __has_include(<napi/env.h>) to pick the right entry point at
   compile time so the same source builds against both the shared and
   JSI N-API surfaces. The shim itself stays identical.

2. Android builds the Playground via Apps/Playground/Android/
   BabylonNative/CMakeLists.txt, which has its own target_link_libraries
   list and is not aware of the top-level Apps/Playground/CMakeLists.txt
   additions. Add ErrorCause and StringTrim there too so AppContext.cpp
   can include the new public headers.

Verified ErrorCause and StringTrim compile under JSI locally
(build\\win32_jsi RelWithDebInfo, .lib files produced).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Two small fixes to the patched Error/TypeError/... constructors so they're
faithful drop-ins for the originals:

1. Redefine `Orig.prototype.constructor` to point at Patched (wrapped in
   try/catch in case the prototype is non-configurable). Before this,
   `(new Error()).constructor` resolved to `Orig` -- not the global
   `Error` (which is now Patched). `instanceof` already worked because
   the prototype chain is unchanged; this just fixes `.constructor`
   identity for callers that key off it (serializers, error reporters,
   anything doing `err.constructor === Error`).

2. `Object.setPrototypeOf(Patched, Orig)` so Patched inherits all of
   Orig's static methods/properties (`captureStackTrace`,
   `stackTraceLimit`, anything else V8 ships). Subsumes the previous
   one-off `Patched.captureStackTrace = Orig.captureStackTrace` copy.

Verified against a Chakra-like simulation (Node, class-based Old extends):
the patched constructor identity, prototype-chain `instanceof`, and
`Reflect.construct` new.target preservation all hold.

Addresses Copilot review feedback on PR BabylonJS#1700.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

@bghgary bghgary left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Reviewed by Copilot on behalf of @bghgary]

Apologies — my earlier review asked you to move these into Polyfills/ C++ modules; on reflection that was the wrong direction. They shouldn't exist at all (concerns inline). babylonjs-addons bundling is fine.

@@ -0,0 +1,132 @@
#include <Babylon/Polyfills/ErrorCause.h>
Copy link
Copy Markdown
Contributor

@bghgary bghgary May 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Drop (sorry — flipping my earlier ask). Either run Playground validation against an ES-compliant engine (Win32_x64_V8_D3D11 exists in CI), or bundle babel-standalone in the Playground to transpile snippets at load if we want to keep Chakra coverage. Chakra consumers use Babel themselves. If error surfacing through Node-API is degraded on Chakra, fix in JsRuntimeHost.

@@ -0,0 +1,48 @@
#include <Babylon/Polyfills/StringTrim.h>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Drop (sorry — same as ErrorCause). Switching Playground validation off Chakra makes this unnecessary.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants