/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sts=4 et sw=4 tw=99: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "vm/GlobalObject.h" #include "jscntxt.h" #include "jsdate.h" #include "jsexn.h" #include "jsfriendapi.h" #include "jsmath.h" #include "json.h" #include "jsprototypes.h" #include "jsweakmap.h" #include "builtin/AtomicsObject.h" #include "builtin/Eval.h" #include "builtin/Intl.h" #include "builtin/MapObject.h" #include "builtin/ModuleObject.h" #include "builtin/Object.h" #include "builtin/Promise.h" #include "builtin/RegExp.h" #include "builtin/SelfHostingDefines.h" #include "builtin/SymbolObject.h" #include "builtin/TypedObject.h" #include "builtin/WeakMapObject.h" #include "builtin/WeakSetObject.h" #include "vm/Debugger.h" #include "vm/EnvironmentObject.h" #include "vm/HelperThreads.h" #include "vm/PIC.h" #include "vm/RegExpStatics.h" #include "vm/RegExpStaticsObject.h" #include "vm/StopIterationObject.h" #include "wasm/WasmJS.h" #include "jscompartmentinlines.h" #include "jsobjinlines.h" #include "jsscriptinlines.h" #include "vm/NativeObject-inl.h" using namespace js; struct ProtoTableEntry { const Class* clasp; ClassInitializerOp init; }; namespace js { #define DECLARE_PROTOTYPE_CLASS_INIT(name,code,init,clasp) \ extern JSObject* init(JSContext* cx, Handle obj); JS_FOR_EACH_PROTOTYPE(DECLARE_PROTOTYPE_CLASS_INIT) #undef DECLARE_PROTOTYPE_CLASS_INIT } // namespace js JSObject* js::InitViaClassSpec(JSContext* cx, Handle obj) { MOZ_CRASH("InitViaClassSpec() should not be called."); } static const ProtoTableEntry protoTable[JSProto_LIMIT] = { #define INIT_FUNC(name,code,init,clasp) { clasp, init }, #define INIT_FUNC_DUMMY(name,code,init,clasp) { nullptr, nullptr }, JS_FOR_PROTOTYPES(INIT_FUNC, INIT_FUNC_DUMMY) #undef INIT_FUNC_DUMMY #undef INIT_FUNC }; JS_FRIEND_API(const js::Class*) js::ProtoKeyToClass(JSProtoKey key) { MOZ_ASSERT(key < JSProto_LIMIT); return protoTable[key].clasp; } // This method is not in the header file to avoid having to include // TypedObject.h from GlobalObject.h. It is not generally perf // sensitive. TypedObjectModuleObject& js::GlobalObject::getTypedObjectModule() const { Value v = getConstructor(JSProto_TypedObject); // only gets called from contexts where TypedObject must be initialized MOZ_ASSERT(v.isObject()); return v.toObject().as(); } /* static */ bool GlobalObject::skipDeselectedConstructor(JSContext* cx, JSProtoKey key) { if (key == JSProto_WebAssembly) return !wasm::HasSupport(cx); #ifdef ENABLE_SHARED_ARRAY_BUFFER // Return true if the given constructor has been disabled at run-time. switch (key) { case JSProto_Atomics: case JSProto_SharedArrayBuffer: return !cx->compartment()->creationOptions().getSharedMemoryAndAtomicsEnabled(); default: return false; } #else return false; #endif } /* static */ bool GlobalObject::ensureConstructor(JSContext* cx, Handle global, JSProtoKey key) { if (global->isStandardClassResolved(key)) return true; return resolveConstructor(cx, global, key); } /* static*/ bool GlobalObject::resolveConstructor(JSContext* cx, Handle global, JSProtoKey key) { MOZ_ASSERT(!global->isStandardClassResolved(key)); // Prohibit collection of allocation metadata. Metadata builders shouldn't // need to observe lazily-constructed prototype objects coming into // existence. And assertions start to fail when the builder itself attempts // an allocation that re-entrantly tries to create the same prototype. AutoSuppressAllocationMetadataBuilder suppressMetadata(cx); // Constructor resolution may execute self-hosted scripts. These // self-hosted scripts do not call out to user code by construction. Allow // all scripts to execute, even in debuggee compartments that are paused. AutoSuppressDebuggeeNoExecuteChecks suppressNX(cx); // There are two different kinds of initialization hooks. One of them is // the class js::InitFoo hook, defined in a JSProtoKey-keyed table at the // top of this file. The other lives in the ClassSpec for classes that // define it. Classes may use one or the other, but not both. ClassInitializerOp init = protoTable[key].init; if (init == InitViaClassSpec) init = nullptr; const Class* clasp = ProtoKeyToClass(key); if (!init && !clasp) return true; // JSProto_Null or a compile-time-disabled feature. if (skipDeselectedConstructor(cx, key)) return true; // Some classes have no init routine, which means that they're disabled at // compile-time. We could try to enforce that callers never pass such keys // to resolveConstructor, but that would cramp the style of consumers like // GlobalObject::initStandardClasses that want to just carpet-bomb-call // ensureConstructor with every JSProtoKey. So it's easier to just handle // it here. bool haveSpec = clasp && clasp->specDefined(); if (!init && !haveSpec) return true; // See if there's an old-style initialization hook. if (init) { MOZ_ASSERT(!haveSpec); return init(cx, global); } // // Ok, we're doing it with a class spec. // bool isObjectOrFunction = key == JSProto_Function || key == JSProto_Object; // We need to create the prototype first, and immediately stash it in the // slot. This is so the following bootstrap ordering is possible: // * Object.prototype // * Function.prototype // * Function // * Object // // We get the above when Object is resolved before Function. If Function // is resolved before Object, we'll end up re-entering resolveConstructor // for Function, which is a problem. So if Function is being resolved // before Object.prototype exists, we just resolve Object instead, since we // know that Function will also be resolved before we return. if (key == JSProto_Function && global->getPrototype(JSProto_Object).isUndefined()) return resolveConstructor(cx, global, JSProto_Object); // We don't always have a prototype (i.e. Math and JSON). If we don't, // |createPrototype|, |prototypeFunctions|, and |prototypeProperties| // should all be null. RootedObject proto(cx); if (ClassObjectCreationOp createPrototype = clasp->specCreatePrototypeHook()) { proto = createPrototype(cx, key); if (!proto) return false; if (isObjectOrFunction) { // Make sure that creating the prototype didn't recursively resolve // our own constructor. We can't just assert that there's no // prototype; OOMs can result in incomplete resolutions in which // the prototype is saved but not the constructor. So use the same // criteria that protects entry into this function. MOZ_ASSERT(!global->isStandardClassResolved(key)); global->setPrototype(key, ObjectValue(*proto)); } } // Create the constructor. RootedObject ctor(cx, clasp->specCreateConstructorHook()(cx, key)); if (!ctor) return false; RootedId id(cx, NameToId(ClassName(key, cx))); if (isObjectOrFunction) { if (clasp->specShouldDefineConstructor()) { RootedValue ctorValue(cx, ObjectValue(*ctor)); if (!DefineProperty(cx, global, id, ctorValue, nullptr, nullptr, JSPROP_RESOLVING)) return false; } global->setConstructor(key, ObjectValue(*ctor)); } // If we're operating on the self-hosting global, we don't want any // functions and properties on the builtins and their prototypes. if (!cx->runtime()->isSelfHostingGlobal(global)) { if (const JSFunctionSpec* funs = clasp->specPrototypeFunctions()) { if (!JS_DefineFunctions(cx, proto, funs)) return false; } if (const JSPropertySpec* props = clasp->specPrototypeProperties()) { if (!JS_DefineProperties(cx, proto, props)) return false; } if (const JSFunctionSpec* funs = clasp->specConstructorFunctions()) { if (!JS_DefineFunctions(cx, ctor, funs)) return false; } if (const JSPropertySpec* props = clasp->specConstructorProperties()) { if (!JS_DefineProperties(cx, ctor, props)) return false; } } // If the prototype exists, link it with the constructor. if (proto && !LinkConstructorAndPrototype(cx, ctor, proto)) return false; // Call the post-initialization hook, if provided. if (FinishClassInitOp finishInit = clasp->specFinishInitHook()) { if (!finishInit(cx, ctor, proto)) return false; } if (!isObjectOrFunction) { // Any operations that modifies the global object should be placed // after any other fallible operations. // Fallible operation that modifies the global object. if (clasp->specShouldDefineConstructor()) { RootedValue ctorValue(cx, ObjectValue(*ctor)); if (!DefineProperty(cx, global, id, ctorValue, nullptr, nullptr, JSPROP_RESOLVING)) return false; } // Infallible operations that modify the global object. global->setConstructor(key, ObjectValue(*ctor)); if (proto) global->setPrototype(key, ObjectValue(*proto)); } return true; } /* static */ bool GlobalObject::initBuiltinConstructor(JSContext* cx, Handle global, JSProtoKey key, HandleObject ctor, HandleObject proto) { MOZ_ASSERT(!global->empty()); // reserved slots already allocated MOZ_ASSERT(key != JSProto_Null); MOZ_ASSERT(ctor); MOZ_ASSERT(proto); RootedId id(cx, NameToId(ClassName(key, cx))); MOZ_ASSERT(!global->lookup(cx, id)); RootedValue ctorValue(cx, ObjectValue(*ctor)); if (!DefineProperty(cx, global, id, ctorValue, nullptr, nullptr, JSPROP_RESOLVING)) return false; global->setConstructor(key, ObjectValue(*ctor)); global->setPrototype(key, ObjectValue(*proto)); return true; } // Resolve a "globalThis" self-referential property if necessary, // per a stage-3 proposal. https://github.com/tc39/ecma262/pull/702 // // We could also do this in |FinishObjectClassInit| to trim the global // resolve hook. Unfortunately, |ToWindowProxyIfWindow| doesn't work then: // the browser's |nsGlobalWindow::SetNewDocument| invokes Object init // *before* it sets the global's WindowProxy using |js::SetWindowProxy|. // // Refactoring global object creation code to support this approach is a // challenge for another day. /* static */ bool GlobalObject::maybeResolveGlobalThis(JSContext* cx, Handle global, bool* resolved) { if (global->getSlot(GLOBAL_THIS_RESOLVED).isUndefined()) { RootedValue v(cx, ObjectValue(*ToWindowProxyIfWindow(global))); if (!DefineProperty(cx, global, cx->names().globalThis, v, nullptr, nullptr, JSPROP_RESOLVING)) { return false; } *resolved = true; global->setSlot(GLOBAL_THIS_RESOLVED, BooleanValue(true)); } return true; } GlobalObject* GlobalObject::createInternal(JSContext* cx, const Class* clasp) { MOZ_ASSERT(clasp->flags & JSCLASS_IS_GLOBAL); MOZ_ASSERT(clasp->isTrace(JS_GlobalObjectTraceHook)); JSObject* obj = NewObjectWithGivenProto(cx, clasp, nullptr, SingletonObject); if (!obj) return nullptr; Rooted global(cx, &obj->as()); MOZ_ASSERT(global->isUnqualifiedVarObj()); // Initialize the private slot to null if present, as GC can call class // hooks before the caller gets to set this to a non-garbage value. if (clasp->flags & JSCLASS_HAS_PRIVATE) global->setPrivate(nullptr); Rooted lexical(cx, LexicalEnvironmentObject::createGlobal(cx, global)); if (!lexical) return nullptr; global->setReservedSlot(LEXICAL_ENVIRONMENT, ObjectValue(*lexical)); Rooted emptyGlobalScope(cx, GlobalScope::createEmpty(cx, ScopeKind::Global)); if (!emptyGlobalScope) return nullptr; global->setReservedSlot(EMPTY_GLOBAL_SCOPE, PrivateGCThingValue(emptyGlobalScope)); cx->compartment()->initGlobal(*global); if (!JSObject::setQualifiedVarObj(cx, global)) return nullptr; if (!JSObject::setDelegate(cx, global)) return nullptr; return global; } /* static */ GlobalObject* GlobalObject::new_(JSContext* cx, const Class* clasp, JSPrincipals* principals, JS::OnNewGlobalHookOption hookOption, const JS::CompartmentOptions& options) { MOZ_ASSERT(!cx->isExceptionPending()); MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment())); JSRuntime* rt = cx->runtime(); auto zoneSpecifier = options.creationOptions().zoneSpecifier(); Zone* zone; if (zoneSpecifier == JS::SystemZone) zone = rt->gc.systemZone; else if (zoneSpecifier == JS::FreshZone) zone = nullptr; else zone = static_cast(options.creationOptions().zonePointer()); JSCompartment* compartment = NewCompartment(cx, zone, principals, options); if (!compartment) return nullptr; // Lazily create the system zone. if (!rt->gc.systemZone && zoneSpecifier == JS::SystemZone) { rt->gc.systemZone = compartment->zone(); rt->gc.systemZone->isSystem = true; } Rooted global(cx); { AutoCompartment ac(cx, compartment); global = GlobalObject::createInternal(cx, clasp); if (!global) return nullptr; } if (hookOption == JS::FireOnNewGlobalHook) JS_FireOnNewGlobalObject(cx, global); return global; } LexicalEnvironmentObject& GlobalObject::lexicalEnvironment() const { return getReservedSlot(LEXICAL_ENVIRONMENT).toObject().as(); } GlobalScope& GlobalObject::emptyGlobalScope() const { const Value& v = getReservedSlot(EMPTY_GLOBAL_SCOPE); MOZ_ASSERT(v.isPrivateGCThing() && v.traceKind() == JS::TraceKind::Scope); return static_cast(v.toGCThing())->as(); } /* static */ bool GlobalObject::getOrCreateEval(JSContext* cx, Handle global, MutableHandleObject eval) { if (!getOrCreateObjectPrototype(cx, global)) return false; eval.set(&global->getSlot(EVAL).toObject()); return true; } bool GlobalObject::valueIsEval(const Value& val) { Value eval = getSlot(EVAL); return eval.isObject() && eval == val; } /* static */ bool GlobalObject::initStandardClasses(JSContext* cx, Handle global) { /* Define a top-level property 'undefined' with the undefined value. */ if (!DefineProperty(cx, global, cx->names().undefined, UndefinedHandleValue, nullptr, nullptr, JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_RESOLVING)) { return false; } // Resolve a "globalThis" self-referential property if necessary. bool resolved; if (!GlobalObject::maybeResolveGlobalThis(cx, global, &resolved)) { return false; } for (size_t k = 0; k < JSProto_LIMIT; ++k) { if (!ensureConstructor(cx, global, static_cast(k))) return false; } return true; } /** * Initializes a builtin constructor and its prototype without defining any * properties or functions on it. * * Used in self-hosting to install the few builtin constructors required by * self-hosted builtins. */ static bool InitBareBuiltinCtor(JSContext* cx, Handle global, JSProtoKey protoKey) { MOZ_ASSERT(cx->runtime()->isSelfHostingGlobal(global)); const Class* clasp = ProtoKeyToClass(protoKey); RootedObject proto(cx); proto = clasp->specCreatePrototypeHook()(cx, protoKey); if (!proto) return false; RootedObject ctor(cx, clasp->specCreateConstructorHook()(cx, protoKey)); if (!ctor) return false; return GlobalObject::initBuiltinConstructor(cx, global, protoKey, ctor, proto); } /** * The self-hosting global only gets a small subset of all standard classes. * Even those are only created as bare constructors without any properties * or functions. */ /* static */ bool GlobalObject::initSelfHostingBuiltins(JSContext* cx, Handle global, const JSFunctionSpec* builtins) { // Define a top-level property 'undefined' with the undefined value. if (!DefineProperty(cx, global, cx->names().undefined, UndefinedHandleValue, nullptr, nullptr, JSPROP_PERMANENT | JSPROP_READONLY)) { return false; } struct SymbolAndName { JS::SymbolCode code; const char* name; }; SymbolAndName wellKnownSymbols[] = { {JS::SymbolCode::isConcatSpreadable, "std_isConcatSpreadable"}, {JS::SymbolCode::iterator, "std_iterator"}, {JS::SymbolCode::match, "std_match"}, {JS::SymbolCode::matchAll, "std_matchAll"}, {JS::SymbolCode::replace, "std_replace"}, {JS::SymbolCode::search, "std_search"}, {JS::SymbolCode::species, "std_species"}, {JS::SymbolCode::split, "std_split"}, }; RootedValue symVal(cx); for (const auto& sym : wellKnownSymbols) { symVal.setSymbol(cx->wellKnownSymbols().get(sym.code)); if (!JS_DefineProperty(cx, global, sym.name, symVal, JSPROP_PERMANENT | JSPROP_READONLY)) { return false; } } return InitBareBuiltinCtor(cx, global, JSProto_Array) && InitBareBuiltinCtor(cx, global, JSProto_TypedArray) && InitBareBuiltinCtor(cx, global, JSProto_Uint8Array) && InitBareBuiltinCtor(cx, global, JSProto_Int32Array) && InitBareWeakMapCtor(cx, global) && InitStopIterationClass(cx, global) && DefineFunctions(cx, global, builtins, AsIntrinsic); } /* static */ bool GlobalObject::isRuntimeCodeGenEnabled(JSContext* cx, Handle global) { HeapSlot& v = global->getSlotRef(RUNTIME_CODEGEN_ENABLED); if (v.isUndefined()) { /* * If there are callbacks, make sure that the CSP callback is installed * and that it permits runtime code generation, then cache the result. */ JSCSPEvalChecker allows = cx->runtime()->securityCallbacks->contentSecurityPolicyAllows; Value boolValue = BooleanValue(!allows || allows(cx)); v.set(global, HeapSlot::Slot, RUNTIME_CODEGEN_ENABLED, boolValue); } return !v.isFalse(); } /* static */ bool GlobalObject::warnOnceAbout(JSContext* cx, HandleObject obj, WarnOnceFlag flag, unsigned errorNumber) { Rooted global(cx, &obj->global()); HeapSlot& v = global->getSlotRef(WARNED_ONCE_FLAGS); MOZ_ASSERT_IF(!v.isUndefined(), v.toInt32()); int32_t flags = v.isUndefined() ? 0 : v.toInt32(); if (!(flags & flag)) { if (!JS_ReportErrorFlagsAndNumberASCII(cx, JSREPORT_WARNING, GetErrorMessage, nullptr, errorNumber)) { return false; } if (v.isUndefined()) v.init(global, HeapSlot::Slot, WARNED_ONCE_FLAGS, Int32Value(flags | flag)); else v.set(global, HeapSlot::Slot, WARNED_ONCE_FLAGS, Int32Value(flags | flag)); } return true; } /* static */ JSFunction* GlobalObject::createConstructor(JSContext* cx, Native ctor, JSAtom* nameArg, unsigned length, gc::AllocKind kind, const JSJitInfo* jitInfo) { RootedAtom name(cx, nameArg); JSFunction* fun = NewNativeConstructor(cx, ctor, length, name, kind); if (!fun) return nullptr; if (jitInfo) fun->setJitInfo(jitInfo); return fun; } static NativeObject* CreateBlankProto(JSContext* cx, const Class* clasp, HandleObject proto, HandleObject global) { MOZ_ASSERT(clasp != &JSFunction::class_); RootedNativeObject blankProto(cx, NewNativeObjectWithGivenProto(cx, clasp, proto, SingletonObject)); if (!blankProto || !JSObject::setDelegate(cx, blankProto)) return nullptr; return blankProto; } /* static */ NativeObject* GlobalObject::createBlankPrototype(JSContext* cx, Handle global, const Class* clasp) { RootedObject objectProto(cx, getOrCreateObjectPrototype(cx, global)); if (!objectProto) return nullptr; return CreateBlankProto(cx, clasp, objectProto, global); } /* static */ NativeObject* GlobalObject::createBlankPrototypeInheriting(JSContext* cx, Handle global, const Class* clasp, HandleObject proto) { return CreateBlankProto(cx, clasp, proto, global); } bool js::LinkConstructorAndPrototype(JSContext* cx, JSObject* ctor_, JSObject* proto_, unsigned prototypeAttrs, unsigned constructorAttrs) { RootedObject ctor(cx, ctor_), proto(cx, proto_); RootedValue protoVal(cx, ObjectValue(*proto)); RootedValue ctorVal(cx, ObjectValue(*ctor)); return DefineProperty(cx, ctor, cx->names().prototype, protoVal, nullptr, nullptr, prototypeAttrs) && DefineProperty(cx, proto, cx->names().constructor, ctorVal, nullptr, nullptr, constructorAttrs); } bool js::DefinePropertiesAndFunctions(JSContext* cx, HandleObject obj, const JSPropertySpec* ps, const JSFunctionSpec* fs) { if (ps && !JS_DefineProperties(cx, obj, ps)) return false; if (fs && !JS_DefineFunctions(cx, obj, fs)) return false; return true; } bool js::DefineToStringTag(JSContext *cx, HandleObject obj, JSAtom* tag) { RootedId toStringTagId(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().toStringTag)); RootedValue tagString(cx, StringValue(tag)); return DefineProperty(cx, obj, toStringTagId, tagString, nullptr, nullptr, JSPROP_READONLY); } static void GlobalDebuggees_finalize(FreeOp* fop, JSObject* obj) { MOZ_ASSERT(fop->maybeOffMainThread()); fop->delete_((GlobalObject::DebuggerVector*) obj->as().getPrivate()); } static const ClassOps GlobalDebuggees_classOps = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, GlobalDebuggees_finalize }; static const Class GlobalDebuggees_class = { "GlobalDebuggee", JSCLASS_HAS_PRIVATE | JSCLASS_BACKGROUND_FINALIZE, &GlobalDebuggees_classOps }; GlobalObject::DebuggerVector* GlobalObject::getDebuggers() const { Value debuggers = getReservedSlot(DEBUGGERS); if (debuggers.isUndefined()) return nullptr; MOZ_ASSERT(debuggers.toObject().getClass() == &GlobalDebuggees_class); return (DebuggerVector*) debuggers.toObject().as().getPrivate(); } /* static */ GlobalObject::DebuggerVector* GlobalObject::getOrCreateDebuggers(JSContext* cx, Handle global) { assertSameCompartment(cx, global); DebuggerVector* debuggers = global->getDebuggers(); if (debuggers) return debuggers; NativeObject* obj = NewNativeObjectWithGivenProto(cx, &GlobalDebuggees_class, nullptr); if (!obj) return nullptr; debuggers = cx->new_(); if (!debuggers) return nullptr; obj->setPrivate(debuggers); global->setReservedSlot(DEBUGGERS, ObjectValue(*obj)); return debuggers; } /* static */ NativeObject* GlobalObject::getOrCreateForOfPICObject(JSContext* cx, Handle global) { assertSameCompartment(cx, global); NativeObject* forOfPIC = global->getForOfPICObject(); if (forOfPIC) return forOfPIC; forOfPIC = ForOfPIC::createForOfPICObject(cx, global); if (!forOfPIC) return nullptr; global->setReservedSlot(FOR_OF_PIC_CHAIN, ObjectValue(*forOfPIC)); return forOfPIC; } bool GlobalObject::hasRegExpStatics() const { return !getSlot(REGEXP_STATICS).isUndefined(); } /* static */ RegExpStatics* GlobalObject::getRegExpStatics(ExclusiveContext* cx, Handle global) { MOZ_ASSERT(cx); RegExpStaticsObject* resObj = nullptr; const Value& val = global->getSlot(REGEXP_STATICS); if (!val.isObject()) { MOZ_ASSERT(val.isUndefined()); resObj = RegExpStatics::create(cx, global); if (!resObj) return nullptr; global->initSlot(REGEXP_STATICS, ObjectValue(*resObj)); } else { resObj = &val.toObject().as(); } return static_cast(resObj->getPrivate(/* nfixed = */ 1)); } RegExpStatics* GlobalObject::getAlreadyCreatedRegExpStatics() const { const Value& val = this->getSlot(REGEXP_STATICS); MOZ_ASSERT(val.isObject()); return static_cast(val.toObject().as().getPrivate(/* nfixed = */ 1)); } /* static */ NativeObject* GlobalObject::getIntrinsicsHolder(JSContext* cx, Handle global) { Value slot = global->getReservedSlot(INTRINSICS); MOZ_ASSERT(slot.isUndefined() || slot.isObject()); if (slot.isObject()) return &slot.toObject().as(); Rooted intrinsicsHolder(cx); bool isSelfHostingGlobal = cx->runtime()->isSelfHostingGlobal(global); if (isSelfHostingGlobal) { intrinsicsHolder = global; } else { intrinsicsHolder = NewObjectWithGivenProto(cx, nullptr, TenuredObject); if (!intrinsicsHolder) return nullptr; } /* Define a property 'global' with the current global as its value. */ RootedValue globalValue(cx, ObjectValue(*global)); if (!DefineProperty(cx, intrinsicsHolder, cx->names().global, globalValue, nullptr, nullptr, JSPROP_PERMANENT | JSPROP_READONLY)) { return nullptr; } // Install the intrinsics holder in the intrinsics. global->setReservedSlot(INTRINSICS, ObjectValue(*intrinsicsHolder)); return intrinsicsHolder; } /* static */ bool GlobalObject::getSelfHostedFunction(JSContext* cx, Handle global, HandlePropertyName selfHostedName, HandleAtom name, unsigned nargs, MutableHandleValue funVal) { bool exists = false; if (!GlobalObject::maybeGetIntrinsicValue(cx, global, selfHostedName, funVal, &exists)) return false; if (exists) { RootedFunction fun(cx, &funVal.toObject().as()); if (fun->explicitName() == name) return true; if (fun->explicitName() == selfHostedName) { // This function was initially cloned because it was called by // other self-hosted code, so the clone kept its self-hosted name, // instead of getting the name it's intended to have in content // compartments. This can happen when a lazy builtin is initialized // after self-hosted code for another builtin used the same // function. In that case, we need to change the function's name, // which is ok because it can't have been exposed to content // before. fun->initAtom(name); return true; } // The function might be installed multiple times on the same or // different builtins, under different property names, so its name // might be neither "selfHostedName" nor "name". In that case, its // canonical name must've been set using the `_SetCanonicalName` // intrinsic. cx->runtime()->assertSelfHostedFunctionHasCanonicalName(cx, selfHostedName); return true; } RootedFunction fun(cx); if (!cx->runtime()->createLazySelfHostedFunctionClone(cx, selfHostedName, name, nargs, /* proto = */ nullptr, SingletonObject, &fun)) { return false; } funVal.setObject(*fun); return GlobalObject::addIntrinsicValue(cx, global, selfHostedName, funVal); } /* static */ bool GlobalObject::addIntrinsicValue(JSContext* cx, Handle global, HandlePropertyName name, HandleValue value) { RootedNativeObject holder(cx, GlobalObject::getIntrinsicsHolder(cx, global)); if (!holder) return false; uint32_t slot = holder->slotSpan(); RootedShape last(cx, holder->lastProperty()); Rooted base(cx, last->base()->unowned()); RootedId id(cx, NameToId(name)); Rooted child(cx, StackShape(base, id, slot, 0, 0)); Shape* shape = cx->zone()->propertyTree.getChild(cx, last, child); if (!shape) return false; if (!holder->setLastProperty(cx, shape)) return false; holder->setSlot(shape->slot(), value); return true; } /* static */ bool GlobalObject::ensureModulePrototypesCreated(JSContext *cx, Handle global) { return getOrCreateObject(cx, global, MODULE_PROTO, initModuleProto) && getOrCreateObject(cx, global, IMPORT_ENTRY_PROTO, initImportEntryProto) && getOrCreateObject(cx, global, EXPORT_ENTRY_PROTO, initExportEntryProto); }