Skip to content

JSC napi shim: writing to constructor.prototype pollutes global Object.prototype #172

@bghgary

Description

@bghgary

[Filed by Copilot on behalf of @bghgary]

On the JSC backend, constructors returned by napi_define_class / Napi::Function::New(...) have their user-visible .prototype slot pointing at the global Object.prototype instead of a fresh per-class object. Writing to it from C++ via func.Get("prototype").Set(...) therefore mutates Object.prototype globally, breaking for..in and prototype-chain lookups across the runtime.

V8 and ChakraCore return a fresh per-class object, so the bug is JSC-specific.

Repro

Napi::Function ctor = DefineClass(env, "Foo", {});
ctor.Get("prototype").As<Napi::Object>().Set("X", Napi::Number::New(env, 0));

In JS: 'X' in {}true (should be false), and for (const k in {}) ... enumerates X.

Root cause

JSC's JSObjectMakeConstructor defaults the new function's .prototype to Object.prototype when none is supplied. The napi-jsc shim should allocate a fresh object per class.

Workaround

Use InstanceValue / InstanceMethod / InstanceAccessor inside DefineClass. That path goes through napi's internal prototype slot, distinct from the user-visible .prototype on JSC, and works correctly on every backend.

Discovered while reviewing #169.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions