Skip to content

Commit 0bff942

Browse files
File polyfill: drop JS_PROTOTYPE_CHAIN_SHIM workaround now that BabylonJS#177 landed
BabylonJS#177 fixed the JSC napi shim's Object.prototype pollution by passing the per-class JSClassRef to JSObjectMakeConstructor, so napi-defined classes now get a real per-class .prototype object instead of aliasing the global Object.prototype. That removes the entire reason for the JS_PROTOTYPE_CHAIN_SHIM and the Napi::Eval / try/catch dance: we can wire File.prototype's [[Prototype]] to Blob.prototype with a direct Object.setPrototypeOf call. Drops ~50 lines of explanatory comment + shim + Eval-with-IsExceptionPending guard in File::Initialize down to a 4-line napi call. `file instanceof Blob` regression coverage remains in tests.ts:1564 and BabylonJS#177's `describe("napi class prototype isolation (BabylonJS#172)")` block at tests.ts:1271 (uses Blob — and File extends Blob — to assert that napi-defined classes don't share Object.prototype). Local Chakra build clean. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent d5cd731 commit 0bff942

1 file changed

Lines changed: 17 additions & 42 deletions

File tree

Polyfills/File/Source/File.cpp

Lines changed: 17 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -12,38 +12,6 @@ namespace Babylon::Polyfills::Internal
1212
{
1313
constexpr auto JS_FILE_CONSTRUCTOR_NAME = "File";
1414
constexpr auto JS_BLOB_CONSTRUCTOR_NAME = "Blob";
15-
16-
// Wire File.prototype to inherit from Blob.prototype so that
17-
// `new File(...) instanceof Blob === true`. The shim runs entirely
18-
// in JS so each engine's quirks are handled by the JS try/catch:
19-
//
20-
// - V8 / Chakra: `File.prototype` is the real prototype that
21-
// instances use, so the direct setPrototypeOf succeeds and the
22-
// probe path is skipped.
23-
// - JSC: `File.prototype` aliases Object.prototype (napi_define_class
24-
// wraps JSObjectMakeConstructor; see JsRH#172). The direct call
25-
// throws TypeError, the catch swallows it, and the probe path
26-
// discovers the real napi-internal prototype via
27-
// `Object.getPrototypeOf(new File())` and sets its [[Prototype]]
28-
// to Blob.prototype.
29-
//
30-
// Doing this in JS rather than via Napi::Function::Call avoids a
31-
// JSC napi-shim quirk where setPrototypeOf on Object.prototype
32-
// escapes as an uncaught error instead of being capturable via
33-
// IsExceptionPending.
34-
constexpr auto JS_PROTOTYPE_CHAIN_SHIM = R"JS(
35-
(function() {
36-
if (typeof File !== 'function' || typeof Blob !== 'function') return;
37-
var blobProto = Blob.prototype;
38-
try { Object.setPrototypeOf(File.prototype, blobProto); } catch (e) {}
39-
try {
40-
var probe = new File([], '');
41-
if (!(probe instanceof Blob)) {
42-
Object.setPrototypeOf(Object.getPrototypeOf(probe), blobProto);
43-
}
44-
} catch (e) {}
45-
})();
46-
)JS";
4715
}
4816

4917
void File::Initialize(Napi::Env env)
@@ -86,16 +54,23 @@ namespace Babylon::Polyfills::Internal
8654

8755
global.Set(JS_FILE_CONSTRUCTOR_NAME, func);
8856

89-
// Wire File.prototype -> Blob.prototype via a tiny JS shim. See
90-
// the JS_PROTOTYPE_CHAIN_SHIM comment for engine-specific rationale.
91-
Napi::Eval(env, JS_PROTOTYPE_CHAIN_SHIM, "JsRuntimeHost-File-PrototypeChainShim.js");
92-
if (env.IsExceptionPending())
93-
{
94-
// The shim itself wraps every operation in try/catch, so this
95-
// should never fire. Belt-and-braces: clear so Initialize stays
96-
// best-effort and the rest of the polyfill remains installed.
97-
env.GetAndClearPendingException();
98-
}
57+
// Wire File.prototype's [[Prototype]] to Blob.prototype so
58+
// `new File(...) instanceof Blob === true`. WHATWG specs File as
59+
// a Blob subtype; BJS core (fileTools, Offline/database,
60+
// abstractEngine, thinNativeEngine) branches on `instanceof Blob`
61+
// and needs File inputs to satisfy that check.
62+
//
63+
// Relies on JsRH #177: prior to that fix the JSC napi shim aliased
64+
// every napi class's `.prototype` to Object.prototype, so this
65+
// assignment would have polluted every object globally. With #177
66+
// in place each napi class has its own `.prototype` object and
67+
// this is a plain prototype-chain edit.
68+
auto setPrototypeOf = env.Global().Get("Object").As<Napi::Object>()
69+
.Get("setPrototypeOf").As<Napi::Function>();
70+
setPrototypeOf.Call({
71+
func.Get("prototype"),
72+
blob.As<Napi::Function>().Get("prototype"),
73+
});
9974
}
10075

10176
File::File(const Napi::CallbackInfo& info)

0 commit comments

Comments
 (0)