#include "vm/GlobalObject.h"
#include "jsdate.h"
#include "jsexn.h"
#include "jsfriendapi.h"
#include "builtin/AtomicsObject.h"
#include "builtin/BigInt.h"
#include "builtin/DataViewObject.h"
#include "builtin/Eval.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/Stream.h"
#include "builtin/Symbol.h"
#include "builtin/TypedObject.h"
#include "builtin/WeakMapObject.h"
#include "builtin/WeakSetObject.h"
#include "gc/FreeOp.h"
#include "js/ProtoKey.h"
#include "vm/Debugger.h"
#include "vm/EnvironmentObject.h"
#include "vm/HelperThreads.h"
#include "vm/JSContext.h"
#include "vm/PIC.h"
#include "vm/RegExpStatics.h"
#include "vm/RegExpStaticsObject.h"
#include "vm/JSObject-inl.h"
#include "vm/JSScript-inl.h"
#include "vm/NativeObject-inl.h"
#include "vm/Realm-inl.h"
using namespace js;
struct ProtoTableEntry {
const Class* clasp;
ClassInitializerOp init;
};
namespace js {
extern const Class IntlClass;
extern const Class JSONClass;
extern const Class MathClass;
extern const Class WebAssemblyClass;
#define DECLARE_PROTOTYPE_CLASS_INIT(name, init, clasp) \
extern JSObject* init(JSContext* cx, Handle<GlobalObject*> global);
JS_FOR_EACH_PROTOTYPE(DECLARE_PROTOTYPE_CLASS_INIT)
#undef DECLARE_PROTOTYPE_CLASS_INIT
}
JSObject* js::InitViaClassSpec(JSContext* cx, Handle<GlobalObject*> global) {
MOZ_CRASH("InitViaClassSpec() should not be called.");
}
static const ProtoTableEntry protoTable[JSProto_LIMIT] = {
#define INIT_FUNC(name, init, clasp) {clasp, init},
#define INIT_FUNC_DUMMY(name, 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;
}
TypedObjectModuleObject& js::GlobalObject::getTypedObjectModule() const {
Value v = getConstructor(JSProto_TypedObject);
MOZ_ASSERT(v.isObject());
return v.toObject().as<TypedObjectModuleObject>();
}
bool GlobalObject::skipDeselectedConstructor(JSContext* cx, JSProtoKey key) {
switch (key) {
case JSProto_WebAssembly:
return !wasm::HasSupport(cx);
case JSProto_ReadableStream:
case JSProto_ReadableStreamDefaultReader:
case JSProto_ReadableStreamDefaultController:
case JSProto_ReadableByteStreamController:
case JSProto_ByteLengthQueuingStrategy:
case JSProto_CountQueuingStrategy:
return !cx->realm()->creationOptions().getStreamsEnabled();
case JSProto_BigInt:
return !cx->realm()->creationOptions().getBigIntEnabled();
case JSProto_Atomics:
case JSProto_SharedArrayBuffer:
return !cx->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled();
default:
return false;
}
}
bool GlobalObject::resolveConstructor(JSContext* cx,
Handle<GlobalObject*> global,
JSProtoKey key, IfClassIsDisabled mode) {
MOZ_ASSERT(key != JSProto_Null);
MOZ_ASSERT(!global->isStandardClassResolved(key));
MOZ_ASSERT(cx->compartment() == global->compartment());
AutoRealm ar(cx, global);
if (global->zone()->createdForHelperThread()) {
return resolveOffThreadConstructor(cx, global, key);
}
MOZ_ASSERT(!cx->helperThread());
AutoSuppressAllocationMetadataBuilder suppressMetadata(cx);
AutoSuppressDebuggeeNoExecuteChecks suppressNX(cx);
ClassInitializerOp init = protoTable[key].init;
if (init == InitViaClassSpec) {
init = nullptr;
}
const Class* clasp = ProtoKeyToClass(key);
if ((!init && !clasp) || skipDeselectedConstructor(cx, key)) {
if (mode == IfClassIsDisabled::Throw) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_CONSTRUCTOR_DISABLED,
clasp ? clasp->name : "constructor");
return false;
}
return true;
}
bool haveSpec = clasp && clasp->specDefined();
if (!init && !haveSpec) {
return true;
}
if (init) {
MOZ_ASSERT(!haveSpec);
return init(cx, global);
}
bool isObjectOrFunction = key == JSProto_Function || key == JSProto_Object;
if (key == JSProto_Function &&
global->getPrototype(JSProto_Object).isUndefined()) {
return resolveConstructor(cx, global, JSProto_Object,
IfClassIsDisabled::DoNothing);
}
RootedObject proto(cx);
if (ClassObjectCreationOp createPrototype =
clasp->specCreatePrototypeHook()) {
proto = createPrototype(cx, key);
if (!proto) {
return false;
}
if (isObjectOrFunction) {
MOZ_ASSERT(!global->isStandardClassResolved(key));
global->setPrototype(key, ObjectValue(*proto));
}
}
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 (!DefineDataProperty(cx, global, id, ctorValue, JSPROP_RESOLVING)) {
return false;
}
}
global->setConstructor(key, ObjectValue(*ctor));
}
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 (proto && !LinkConstructorAndPrototype(cx, ctor, proto)) {
return false;
}
if (FinishClassInitOp finishInit = clasp->specFinishInitHook()) {
if (!finishInit(cx, ctor, proto)) {
return false;
}
}
if (!isObjectOrFunction) {
if (clasp->specShouldDefineConstructor()) {
RootedValue ctorValue(cx, ObjectValue(*ctor));
if (!DefineDataProperty(cx, global, id, ctorValue, JSPROP_RESOLVING)) {
return false;
}
}
global->setConstructor(key, ObjectValue(*ctor));
if (proto) {
global->setPrototype(key, ObjectValue(*proto));
}
}
return true;
}
bool GlobalObject::maybeResolveGlobalThis(JSContext* cx,
Handle<GlobalObject*> global,
bool* resolved) {
if (global->getSlot(GLOBAL_THIS_RESOLVED).isUndefined()) {
RootedValue v(cx, ObjectValue(*ToWindowProxyIfWindow(global)));
if (!DefineDataProperty(cx, global, cx->names().globalThis, v,
JSPROP_RESOLVING)) {
return false;
}
*resolved = true;
global->setSlot(GLOBAL_THIS_RESOLVED, BooleanValue(true));
}
return true;
}
JSObject* GlobalObject::createObject(JSContext* cx,
Handle<GlobalObject*> global,
unsigned slot, ObjectInitOp init) {
if (global->zone()->createdForHelperThread()) {
return createOffThreadObject(cx, global, slot);
}
MOZ_ASSERT(!cx->helperThread());
if (!init(cx, global)) {
return nullptr;
}
return &global->getSlot(slot).toObject();
}
const Class GlobalObject::OffThreadPlaceholderObject::class_ = {
"off-thread-prototype-placeholder", JSCLASS_HAS_RESERVED_SLOTS(1)};
GlobalObject::OffThreadPlaceholderObject*
GlobalObject::OffThreadPlaceholderObject::New(JSContext* cx, unsigned slot) {
Rooted<OffThreadPlaceholderObject*> placeholder(cx);
placeholder = NewObjectWithGivenTaggedProto<OffThreadPlaceholderObject>(
cx, AsTaggedProto(nullptr));
if (!placeholder) {
return nullptr;
}
placeholder->setReservedSlot(SlotIndexSlot, Int32Value(slot));
return placeholder;
}
inline int32_t GlobalObject::OffThreadPlaceholderObject::getSlotIndex() const {
return getReservedSlot(SlotIndexSlot).toInt32();
}
bool GlobalObject::resolveOffThreadConstructor(JSContext* cx,
Handle<GlobalObject*> global,
JSProtoKey key) {
MOZ_ASSERT(global->zone()->createdForHelperThread());
MOZ_ASSERT(key == JSProto_Object || key == JSProto_Function ||
key == JSProto_Array || key == JSProto_RegExp);
Rooted<OffThreadPlaceholderObject*> placeholder(cx);
placeholder = OffThreadPlaceholderObject::New(cx, prototypeSlot(key));
if (!placeholder) {
return false;
}
if (key == JSProto_Object &&
!JSObject::setFlags(cx, placeholder, BaseShape::IMMUTABLE_PROTOTYPE)) {
return false;
}
if (key == JSProto_Object || key == JSProto_Function ||
key == JSProto_Array) {
ObjectGroupRealm& realm = ObjectGroupRealm::getForNewObject(cx);
if (!JSObject::setNewGroupUnknown(cx, realm, placeholder->getClass(),
placeholder)) {
return false;
}
}
global->setPrototype(key, ObjectValue(*placeholder));
global->setConstructor(key, MagicValue(JS_OFF_THREAD_CONSTRUCTOR));
return true;
}
JSObject* GlobalObject::createOffThreadObject(JSContext* cx,
Handle<GlobalObject*> global,
unsigned slot) {
MOZ_ASSERT(global->zone()->createdForHelperThread());
MOZ_ASSERT(slot == GENERATOR_FUNCTION_PROTO || slot == ASYNC_FUNCTION_PROTO ||
slot == ASYNC_GENERATOR || slot == MODULE_PROTO ||
slot == IMPORT_ENTRY_PROTO || slot == EXPORT_ENTRY_PROTO ||
slot == REQUESTED_MODULE_PROTO);
auto placeholder = OffThreadPlaceholderObject::New(cx, slot);
if (!placeholder) {
return nullptr;
}
global->setSlot(slot, ObjectValue(*placeholder));
return placeholder;
}
JSObject* GlobalObject::getPrototypeForOffThreadPlaceholder(JSObject* obj) {
auto placeholder = &obj->as<OffThreadPlaceholderObject>();
return &getSlot(placeholder->getSlotIndex()).toObject();
}
bool GlobalObject::initBuiltinConstructor(JSContext* cx,
Handle<GlobalObject*> global,
JSProtoKey key, HandleObject ctor,
HandleObject proto) {
MOZ_ASSERT(!global->empty()); 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 (!DefineDataProperty(cx, global, id, ctorValue, JSPROP_RESOLVING)) {
return false;
}
global->setConstructor(key, ObjectValue(*ctor));
global->setPrototype(key, ObjectValue(*proto));
return true;
}
static bool ThrowTypeError(JSContext* cx, unsigned argc, Value* vp) {
ThrowTypeErrorBehavior(cx);
return false;
}
JSObject* GlobalObject::getOrCreateThrowTypeError(
JSContext* cx, Handle<GlobalObject*> global) {
Value v = global->getReservedSlot(THROWTYPEERROR);
if (v.isObject()) {
return &v.toObject();
}
MOZ_ASSERT(v.isUndefined());
RootedFunction throwTypeError(
cx, NewNativeFunction(cx, ThrowTypeError, 0, nullptr));
if (!throwTypeError || !PreventExtensions(cx, throwTypeError)) {
return nullptr;
}
Rooted<PropertyDescriptor> nonConfigurableDesc(cx);
nonConfigurableDesc.setAttributes(JSPROP_PERMANENT | JSPROP_IGNORE_READONLY |
JSPROP_IGNORE_ENUMERATE |
JSPROP_IGNORE_VALUE);
RootedId lengthId(cx, NameToId(cx->names().length));
ObjectOpResult lengthResult;
if (!NativeDefineProperty(cx, throwTypeError, lengthId, nonConfigurableDesc,
lengthResult)) {
return nullptr;
}
MOZ_ASSERT(lengthResult);
RootedId nameId(cx, NameToId(cx->names().name));
ObjectOpResult nameResult;
if (!NativeDefineProperty(cx, throwTypeError, nameId, nonConfigurableDesc,
nameResult)) {
return nullptr;
}
MOZ_ASSERT(nameResult);
global->setReservedSlot(THROWTYPEERROR, ObjectValue(*throwTypeError));
return throwTypeError;
}
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<GlobalObject*> global(cx, &obj->as<GlobalObject>());
MOZ_ASSERT(global->isUnqualifiedVarObj());
if (clasp->flags & JSCLASS_HAS_PRIVATE) {
global->setPrivate(nullptr);
}
Rooted<LexicalEnvironmentObject*> lexical(
cx, LexicalEnvironmentObject::createGlobal(cx, global));
if (!lexical) {
return nullptr;
}
global->setReservedSlot(LEXICAL_ENVIRONMENT, ObjectValue(*lexical));
Rooted<GlobalScope*> emptyGlobalScope(
cx, GlobalScope::createEmpty(cx, ScopeKind::Global));
if (!emptyGlobalScope) {
return nullptr;
}
global->setReservedSlot(EMPTY_GLOBAL_SCOPE,
PrivateGCThingValue(emptyGlobalScope));
cx->realm()->initGlobal(*global);
if (!JSObject::setQualifiedVarObj(cx, global)) {
return nullptr;
}
if (!JSObject::setDelegate(cx, global)) {
return nullptr;
}
return global;
}
GlobalObject* GlobalObject::new_(JSContext* cx, const Class* clasp,
JSPrincipals* principals,
JS::OnNewGlobalHookOption hookOption,
const JS::RealmOptions& options) {
MOZ_ASSERT(!cx->isExceptionPending());
MOZ_ASSERT_IF(cx->zone(), !cx->zone()->isAtomsZone());
Rooted<GlobalObject*> existingGlobal(cx);
const JS::RealmCreationOptions& creationOptions = options.creationOptions();
if (creationOptions.compartmentSpecifier() ==
JS::CompartmentSpecifier::ExistingCompartment) {
Compartment* comp = creationOptions.compartment();
existingGlobal = &comp->firstGlobal();
}
Realm* realm = NewRealm(cx, principals, options);
if (!realm) {
return nullptr;
}
Rooted<GlobalObject*> global(cx);
{
AutoRealmUnchecked ar(cx, realm);
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<LexicalEnvironmentObject>();
}
GlobalScope& GlobalObject::emptyGlobalScope() const {
const Value& v = getReservedSlot(EMPTY_GLOBAL_SCOPE);
MOZ_ASSERT(v.isPrivateGCThing() && v.traceKind() == JS::TraceKind::Scope);
return static_cast<Scope*>(v.toGCThing())->as<GlobalScope>();
}
bool GlobalObject::getOrCreateEval(JSContext* cx, Handle<GlobalObject*> 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;
}
bool GlobalObject::initStandardClasses(JSContext* cx,
Handle<GlobalObject*> global) {
if (!DefineDataProperty(
cx, global, cx->names().undefined, UndefinedHandleValue,
JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_RESOLVING)) {
return false;
}
bool resolved;
if (!GlobalObject::maybeResolveGlobalThis(cx, global, &resolved)) {
return false;
}
for (size_t k = 0; k < JSProto_LIMIT; ++k) {
JSProtoKey key = static_cast<JSProtoKey>(k);
if (key != JSProto_Null && !global->isStandardClassResolved(key)) {
if (!resolveConstructor(cx, global, static_cast<JSProtoKey>(k),
IfClassIsDisabled::DoNothing)) {
return false;
}
}
}
return true;
}
static bool InitBareBuiltinCtor(JSContext* cx, Handle<GlobalObject*> 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);
}
bool GlobalObject::initSelfHostingBuiltins(JSContext* cx,
Handle<GlobalObject*> global,
const JSFunctionSpec* builtins) {
if (!DefineDataProperty(cx, global, cx->names().undefined,
UndefinedHandleValue,
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) &&
InitBareSymbolCtor(cx, global) &&
DefineFunctions(cx, global, builtins, AsIntrinsic);
}
bool GlobalObject::isRuntimeCodeGenEnabled(JSContext* cx, HandleValue code,
Handle<GlobalObject*> global) {
HeapSlot& v = global->getSlotRef(RUNTIME_CODEGEN_ENABLED);
if (v.isUndefined()) {
JSCSPEvalChecker allows =
cx->runtime()->securityCallbacks->contentSecurityPolicyAllows;
if (allows) {
return allows(cx, code);
}
v.set(global, HeapSlot::Slot, RUNTIME_CODEGEN_ENABLED, JS::TrueValue());
}
return !v.isFalse();
}
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) {
MOZ_ASSERT(clasp != &JSFunction::class_);
RootedNativeObject blankProto(
cx, NewNativeObjectWithGivenProto(cx, clasp, proto, SingletonObject));
if (!blankProto || !JSObject::setDelegate(cx, blankProto)) {
return nullptr;
}
return blankProto;
}
NativeObject* GlobalObject::createBlankPrototype(JSContext* cx,
Handle<GlobalObject*> global,
const Class* clasp) {
RootedObject objectProto(cx, getOrCreateObjectPrototype(cx, global));
if (!objectProto) {
return nullptr;
}
return CreateBlankProto(cx, clasp, objectProto);
}
NativeObject* GlobalObject::createBlankPrototypeInheriting(JSContext* cx,
const Class* clasp,
HandleObject proto) {
return CreateBlankProto(cx, clasp, proto);
}
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 DefineDataProperty(cx, ctor, cx->names().prototype, protoVal,
prototypeAttrs) &&
DefineDataProperty(cx, proto, cx->names().constructor, ctorVal,
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 DefineDataProperty(cx, obj, toStringTagId, tagString, JSPROP_READONLY);
}
static void GlobalDebuggees_finalize(FreeOp* fop, JSObject* obj) {
MOZ_ASSERT(fop->maybeOnHelperThread());
fop->delete_(
(GlobalObject::DebuggerVector*)obj->as<NativeObject>().getPrivate());
}
static const ClassOps GlobalDebuggees_classOps = {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<NativeObject>().getPrivate();
}
GlobalObject::DebuggerVector* GlobalObject::getOrCreateDebuggers(
JSContext* cx, Handle<GlobalObject*> global) {
cx->check(global);
DebuggerVector* debuggers = global->getDebuggers();
if (debuggers) {
return debuggers;
}
NativeObject* obj =
NewNativeObjectWithGivenProto(cx, &GlobalDebuggees_class, nullptr);
if (!obj) {
return nullptr;
}
debuggers = cx->new_<DebuggerVector>();
if (!debuggers) {
return nullptr;
}
obj->setPrivate(debuggers);
global->setReservedSlot(DEBUGGERS, ObjectValue(*obj));
return debuggers;
}
NativeObject* GlobalObject::getOrCreateForOfPICObject(
JSContext* cx, Handle<GlobalObject*> global) {
cx->check(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();
}
RegExpStatics* GlobalObject::getRegExpStatics(JSContext* cx,
Handle<GlobalObject*> 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);
if (!resObj) {
return nullptr;
}
global->initSlot(REGEXP_STATICS, ObjectValue(*resObj));
} else {
resObj = &val.toObject().as<RegExpStaticsObject>();
}
return static_cast<RegExpStatics*>(resObj->getPrivate( 1));
}
RegExpStatics* GlobalObject::getAlreadyCreatedRegExpStatics() const {
const Value& val = this->getSlot(REGEXP_STATICS);
MOZ_ASSERT(val.isObject());
return static_cast<RegExpStatics*>(
val.toObject().as<RegExpStaticsObject>().getPrivate( 1));
}
NativeObject* GlobalObject::getIntrinsicsHolder(JSContext* cx,
Handle<GlobalObject*> global) {
Value slot = global->getReservedSlot(INTRINSICS);
MOZ_ASSERT(slot.isUndefined() || slot.isObject());
if (slot.isObject()) {
return &slot.toObject().as<NativeObject>();
}
Rooted<NativeObject*> intrinsicsHolder(cx);
bool isSelfHostingGlobal = cx->runtime()->isSelfHostingGlobal(global);
if (isSelfHostingGlobal) {
intrinsicsHolder = global;
} else {
intrinsicsHolder =
NewObjectWithGivenProto<PlainObject>(cx, nullptr, TenuredObject);
if (!intrinsicsHolder) {
return nullptr;
}
}
RootedValue globalValue(cx, ObjectValue(*global));
if (!DefineDataProperty(cx, intrinsicsHolder, cx->names().global, globalValue,
JSPROP_PERMANENT | JSPROP_READONLY)) {
return nullptr;
}
global->setReservedSlot(INTRINSICS, ObjectValue(*intrinsicsHolder));
return intrinsicsHolder;
}
bool GlobalObject::getSelfHostedFunction(JSContext* cx,
Handle<GlobalObject*> 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<JSFunction>());
if (fun->explicitName() == name) {
return true;
}
if (fun->explicitName() == selfHostedName) {
fun->initAtom(name);
return true;
}
cx->runtime()->assertSelfHostedFunctionHasCanonicalName(cx, selfHostedName);
return true;
}
RootedFunction fun(cx);
if (!cx->runtime()->createLazySelfHostedFunctionClone(
cx, selfHostedName, name, nargs,
nullptr, SingletonObject, &fun)) {
return false;
}
funVal.setObject(*fun);
return GlobalObject::addIntrinsicValue(cx, global, selfHostedName, funVal);
}
bool GlobalObject::addIntrinsicValue(JSContext* cx,
Handle<GlobalObject*> 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<UnownedBaseShape*> base(cx, last->base()->unowned());
RootedId id(cx, NameToId(name));
Rooted<StackShape> child(cx, StackShape(base, id, slot, 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;
}
bool GlobalObject::ensureModulePrototypesCreated(JSContext* cx,
Handle<GlobalObject*> global) {
return getOrCreateModulePrototype(cx, global) &&
getOrCreateImportEntryPrototype(cx, global) &&
getOrCreateExportEntryPrototype(cx, global) &&
getOrCreateRequestedModulePrototype(cx, global);
}