Loading js/public/Realm.h +6 −0 Original line number Diff line number Diff line Loading @@ -94,6 +94,12 @@ extern JS_PUBLIC_API JSObject* GetRealmGlobalOrNull(Realm* realm); // for Number). extern JS_PUBLIC_API bool InitRealmStandardClasses(JSContext* cx); // If the current realm has the non-standard freezeBuiltins option set to true, // freeze the constructor object and seal the prototype. extern JS_PUBLIC_API bool MaybeFreezeCtorAndPrototype(JSContext* cx, HandleObject ctor, HandleObject maybeProto); /* * Ways to get various per-Realm objects. All the getters declared below operate * on the JSContext's current Realm. Loading js/public/RealmOptions.h +10 −0 Original line number Diff line number Diff line Loading @@ -252,6 +252,15 @@ class JS_PUBLIC_API RealmCreationOptions { return *this; } // Non-standard option to freeze certain builtin constructors and seal their // prototypes. Also defines these constructors on the global as non-writable // and non-configurable. bool freezeBuiltins() const { return freezeBuiltins_; } RealmCreationOptions& setFreezeBuiltins(bool flag) { freezeBuiltins_ = flag; return *this; } uint64_t profilerRealmID() const { return profilerRealmID_; } RealmCreationOptions& setProfilerRealmID(uint64_t id) { profilerRealmID_ = id; Loading Loading @@ -282,6 +291,7 @@ class JS_PUBLIC_API RealmCreationOptions { bool propertyErrorMessageFix_ = false; bool iteratorHelpers_ = false; bool secureContext_ = false; bool freezeBuiltins_ = false; }; /** Loading js/src/jit-test/tests/basic/freeze-builtins.js 0 → 100644 +21 −0 Original line number Diff line number Diff line var g = newGlobal({freezeBuiltins: true}); g.evaluate("" + function checkFrozen(name) { // Check constructor on the global is non-writable/non-configurable. let desc = Object.getOwnPropertyDescriptor(this, name); assertEq(desc.writable, false); assertEq(desc.configurable, false); // Constructor must be frozen. let ctor = desc.value; assertEq(Object.isFrozen(ctor), true); // Prototype must be sealed. if (ctor.prototype) { assertEq(Object.isSealed(ctor.prototype), true); } }); g.checkFrozen("Object"); g.checkFrozen("Array"); g.checkFrozen("Function"); js/src/shell/js.cpp +10 −0 Original line number Diff line number Diff line Loading @@ -7344,6 +7344,13 @@ static bool NewGlobal(JSContext* cx, unsigned argc, Value* vp) { creationOptions.setCoopAndCoepEnabled(v.toBoolean()); } if (!JS_GetProperty(cx, opts, "freezeBuiltins", &v)) { return false; } if (v.isBoolean()) { creationOptions.setFreezeBuiltins(v.toBoolean()); } // On the web, the SharedArrayBuffer constructor is not installed as a // global property in pages that aren't isolated in a separate process (and // thus can't allow the structured cloning of shared memory). Specify false Loading Loading @@ -9790,6 +9797,9 @@ static const JSFunctionSpecWithHelp shell_functions[] = { " (default false).\n" " useWindowProxy: the global will be created with a WindowProxy attached. In this\n" " case, the WindowProxy will be returned.\n" " freezeBuiltins: certain builtin constructors will be frozen when created and\n" " their prototypes will be sealed. These constructors will be defined on the\n" " global as non-configurable and non-writable.\n" " immutablePrototype: whether the global's prototype is immutable.\n" " principal: if present, its value converted to a number must be an\n" " integer that fits in 32 bits; use that as the new realm's\n" Loading js/src/vm/GlobalObject.cpp +31 −2 Original line number Diff line number Diff line Loading @@ -209,6 +209,27 @@ bool GlobalObject::skipDeselectedConstructor(JSContext* cx, JSProtoKey key) { } } static bool ShouldFreezeBuiltin(JSProtoKey key) { switch (key) { case JSProto_Object: case JSProto_Array: case JSProto_Function: return true; default: return false; } } static unsigned GetAttrsForResolvedGlobal(GlobalObject* global, JSProtoKey key) { unsigned attrs = JSPROP_RESOLVING; if (global->realm()->creationOptions().freezeBuiltins() && ShouldFreezeBuiltin(key)) { attrs |= JSPROP_PERMANENT | JSPROP_READONLY; } return attrs; } /* static*/ bool GlobalObject::resolveConstructor(JSContext* cx, Handle<GlobalObject*> global, Loading Loading @@ -326,7 +347,8 @@ bool GlobalObject::resolveConstructor(JSContext* cx, if (isObjectOrFunction) { if (clasp->specShouldDefineConstructor()) { RootedValue ctorValue(cx, ObjectValue(*ctor)); if (!DefineDataProperty(cx, global, id, ctorValue, JSPROP_RESOLVING)) { unsigned attrs = GetAttrsForResolvedGlobal(global, key); if (!DefineDataProperty(cx, global, id, ctorValue, attrs)) { return false; } } Loading Loading @@ -371,6 +393,12 @@ bool GlobalObject::resolveConstructor(JSContext* cx, } } if (ShouldFreezeBuiltin(key)) { if (!JS::MaybeFreezeCtorAndPrototype(cx, ctor, proto)) { return false; } } if (!isObjectOrFunction) { // Any operations that modifies the global object should be placed // after any other fallible operations. Loading @@ -396,7 +424,8 @@ bool GlobalObject::resolveConstructor(JSContext* cx, if (shouldReallyDefine) { RootedValue ctorValue(cx, ObjectValue(*ctor)); if (!DefineDataProperty(cx, global, id, ctorValue, JSPROP_RESOLVING)) { unsigned attrs = GetAttrsForResolvedGlobal(global, key); if (!DefineDataProperty(cx, global, id, ctorValue, attrs)) { return false; } } Loading Loading
js/public/Realm.h +6 −0 Original line number Diff line number Diff line Loading @@ -94,6 +94,12 @@ extern JS_PUBLIC_API JSObject* GetRealmGlobalOrNull(Realm* realm); // for Number). extern JS_PUBLIC_API bool InitRealmStandardClasses(JSContext* cx); // If the current realm has the non-standard freezeBuiltins option set to true, // freeze the constructor object and seal the prototype. extern JS_PUBLIC_API bool MaybeFreezeCtorAndPrototype(JSContext* cx, HandleObject ctor, HandleObject maybeProto); /* * Ways to get various per-Realm objects. All the getters declared below operate * on the JSContext's current Realm. Loading
js/public/RealmOptions.h +10 −0 Original line number Diff line number Diff line Loading @@ -252,6 +252,15 @@ class JS_PUBLIC_API RealmCreationOptions { return *this; } // Non-standard option to freeze certain builtin constructors and seal their // prototypes. Also defines these constructors on the global as non-writable // and non-configurable. bool freezeBuiltins() const { return freezeBuiltins_; } RealmCreationOptions& setFreezeBuiltins(bool flag) { freezeBuiltins_ = flag; return *this; } uint64_t profilerRealmID() const { return profilerRealmID_; } RealmCreationOptions& setProfilerRealmID(uint64_t id) { profilerRealmID_ = id; Loading Loading @@ -282,6 +291,7 @@ class JS_PUBLIC_API RealmCreationOptions { bool propertyErrorMessageFix_ = false; bool iteratorHelpers_ = false; bool secureContext_ = false; bool freezeBuiltins_ = false; }; /** Loading
js/src/jit-test/tests/basic/freeze-builtins.js 0 → 100644 +21 −0 Original line number Diff line number Diff line var g = newGlobal({freezeBuiltins: true}); g.evaluate("" + function checkFrozen(name) { // Check constructor on the global is non-writable/non-configurable. let desc = Object.getOwnPropertyDescriptor(this, name); assertEq(desc.writable, false); assertEq(desc.configurable, false); // Constructor must be frozen. let ctor = desc.value; assertEq(Object.isFrozen(ctor), true); // Prototype must be sealed. if (ctor.prototype) { assertEq(Object.isSealed(ctor.prototype), true); } }); g.checkFrozen("Object"); g.checkFrozen("Array"); g.checkFrozen("Function");
js/src/shell/js.cpp +10 −0 Original line number Diff line number Diff line Loading @@ -7344,6 +7344,13 @@ static bool NewGlobal(JSContext* cx, unsigned argc, Value* vp) { creationOptions.setCoopAndCoepEnabled(v.toBoolean()); } if (!JS_GetProperty(cx, opts, "freezeBuiltins", &v)) { return false; } if (v.isBoolean()) { creationOptions.setFreezeBuiltins(v.toBoolean()); } // On the web, the SharedArrayBuffer constructor is not installed as a // global property in pages that aren't isolated in a separate process (and // thus can't allow the structured cloning of shared memory). Specify false Loading Loading @@ -9790,6 +9797,9 @@ static const JSFunctionSpecWithHelp shell_functions[] = { " (default false).\n" " useWindowProxy: the global will be created with a WindowProxy attached. In this\n" " case, the WindowProxy will be returned.\n" " freezeBuiltins: certain builtin constructors will be frozen when created and\n" " their prototypes will be sealed. These constructors will be defined on the\n" " global as non-configurable and non-writable.\n" " immutablePrototype: whether the global's prototype is immutable.\n" " principal: if present, its value converted to a number must be an\n" " integer that fits in 32 bits; use that as the new realm's\n" Loading
js/src/vm/GlobalObject.cpp +31 −2 Original line number Diff line number Diff line Loading @@ -209,6 +209,27 @@ bool GlobalObject::skipDeselectedConstructor(JSContext* cx, JSProtoKey key) { } } static bool ShouldFreezeBuiltin(JSProtoKey key) { switch (key) { case JSProto_Object: case JSProto_Array: case JSProto_Function: return true; default: return false; } } static unsigned GetAttrsForResolvedGlobal(GlobalObject* global, JSProtoKey key) { unsigned attrs = JSPROP_RESOLVING; if (global->realm()->creationOptions().freezeBuiltins() && ShouldFreezeBuiltin(key)) { attrs |= JSPROP_PERMANENT | JSPROP_READONLY; } return attrs; } /* static*/ bool GlobalObject::resolveConstructor(JSContext* cx, Handle<GlobalObject*> global, Loading Loading @@ -326,7 +347,8 @@ bool GlobalObject::resolveConstructor(JSContext* cx, if (isObjectOrFunction) { if (clasp->specShouldDefineConstructor()) { RootedValue ctorValue(cx, ObjectValue(*ctor)); if (!DefineDataProperty(cx, global, id, ctorValue, JSPROP_RESOLVING)) { unsigned attrs = GetAttrsForResolvedGlobal(global, key); if (!DefineDataProperty(cx, global, id, ctorValue, attrs)) { return false; } } Loading Loading @@ -371,6 +393,12 @@ bool GlobalObject::resolveConstructor(JSContext* cx, } } if (ShouldFreezeBuiltin(key)) { if (!JS::MaybeFreezeCtorAndPrototype(cx, ctor, proto)) { return false; } } if (!isObjectOrFunction) { // Any operations that modifies the global object should be placed // after any other fallible operations. Loading @@ -396,7 +424,8 @@ bool GlobalObject::resolveConstructor(JSContext* cx, if (shouldReallyDefine) { RootedValue ctorValue(cx, ObjectValue(*ctor)); if (!DefineDataProperty(cx, global, id, ctorValue, JSPROP_RESOLVING)) { unsigned attrs = GetAttrsForResolvedGlobal(global, key); if (!DefineDataProperty(cx, global, id, ctorValue, attrs)) { return false; } } Loading