michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "vm/GlobalObject.h" michael@0: michael@0: #include "jscntxt.h" michael@0: #include "jsdate.h" michael@0: #include "jsexn.h" michael@0: #include "jsfriendapi.h" michael@0: #include "jsmath.h" michael@0: #include "json.h" michael@0: #include "jsprototypes.h" michael@0: #include "jsweakmap.h" michael@0: #include "jsworkers.h" michael@0: michael@0: #include "builtin/Eval.h" michael@0: #if EXPOSE_INTL_API michael@0: # include "builtin/Intl.h" michael@0: #endif michael@0: #include "builtin/MapObject.h" michael@0: #include "builtin/Object.h" michael@0: #include "builtin/RegExp.h" michael@0: #include "builtin/SIMD.h" michael@0: #include "builtin/TypedObject.h" michael@0: #include "vm/PIC.h" michael@0: #include "vm/RegExpStatics.h" michael@0: #include "vm/StopIterationObject.h" michael@0: #include "vm/WeakMapObject.h" michael@0: michael@0: #include "jscompartmentinlines.h" michael@0: #include "jsobjinlines.h" michael@0: #include "jsscriptinlines.h" michael@0: michael@0: #include "vm/ObjectImpl-inl.h" michael@0: michael@0: using namespace js; michael@0: michael@0: struct ProtoTableEntry { michael@0: const Class *clasp; michael@0: ClassInitializerOp init; michael@0: }; michael@0: michael@0: #define DECLARE_PROTOTYPE_CLASS_INIT(name,code,init,clasp) \ michael@0: extern JSObject *init(JSContext *cx, Handle obj); michael@0: JS_FOR_EACH_PROTOTYPE(DECLARE_PROTOTYPE_CLASS_INIT) michael@0: #undef DECLARE_PROTOTYPE_CLASS_INIT michael@0: michael@0: JSObject * michael@0: js_InitViaClassSpec(JSContext *cx, Handle obj) michael@0: { michael@0: MOZ_ASSUME_UNREACHABLE(); michael@0: } michael@0: michael@0: static const ProtoTableEntry protoTable[JSProto_LIMIT] = { michael@0: #define INIT_FUNC(name,code,init,clasp) { clasp, init }, michael@0: #define INIT_FUNC_DUMMY(name,code,init,clasp) { nullptr, nullptr }, michael@0: JS_FOR_PROTOTYPES(INIT_FUNC, INIT_FUNC_DUMMY) michael@0: #undef INIT_FUNC_DUMMY michael@0: #undef INIT_FUNC michael@0: }; michael@0: michael@0: const js::Class * michael@0: js::ProtoKeyToClass(JSProtoKey key) michael@0: { michael@0: MOZ_ASSERT(key < JSProto_LIMIT); michael@0: return protoTable[key].clasp; michael@0: } michael@0: michael@0: // This method is not in the header file to avoid having to include michael@0: // TypedObject.h from GlobalObject.h. It is not generally perf michael@0: // sensitive. michael@0: TypedObjectModuleObject& michael@0: js::GlobalObject::getTypedObjectModule() const { michael@0: Value v = getConstructor(JSProto_TypedObject); michael@0: // only gets called from contexts where TypedObject must be initialized michael@0: JS_ASSERT(v.isObject()); michael@0: return v.toObject().as(); michael@0: } michael@0: michael@0: JSObject * michael@0: js_InitObjectClass(JSContext *cx, HandleObject obj) michael@0: { michael@0: JS_ASSERT(obj->isNative()); michael@0: michael@0: return obj->as().getOrCreateObjectPrototype(cx); michael@0: } michael@0: michael@0: JSObject * michael@0: js_InitFunctionClass(JSContext *cx, HandleObject obj) michael@0: { michael@0: JS_ASSERT(obj->isNative()); michael@0: michael@0: return obj->as().getOrCreateFunctionPrototype(cx); michael@0: } michael@0: michael@0: static bool michael@0: ThrowTypeError(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, nullptr, michael@0: JSMSG_THROW_TYPE_ERROR); michael@0: return false; michael@0: } michael@0: michael@0: static bool michael@0: TestProtoThis(HandleValue v) michael@0: { michael@0: return !v.isNullOrUndefined(); michael@0: } michael@0: michael@0: static bool michael@0: ProtoGetterImpl(JSContext *cx, CallArgs args) michael@0: { michael@0: JS_ASSERT(TestProtoThis(args.thisv())); michael@0: michael@0: HandleValue thisv = args.thisv(); michael@0: if (thisv.isPrimitive() && !BoxNonStrictThis(cx, args)) michael@0: return false; michael@0: michael@0: RootedObject obj(cx, &args.thisv().toObject()); michael@0: RootedObject proto(cx); michael@0: if (!JSObject::getProto(cx, obj, &proto)) michael@0: return false; michael@0: args.rval().setObjectOrNull(proto); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: ProtoGetter(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: return CallNonGenericMethod(cx, TestProtoThis, ProtoGetterImpl, args); michael@0: } michael@0: michael@0: namespace js { michael@0: size_t sSetProtoCalled = 0; michael@0: } // namespace js michael@0: michael@0: static bool michael@0: ProtoSetterImpl(JSContext *cx, CallArgs args) michael@0: { michael@0: JS_ASSERT(TestProtoThis(args.thisv())); michael@0: michael@0: HandleValue thisv = args.thisv(); michael@0: if (thisv.isPrimitive()) { michael@0: JS_ASSERT(!thisv.isNullOrUndefined()); michael@0: michael@0: // Mutating a boxed primitive's [[Prototype]] has no side effects. michael@0: args.rval().setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: if (!cx->runningWithTrustedPrincipals()) michael@0: ++sSetProtoCalled; michael@0: michael@0: Rooted obj(cx, &args.thisv().toObject()); michael@0: michael@0: /* Do nothing if __proto__ isn't being set to an object or null. */ michael@0: if (args.length() == 0 || !args[0].isObjectOrNull()) { michael@0: args.rval().setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: Rooted newProto(cx, args[0].toObjectOrNull()); michael@0: michael@0: bool success; michael@0: if (!JSObject::setProto(cx, obj, newProto, &success)) michael@0: return false; michael@0: michael@0: if (!success) { michael@0: js_ReportValueError(cx, JSMSG_SETPROTOTYPEOF_FAIL, JSDVG_IGNORE_STACK, thisv, js::NullPtr()); michael@0: return false; michael@0: } michael@0: michael@0: args.rval().setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: ProtoSetter(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: michael@0: // Do this here, rather than in |ProtoSetterImpl|, so even likely-buggy michael@0: // use of the __proto__ setter on unacceptable values, where no subsequent michael@0: // use occurs on an acceptable value, will trigger a warning. michael@0: RootedObject callee(cx, &args.callee()); michael@0: if (!GlobalObject::warnOnceAboutPrototypeMutation(cx, callee)) michael@0: return false; michael@0: michael@0: return CallNonGenericMethod(cx, TestProtoThis, ProtoSetterImpl, args); michael@0: } michael@0: michael@0: JSObject * michael@0: GlobalObject::initFunctionAndObjectClasses(JSContext *cx) michael@0: { michael@0: Rooted self(cx, this); michael@0: michael@0: JS_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment())); michael@0: JS_ASSERT(isNative()); michael@0: michael@0: cx->setDefaultCompartmentObjectIfUnset(self); michael@0: michael@0: RootedObject objectProto(cx); michael@0: michael@0: /* michael@0: * Create |Object.prototype| first, mirroring CreateBlankProto but for the michael@0: * prototype of the created object. michael@0: */ michael@0: objectProto = NewObjectWithGivenProto(cx, &JSObject::class_, nullptr, self, SingletonObject); michael@0: if (!objectProto) michael@0: return nullptr; michael@0: michael@0: /* michael@0: * The default 'new' type of Object.prototype is required by type inference michael@0: * to have unknown properties, to simplify handling of e.g. heterogenous michael@0: * objects in JSON and script literals. michael@0: */ michael@0: if (!setNewTypeUnknown(cx, &JSObject::class_, objectProto)) michael@0: return nullptr; michael@0: michael@0: /* Create |Function.prototype| next so we can create other functions. */ michael@0: RootedFunction functionProto(cx); michael@0: { michael@0: JSObject *functionProto_ = NewObjectWithGivenProto(cx, &JSFunction::class_, michael@0: objectProto, self, SingletonObject); michael@0: if (!functionProto_) michael@0: return nullptr; michael@0: functionProto = &functionProto_->as(); michael@0: michael@0: /* michael@0: * Bizarrely, |Function.prototype| must be an interpreted function, so michael@0: * give it the guts to be one. michael@0: */ michael@0: { michael@0: JSObject *proto = NewFunction(cx, functionProto, nullptr, 0, JSFunction::INTERPRETED, michael@0: self, NullPtr()); michael@0: if (!proto) michael@0: return nullptr; michael@0: JS_ASSERT(proto == functionProto); michael@0: functionProto->setIsFunctionPrototype(); michael@0: } michael@0: michael@0: const char *rawSource = "() {\n}"; michael@0: size_t sourceLen = strlen(rawSource); michael@0: jschar *source = InflateString(cx, rawSource, &sourceLen); michael@0: if (!source) michael@0: return nullptr; michael@0: ScriptSource *ss = michael@0: cx->new_(); michael@0: if (!ss) { michael@0: js_free(source); michael@0: return nullptr; michael@0: } michael@0: ScriptSourceHolder ssHolder(ss); michael@0: ss->setSource(source, sourceLen); michael@0: CompileOptions options(cx); michael@0: options.setNoScriptRval(true) michael@0: .setVersion(JSVERSION_DEFAULT); michael@0: RootedScriptSource sourceObject(cx, ScriptSourceObject::create(cx, ss, options)); michael@0: if (!sourceObject) michael@0: return nullptr; michael@0: michael@0: RootedScript script(cx, JSScript::Create(cx, michael@0: /* enclosingScope = */ NullPtr(), michael@0: /* savedCallerFun = */ false, michael@0: options, michael@0: /* staticLevel = */ 0, michael@0: sourceObject, michael@0: 0, michael@0: ss->length())); michael@0: if (!script || !JSScript::fullyInitTrivial(cx, script)) michael@0: return nullptr; michael@0: michael@0: functionProto->initScript(script); michael@0: types::TypeObject* protoType = functionProto->getType(cx); michael@0: if (!protoType) michael@0: return nullptr; michael@0: protoType->interpretedFunction = functionProto; michael@0: script->setFunction(functionProto); michael@0: michael@0: /* michael@0: * The default 'new' type of Function.prototype is required by type michael@0: * inference to have unknown properties, to simplify handling of e.g. michael@0: * CloneFunctionObject. michael@0: */ michael@0: if (!setNewTypeUnknown(cx, &JSFunction::class_, functionProto)) michael@0: return nullptr; michael@0: } michael@0: michael@0: /* Create the Object function now that we have a [[Prototype]] for it. */ michael@0: RootedFunction objectCtor(cx); michael@0: { michael@0: RootedObject ctor(cx, NewObjectWithGivenProto(cx, &JSFunction::class_, functionProto, michael@0: self, SingletonObject)); michael@0: if (!ctor) michael@0: return nullptr; michael@0: RootedAtom objectAtom(cx, cx->names().Object); michael@0: objectCtor = NewFunction(cx, ctor, obj_construct, 1, JSFunction::NATIVE_CTOR, self, michael@0: objectAtom); michael@0: if (!objectCtor) michael@0: return nullptr; michael@0: } michael@0: michael@0: /* michael@0: * Install |Object| and |Object.prototype| for the benefit of subsequent michael@0: * code that looks for them. michael@0: */ michael@0: self->setObjectClassDetails(objectCtor, objectProto); michael@0: michael@0: /* Create |Function| so it and |Function.prototype| can be installed. */ michael@0: RootedFunction functionCtor(cx); michael@0: { michael@0: // Note that ctor is rooted purely for the JS_ASSERT at the end michael@0: RootedObject ctor(cx, NewObjectWithGivenProto(cx, &JSFunction::class_, functionProto, michael@0: self, SingletonObject)); michael@0: if (!ctor) michael@0: return nullptr; michael@0: RootedAtom functionAtom(cx, cx->names().Function); michael@0: functionCtor = NewFunction(cx, ctor, Function, 1, JSFunction::NATIVE_CTOR, self, michael@0: functionAtom); michael@0: if (!functionCtor) michael@0: return nullptr; michael@0: JS_ASSERT(ctor == functionCtor); michael@0: } michael@0: michael@0: /* michael@0: * Install |Function| and |Function.prototype| so that we can freely create michael@0: * functions and objects without special effort. michael@0: */ michael@0: self->setFunctionClassDetails(functionCtor, functionProto); michael@0: michael@0: /* michael@0: * The hard part's done: now go back and add all the properties these michael@0: * primordial values have. michael@0: */ michael@0: if (!LinkConstructorAndPrototype(cx, objectCtor, objectProto) || michael@0: !DefinePropertiesAndBrand(cx, objectProto, nullptr, object_methods)) michael@0: { michael@0: return nullptr; michael@0: } michael@0: michael@0: /* michael@0: * Add an Object.prototype.__proto__ accessor property to implement that michael@0: * extension (if it's actually enabled). Cache the getter for this michael@0: * function so that cross-compartment [[Prototype]]-getting is implemented michael@0: * in one place. michael@0: */ michael@0: RootedFunction getter(cx, NewFunction(cx, NullPtr(), ProtoGetter, 0, JSFunction::NATIVE_FUN, michael@0: self, NullPtr())); michael@0: if (!getter) michael@0: return nullptr; michael@0: #if JS_HAS_OBJ_PROTO_PROP michael@0: RootedFunction setter(cx, NewFunction(cx, NullPtr(), ProtoSetter, 0, JSFunction::NATIVE_FUN, michael@0: self, NullPtr())); michael@0: if (!setter) michael@0: return nullptr; michael@0: if (!JSObject::defineProperty(cx, objectProto, michael@0: cx->names().proto, UndefinedHandleValue, michael@0: JS_DATA_TO_FUNC_PTR(PropertyOp, getter.get()), michael@0: JS_DATA_TO_FUNC_PTR(StrictPropertyOp, setter.get()), michael@0: JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED)) michael@0: { michael@0: return nullptr; michael@0: } michael@0: #endif /* JS_HAS_OBJ_PROTO_PROP */ michael@0: self->setProtoGetter(getter); michael@0: michael@0: michael@0: if (!DefinePropertiesAndBrand(cx, objectCtor, nullptr, object_static_methods) || michael@0: !LinkConstructorAndPrototype(cx, functionCtor, functionProto) || michael@0: !DefinePropertiesAndBrand(cx, functionProto, nullptr, function_methods) || michael@0: !DefinePropertiesAndBrand(cx, functionCtor, nullptr, nullptr)) michael@0: { michael@0: return nullptr; michael@0: } michael@0: michael@0: /* Add the global Function and Object properties now. */ michael@0: if (!self->addDataProperty(cx, cx->names().Object, constructorPropertySlot(JSProto_Object), 0)) michael@0: return nullptr; michael@0: if (!self->addDataProperty(cx, cx->names().Function, constructorPropertySlot(JSProto_Function), 0)) michael@0: return nullptr; michael@0: michael@0: /* Heavy lifting done, but lingering tasks remain. */ michael@0: michael@0: /* ES5 15.1.2.1. */ michael@0: RootedId evalId(cx, NameToId(cx->names().eval)); michael@0: JSObject *evalobj = DefineFunction(cx, self, evalId, IndirectEval, 1, JSFUN_STUB_GSOPS); michael@0: if (!evalobj) michael@0: return nullptr; michael@0: self->setOriginalEval(evalobj); michael@0: michael@0: /* ES5 13.2.3: Construct the unique [[ThrowTypeError]] function object. */ michael@0: RootedFunction throwTypeError(cx, NewFunction(cx, NullPtr(), ThrowTypeError, 0, michael@0: JSFunction::NATIVE_FUN, self, NullPtr())); michael@0: if (!throwTypeError) michael@0: return nullptr; michael@0: if (!JSObject::preventExtensions(cx, throwTypeError)) michael@0: return nullptr; michael@0: self->setThrowTypeError(throwTypeError); michael@0: michael@0: RootedObject intrinsicsHolder(cx); michael@0: if (cx->runtime()->isSelfHostingGlobal(self)) { michael@0: intrinsicsHolder = self; michael@0: } else { michael@0: RootedObject proto(cx, self->getOrCreateObjectPrototype(cx)); michael@0: if (!proto) michael@0: return nullptr; michael@0: intrinsicsHolder = NewObjectWithGivenProto(cx, &JSObject::class_, proto, self, michael@0: TenuredObject); michael@0: if (!intrinsicsHolder) michael@0: return nullptr; michael@0: } michael@0: self->setIntrinsicsHolder(intrinsicsHolder); michael@0: /* Define a property 'global' with the current global as its value. */ michael@0: RootedValue global(cx, ObjectValue(*self)); michael@0: if (!JSObject::defineProperty(cx, intrinsicsHolder, cx->names().global, michael@0: global, JS_PropertyStub, JS_StrictPropertyStub, michael@0: JSPROP_PERMANENT | JSPROP_READONLY)) michael@0: { michael@0: return nullptr; michael@0: } michael@0: michael@0: /* michael@0: * The global object should have |Object.prototype| as its [[Prototype]]. michael@0: * Eventually we'd like to have standard classes be there from the start, michael@0: * and thus we would know we were always setting what had previously been a michael@0: * null [[Prototype]], but right now some code assumes it can set the michael@0: * [[Prototype]] before standard classes have been initialized. For now, michael@0: * only set the [[Prototype]] if it hasn't already been set. michael@0: */ michael@0: Rooted tagged(cx, TaggedProto(objectProto)); michael@0: if (self->shouldSplicePrototype(cx) && !self->splicePrototype(cx, self->getClass(), tagged)) michael@0: return nullptr; michael@0: michael@0: /* michael@0: * Notify any debuggers about the creation of the script for michael@0: * |Function.prototype| -- after all initialization, for simplicity. michael@0: */ michael@0: RootedScript functionProtoScript(cx, functionProto->nonLazyScript()); michael@0: CallNewScriptHook(cx, functionProtoScript, functionProto); michael@0: return functionProto; michael@0: } michael@0: michael@0: /* static */ bool michael@0: GlobalObject::ensureConstructor(JSContext *cx, Handle global, JSProtoKey key) michael@0: { michael@0: if (global->isStandardClassResolved(key)) michael@0: return true; michael@0: return resolveConstructor(cx, global, key); michael@0: } michael@0: michael@0: /* static*/ bool michael@0: GlobalObject::resolveConstructor(JSContext *cx, Handle global, JSProtoKey key) michael@0: { michael@0: MOZ_ASSERT(!global->isStandardClassResolved(key)); michael@0: michael@0: // There are two different kinds of initialization hooks. One of them is michael@0: // the class js_InitFoo hook, defined in a JSProtoKey-keyed table at the michael@0: // top of this file. The other lives in the ClassSpec for classes that michael@0: // define it. Classes may use one or the other, but not both. michael@0: ClassInitializerOp init = protoTable[key].init; michael@0: if (init == js_InitViaClassSpec) michael@0: init = nullptr; michael@0: michael@0: const Class *clasp = ProtoKeyToClass(key); michael@0: michael@0: // Some classes have no init routine, which means that they're disabled at michael@0: // compile-time. We could try to enforce that callers never pass such keys michael@0: // to resolveConstructor, but that would cramp the style of consumers like michael@0: // GlobalObject::initStandardClasses that want to just carpet-bomb-call michael@0: // ensureConstructor with every JSProtoKey. So it's easier to just handle michael@0: // it here. michael@0: bool haveSpec = clasp && clasp->spec.defined(); michael@0: if (!init && !haveSpec) michael@0: return true; michael@0: michael@0: // See if there's an old-style initialization hook. michael@0: if (init) { michael@0: MOZ_ASSERT(!haveSpec); michael@0: return init(cx, global); michael@0: } michael@0: michael@0: // michael@0: // Ok, we're doing it with a class spec. michael@0: // michael@0: michael@0: // Create the constructor. michael@0: RootedObject ctor(cx, clasp->spec.createConstructor(cx, key)); michael@0: if (!ctor) michael@0: return false; michael@0: michael@0: // Define any specified functions. michael@0: if (const JSFunctionSpec *funs = clasp->spec.constructorFunctions) { michael@0: if (!JS_DefineFunctions(cx, ctor, funs)) michael@0: return false; michael@0: } michael@0: michael@0: // We don't always have a prototype (i.e. Math and JSON). If we don't, michael@0: // |createPrototype| and |prototypeFunctions| should both be null. michael@0: RootedObject proto(cx); michael@0: if (clasp->spec.createPrototype) { michael@0: proto = clasp->spec.createPrototype(cx, key); michael@0: if (!proto) michael@0: return false; michael@0: } michael@0: if (const JSFunctionSpec *funs = clasp->spec.prototypeFunctions) { michael@0: if (!JS_DefineFunctions(cx, proto, funs)) michael@0: return false; michael@0: } michael@0: michael@0: // If the prototype exists, link it with the constructor. michael@0: if (proto && !LinkConstructorAndPrototype(cx, ctor, proto)) michael@0: return false; michael@0: michael@0: // Call the post-initialization hook, if provided. michael@0: if (clasp->spec.finishInit && !clasp->spec.finishInit(cx, ctor, proto)) michael@0: return false; michael@0: michael@0: // Stash things in the right slots and define the constructor on the global. michael@0: return initBuiltinConstructor(cx, global, key, ctor, proto); michael@0: } michael@0: michael@0: /* static */ bool michael@0: GlobalObject::initBuiltinConstructor(JSContext *cx, Handle global, michael@0: JSProtoKey key, HandleObject ctor, HandleObject proto) michael@0: { michael@0: JS_ASSERT(!global->nativeEmpty()); // reserved slots already allocated michael@0: JS_ASSERT(key != JSProto_Null); michael@0: JS_ASSERT(ctor); michael@0: JS_ASSERT(proto); michael@0: michael@0: RootedId id(cx, NameToId(ClassName(key, cx))); michael@0: JS_ASSERT(!global->nativeLookup(cx, id)); michael@0: michael@0: if (!global->addDataProperty(cx, id, constructorPropertySlot(key), 0)) michael@0: return false; michael@0: michael@0: global->setConstructor(key, ObjectValue(*ctor)); michael@0: global->setPrototype(key, ObjectValue(*proto)); michael@0: global->setConstructorPropertySlot(key, ObjectValue(*ctor)); michael@0: michael@0: types::AddTypePropertyId(cx, global, id, ObjectValue(*ctor)); michael@0: return true; michael@0: } michael@0: michael@0: GlobalObject * michael@0: GlobalObject::create(JSContext *cx, const Class *clasp) michael@0: { michael@0: JS_ASSERT(clasp->flags & JSCLASS_IS_GLOBAL); michael@0: JS_ASSERT(clasp->trace == JS_GlobalObjectTraceHook); michael@0: michael@0: JSObject *obj = NewObjectWithGivenProto(cx, clasp, nullptr, nullptr, SingletonObject); michael@0: if (!obj) michael@0: return nullptr; michael@0: michael@0: Rooted global(cx, &obj->as()); michael@0: michael@0: cx->compartment()->initGlobal(*global); michael@0: michael@0: if (!global->setVarObj(cx)) michael@0: return nullptr; michael@0: if (!global->setDelegate(cx)) michael@0: return nullptr; michael@0: michael@0: /* Construct a regexp statics object for this global object. */ michael@0: JSObject *res = RegExpStatics::create(cx, global); michael@0: if (!res) michael@0: return nullptr; michael@0: michael@0: global->initSlot(REGEXP_STATICS, ObjectValue(*res)); michael@0: return global; michael@0: } michael@0: michael@0: /* static */ bool michael@0: GlobalObject::getOrCreateEval(JSContext *cx, Handle global, michael@0: MutableHandleObject eval) michael@0: { michael@0: if (!global->getOrCreateObjectPrototype(cx)) michael@0: return false; michael@0: eval.set(&global->getSlot(EVAL).toObject()); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: GlobalObject::valueIsEval(Value val) michael@0: { michael@0: Value eval = getSlot(EVAL); michael@0: return eval.isObject() && eval == val; michael@0: } michael@0: michael@0: /* static */ bool michael@0: GlobalObject::initStandardClasses(JSContext *cx, Handle global) michael@0: { michael@0: /* Define a top-level property 'undefined' with the undefined value. */ michael@0: if (!JSObject::defineProperty(cx, global, cx->names().undefined, UndefinedHandleValue, michael@0: JS_PropertyStub, JS_StrictPropertyStub, JSPROP_PERMANENT | JSPROP_READONLY)) michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: for (size_t k = 0; k < JSProto_LIMIT; ++k) { michael@0: if (!ensureConstructor(cx, global, static_cast(k))) michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: /* static */ bool michael@0: GlobalObject::isRuntimeCodeGenEnabled(JSContext *cx, Handle global) michael@0: { michael@0: HeapSlot &v = global->getSlotRef(RUNTIME_CODEGEN_ENABLED); michael@0: if (v.isUndefined()) { michael@0: /* michael@0: * If there are callbacks, make sure that the CSP callback is installed michael@0: * and that it permits runtime code generation, then cache the result. michael@0: */ michael@0: JSCSPEvalChecker allows = cx->runtime()->securityCallbacks->contentSecurityPolicyAllows; michael@0: Value boolValue = BooleanValue(!allows || allows(cx)); michael@0: v.set(global, HeapSlot::Slot, RUNTIME_CODEGEN_ENABLED, boolValue); michael@0: } michael@0: return !v.isFalse(); michael@0: } michael@0: michael@0: /* static */ bool michael@0: GlobalObject::warnOnceAbout(JSContext *cx, HandleObject obj, uint32_t slot, unsigned errorNumber) michael@0: { michael@0: Rooted global(cx, &obj->global()); michael@0: HeapSlot &v = global->getSlotRef(slot); michael@0: if (v.isUndefined()) { michael@0: if (!JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING, js_GetErrorMessage, nullptr, michael@0: errorNumber)) michael@0: { michael@0: return false; michael@0: } michael@0: v.init(global, HeapSlot::Slot, slot, BooleanValue(true)); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: JSFunction * michael@0: GlobalObject::createConstructor(JSContext *cx, Native ctor, JSAtom *nameArg, unsigned length, michael@0: gc::AllocKind kind) michael@0: { michael@0: RootedAtom name(cx, nameArg); michael@0: RootedObject self(cx, this); michael@0: return NewFunction(cx, NullPtr(), ctor, length, JSFunction::NATIVE_CTOR, self, name, kind); michael@0: } michael@0: michael@0: static JSObject * michael@0: CreateBlankProto(JSContext *cx, const Class *clasp, JSObject &proto, GlobalObject &global) michael@0: { michael@0: JS_ASSERT(clasp != &JSObject::class_); michael@0: JS_ASSERT(clasp != &JSFunction::class_); michael@0: michael@0: RootedObject blankProto(cx, NewObjectWithGivenProto(cx, clasp, &proto, &global, SingletonObject)); michael@0: if (!blankProto || !blankProto->setDelegate(cx)) michael@0: return nullptr; michael@0: michael@0: return blankProto; michael@0: } michael@0: michael@0: JSObject * michael@0: GlobalObject::createBlankPrototype(JSContext *cx, const Class *clasp) michael@0: { michael@0: Rooted self(cx, this); michael@0: JSObject *objectProto = getOrCreateObjectPrototype(cx); michael@0: if (!objectProto) michael@0: return nullptr; michael@0: michael@0: return CreateBlankProto(cx, clasp, *objectProto, *self.get()); michael@0: } michael@0: michael@0: JSObject * michael@0: GlobalObject::createBlankPrototypeInheriting(JSContext *cx, const Class *clasp, JSObject &proto) michael@0: { michael@0: return CreateBlankProto(cx, clasp, proto, *this); michael@0: } michael@0: michael@0: bool michael@0: js::LinkConstructorAndPrototype(JSContext *cx, JSObject *ctor_, JSObject *proto_) michael@0: { michael@0: RootedObject ctor(cx, ctor_), proto(cx, proto_); michael@0: michael@0: RootedValue protoVal(cx, ObjectValue(*proto)); michael@0: RootedValue ctorVal(cx, ObjectValue(*ctor)); michael@0: michael@0: return JSObject::defineProperty(cx, ctor, cx->names().prototype, michael@0: protoVal, JS_PropertyStub, JS_StrictPropertyStub, michael@0: JSPROP_PERMANENT | JSPROP_READONLY) && michael@0: JSObject::defineProperty(cx, proto, cx->names().constructor, michael@0: ctorVal, JS_PropertyStub, JS_StrictPropertyStub, 0); michael@0: } michael@0: michael@0: bool michael@0: js::DefinePropertiesAndBrand(JSContext *cx, JSObject *obj_, michael@0: const JSPropertySpec *ps, const JSFunctionSpec *fs) michael@0: { michael@0: RootedObject obj(cx, obj_); michael@0: michael@0: if (ps && !JS_DefineProperties(cx, obj, ps)) michael@0: return false; michael@0: if (fs && !JS_DefineFunctions(cx, obj, fs)) michael@0: return false; michael@0: return true; michael@0: } michael@0: michael@0: static void michael@0: GlobalDebuggees_finalize(FreeOp *fop, JSObject *obj) michael@0: { michael@0: fop->delete_((GlobalObject::DebuggerVector *) obj->getPrivate()); michael@0: } michael@0: michael@0: static const Class michael@0: GlobalDebuggees_class = { michael@0: "GlobalDebuggee", JSCLASS_HAS_PRIVATE, michael@0: JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, michael@0: JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, GlobalDebuggees_finalize michael@0: }; michael@0: michael@0: GlobalObject::DebuggerVector * michael@0: GlobalObject::getDebuggers() michael@0: { michael@0: Value debuggers = getReservedSlot(DEBUGGERS); michael@0: if (debuggers.isUndefined()) michael@0: return nullptr; michael@0: JS_ASSERT(debuggers.toObject().getClass() == &GlobalDebuggees_class); michael@0: return (DebuggerVector *) debuggers.toObject().getPrivate(); michael@0: } michael@0: michael@0: /* static */ GlobalObject::DebuggerVector * michael@0: GlobalObject::getOrCreateDebuggers(JSContext *cx, Handle global) michael@0: { michael@0: assertSameCompartment(cx, global); michael@0: DebuggerVector *debuggers = global->getDebuggers(); michael@0: if (debuggers) michael@0: return debuggers; michael@0: michael@0: JSObject *obj = NewObjectWithGivenProto(cx, &GlobalDebuggees_class, nullptr, global); michael@0: if (!obj) michael@0: return nullptr; michael@0: debuggers = cx->new_(); michael@0: if (!debuggers) michael@0: return nullptr; michael@0: obj->setPrivate(debuggers); michael@0: global->setReservedSlot(DEBUGGERS, ObjectValue(*obj)); michael@0: return debuggers; michael@0: } michael@0: michael@0: /* static */ bool michael@0: GlobalObject::addDebugger(JSContext *cx, Handle global, Debugger *dbg) michael@0: { michael@0: DebuggerVector *debuggers = getOrCreateDebuggers(cx, global); michael@0: if (!debuggers) michael@0: return false; michael@0: #ifdef DEBUG michael@0: for (Debugger **p = debuggers->begin(); p != debuggers->end(); p++) michael@0: JS_ASSERT(*p != dbg); michael@0: #endif michael@0: if (debuggers->empty() && !global->compartment()->addDebuggee(cx, global)) michael@0: return false; michael@0: if (!debuggers->append(dbg)) { michael@0: (void) global->compartment()->removeDebuggee(cx, global); michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: /* static */ JSObject * michael@0: GlobalObject::getOrCreateForOfPICObject(JSContext *cx, Handle global) michael@0: { michael@0: assertSameCompartment(cx, global); michael@0: JSObject *forOfPIC = global->getForOfPICObject(); michael@0: if (forOfPIC) michael@0: return forOfPIC; michael@0: michael@0: forOfPIC = ForOfPIC::createForOfPICObject(cx, global); michael@0: if (!forOfPIC) michael@0: return nullptr; michael@0: global->setReservedSlot(FOR_OF_PIC_CHAIN, ObjectValue(*forOfPIC)); michael@0: return forOfPIC; michael@0: } michael@0: michael@0: bool michael@0: GlobalObject::getSelfHostedFunction(JSContext *cx, HandleAtom selfHostedName, HandleAtom name, michael@0: unsigned nargs, MutableHandleValue funVal) michael@0: { michael@0: RootedId shId(cx, AtomToId(selfHostedName)); michael@0: RootedObject holder(cx, cx->global()->intrinsicsHolder()); michael@0: michael@0: if (cx->global()->maybeGetIntrinsicValue(shId, funVal.address())) michael@0: return true; michael@0: michael@0: JSFunction *fun = NewFunction(cx, NullPtr(), nullptr, nargs, JSFunction::INTERPRETED_LAZY, michael@0: holder, name, JSFunction::ExtendedFinalizeKind, SingletonObject); michael@0: if (!fun) michael@0: return false; michael@0: fun->setIsSelfHostedBuiltin(); michael@0: fun->setExtendedSlot(0, StringValue(selfHostedName)); michael@0: funVal.setObject(*fun); michael@0: michael@0: return cx->global()->addIntrinsicValue(cx, shId, funVal); michael@0: } michael@0: michael@0: bool michael@0: GlobalObject::addIntrinsicValue(JSContext *cx, HandleId id, HandleValue value) michael@0: { michael@0: RootedObject holder(cx, intrinsicsHolder()); michael@0: michael@0: uint32_t slot = holder->slotSpan(); michael@0: RootedShape last(cx, holder->lastProperty()); michael@0: Rooted base(cx, last->base()->unowned()); michael@0: michael@0: StackShape child(base, id, slot, 0, 0); michael@0: RootedShape shape(cx, cx->compartment()->propertyTree.getChild(cx, last, child)); michael@0: if (!shape) michael@0: return false; michael@0: michael@0: if (!JSObject::setLastProperty(cx, holder, shape)) michael@0: return false; michael@0: michael@0: holder->setSlot(shape->slot(), value); michael@0: return true; michael@0: }