[Filed by Copilot on behalf of @bghgary]
Summary
On Chakra, napi_wrap inserts a hidden external object between the wrapped instance and the prototype that the constructor installed:
Before wrap: instance.__proto__ === Foo.prototype
After wrap: instance.__proto__ === <external>
<external>.__proto__ === Foo.prototype
This is observable from JS — Object.getPrototypeOf(new Foo()) !== Foo.prototype for any Napi::ObjectWrap-based class. V8 and JSC don't do this; their napi_wrap is transparent to the prototype chain.
Repro
Surfaced by test 4 in #177's napi class prototype isolation (#172) block:
expect(Object.getPrototypeOf(new Blob([]))).to.equal(Blob.prototype);
Passes on V8 and (post-fix) JSC; fails on Chakra. #177 works around this by using Blob.prototype.isPrototypeOf(blob) instead.
Cause
Core/Node-API/Source/js_native_api_chakra.cc napi_wrap (~L1662):
JsGetPrototype(value, &valuePrototype);
JsSetPrototype(external, valuePrototype);
JsSetPrototype(value, external);
FindWrapper / Unwrap / napi_remove_wrap all walk this chain.
Fix sketch
Store the external as a non-enumerable own property keyed by a hidden symbol on the wrapped value; rewrite FindWrapper / Unwrap / napi_remove_wrap to look it up by property instead of walking the chain.
[Filed by Copilot on behalf of @bghgary]
Summary
On Chakra,
napi_wrapinserts a hidden external object between the wrapped instance and the prototype that the constructor installed:This is observable from JS —
Object.getPrototypeOf(new Foo()) !== Foo.prototypefor anyNapi::ObjectWrap-based class. V8 and JSC don't do this; theirnapi_wrapis transparent to the prototype chain.Repro
Surfaced by test 4 in #177's
napi class prototype isolation (#172)block:Passes on V8 and (post-fix) JSC; fails on Chakra. #177 works around this by using
Blob.prototype.isPrototypeOf(blob)instead.Cause
Core/Node-API/Source/js_native_api_chakra.ccnapi_wrap(~L1662):FindWrapper/Unwrap/napi_remove_wrapall walk this chain.Fix sketch
Store the external as a non-enumerable own property keyed by a hidden symbol on the wrapped value; rewrite
FindWrapper/Unwrap/napi_remove_wrapto look it up by property instead of walking the chain.