Source code
Revision control
Copy as Markdown
Other Tools
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* 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
#ifndef vm_GlobalObject_h
#define vm_GlobalObject_h
#include "js/GlobalObject.h"
#include "mozilla/Assertions.h"
#include "mozilla/EnumeratedArray.h"
#include <stdint.h>
#include <type_traits>
#include "jsexn.h"
#include "jsfriendapi.h"
#include "jspubtd.h"
#include "jstypes.h"
#include "NamespaceImports.h"
#include "gc/AllocKind.h"
#include "js/CallArgs.h"
#include "js/Class.h"
#include "js/ErrorReport.h"
#include "js/PropertyDescriptor.h"
#include "js/RootingAPI.h"
#include "js/TypeDecls.h"
#include "js/Value.h"
#include "vm/ArrayObject.h"
#include "vm/JSAtomState.h"
#include "vm/JSContext.h"
#include "vm/JSFunction.h"
#include "vm/JSObject.h"
#include "vm/NativeObject.h"
#include "vm/Realm.h"
#include "vm/RegExpShared.h"
#include "vm/Shape.h"
#include "vm/StringType.h"
struct JSFunctionSpec;
class JSJitInfo;
struct JSPrincipals;
struct JSPropertySpec;
namespace JS {
class JS_PUBLIC_API RealmOptions;
};
namespace js {
class ArgumentsObject;
class GlobalScope;
class GlobalLexicalEnvironmentObject;
class MapObject;
class PlainObject;
class PropertyIteratorObject;
class RegExpStatics;
class SetObject;
namespace gc {
class FinalizationRegistryGlobalData;
} // namespace gc
// Fixed slot capacities for PlainObjects. The global has a cached Shape for
// PlainObject with default prototype for each of these values.
enum class PlainObjectSlotsKind {
Slots0,
Slots2,
Slots4,
Slots8,
Slots12,
Slots16,
Limit
};
static PlainObjectSlotsKind PlainObjectSlotsKindFromAllocKind(
gc::AllocKind kind) {
switch (kind) {
case gc::AllocKind::OBJECT0:
return PlainObjectSlotsKind::Slots0;
case gc::AllocKind::OBJECT2:
return PlainObjectSlotsKind::Slots2;
case gc::AllocKind::OBJECT4:
return PlainObjectSlotsKind::Slots4;
case gc::AllocKind::OBJECT8:
return PlainObjectSlotsKind::Slots8;
case gc::AllocKind::OBJECT12:
return PlainObjectSlotsKind::Slots12;
case gc::AllocKind::OBJECT16:
return PlainObjectSlotsKind::Slots16;
default:
break;
}
MOZ_CRASH("Invalid kind");
}
// Data attached to a GlobalObject. This is freed when clearing the Realm's
// global_ only because this way we don't need to add a finalizer to all
// GlobalObject JSClasses.
class GlobalObjectData {
friend class js::GlobalObject;
GlobalObjectData(const GlobalObjectData&) = delete;
void operator=(const GlobalObjectData&) = delete;
public:
explicit GlobalObjectData(Zone* zone);
~GlobalObjectData();
// The original values for built-in constructors (with their prototype
// objects) based on JSProtoKey.
//
// This is necessary to implement spec language speaking in terms of "the
// original Array prototype object", or "as if by the expression new Array()"
// referring to the original Array constructor. The actual (writable and even
// deletable) Object, Array, &c. properties are not stored here.
struct ConstructorWithProto {
GCPtr<JSObject*> constructor;
GCPtr<JSObject*> prototype;
};
using CtorArray = mozilla::EnumeratedArray<JSProtoKey, ConstructorWithProto,
size_t(JSProto_LIMIT)>;
CtorArray builtinConstructors;
// Built-in prototypes for this global. Note that this is different from the
// set of built-in constructors/prototypes based on JSProtoKey.
enum class ProtoKind {
ArrayIteratorProto,
StringIteratorProto,
RegExpStringIteratorProto,
GeneratorObjectProto,
AsyncIteratorProto,
AsyncFromSyncIteratorProto,
AsyncGeneratorProto,
MapIteratorProto,
SetIteratorProto,
WrapForValidIteratorProto,
IteratorHelperProto,
AsyncIteratorHelperProto,
SegmentsProto,
SegmentIteratorProto,
Limit
};
using ProtoArray = mozilla::EnumeratedArray<ProtoKind, GCPtr<JSObject*>,
size_t(ProtoKind::Limit)>;
ProtoArray builtinProtos;
GCPtr<GlobalScope*> emptyGlobalScope;
// The lexical environment for global let/const/class bindings.
GCPtr<GlobalLexicalEnvironmentObject*> lexicalEnvironment;
// The WindowProxy associated with this global.
GCPtr<JSObject*> windowProxy;
// Functions and other top-level values for self-hosted code. The "computed"
// holder is used as the target of `SetIntrinsic` calls, but the same property
// may also be cached on the normal intrinsics holder for `GetIntrinsic`.
GCPtr<NativeObject*> intrinsicsHolder;
GCPtr<NativeObject*> computedIntrinsicsHolder;
// Cache used to optimize certain for-of operations.
GCPtr<NativeObject*> forOfPICChain;
// List of source URLs for this realm. This is used by the debugger.
GCPtr<ArrayObject*> sourceURLsHolder;
// Realm-specific object that can be used as key in WeakMaps.
GCPtr<PlainObject*> realmKeyObject;
// The unique %ThrowTypeError% function for this global.
GCPtr<JSFunction*> throwTypeError;
// The unique %eval% function (for indirect eval) for this global.
GCPtr<JSFunction*> eval;
// Empty iterator object used for for-in with null/undefined.
GCPtr<PropertyIteratorObject*> emptyIterator;
// Cached shape for new arrays with Array.prototype as prototype.
GCPtr<SharedShape*> arrayShapeWithDefaultProto;
// Shape for PlainObject with %Object.prototype% as proto, for each object
// AllocKind.
using PlainObjectShapeArray =
mozilla::EnumeratedArray<PlainObjectSlotsKind, GCPtr<SharedShape*>,
size_t(PlainObjectSlotsKind::Limit)>;
PlainObjectShapeArray plainObjectShapesWithDefaultProto;
// Shape for JSFunction with %Function.prototype% as proto, for both
// non-extended and extended functions.
GCPtr<SharedShape*> functionShapeWithDefaultProto;
GCPtr<SharedShape*> extendedFunctionShapeWithDefaultProto;
// Shape for BoundFunctionObject with %Function.prototype% as proto.
GCPtr<SharedShape*> boundFunctionShapeWithDefaultProto;
// Global state for regular expressions.
RegExpRealm regExpRealm;
GCPtr<ArgumentsObject*> mappedArgumentsTemplate;
GCPtr<ArgumentsObject*> unmappedArgumentsTemplate;
// Template objects to speed up allocation of Map/Set objects.
GCPtr<MapObject*> mapObjectTemplate;
GCPtr<SetObject*> setObjectTemplate;
GCPtr<PlainObject*> iterResultTemplate;
GCPtr<PlainObject*> iterResultWithoutPrototypeTemplate;
// Lazily initialized script source object to use for scripts cloned from the
// self-hosting stencil.
GCPtr<ScriptSourceObject*> selfHostingScriptSource;
UniquePtr<gc::FinalizationRegistryGlobalData> finalizationRegistryData;
// The number of times that one of the following has occurred:
// 1. A property of this GlobalObject is deleted.
// 2. A data property of this GlobalObject is converted to an accessor,
// or vice versa.
// 3. A property is defined on the global lexical that shadows a property on
// this GlobalObject.
uint32_t generationCount = 0;
// Whether the |globalThis| property has been resolved on the global object.
bool globalThisResolved = false;
void trace(JSTracer* trc, GlobalObject* global);
void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
JS::ClassInfo* info) const;
static constexpr size_t offsetOfLexicalEnvironment() {
static_assert(sizeof(lexicalEnvironment) == sizeof(uintptr_t),
"JIT code assumes field is pointer-sized");
return offsetof(GlobalObjectData, lexicalEnvironment);
}
static constexpr size_t offsetOfRegExpRealm() {
return offsetof(GlobalObjectData, regExpRealm);
}
};
class GlobalObject : public NativeObject {
enum : unsigned {
GLOBAL_DATA_SLOT = JSCLASS_GLOBAL_APPLICATION_SLOTS,
// Total reserved-slot count for global objects.
RESERVED_SLOTS
};
// The slot count must be in the public API for JSCLASS_GLOBAL_FLAGS, and
// we won't expose GlobalObject, so just assert that the two values are
// synchronized.
static_assert(JSCLASS_GLOBAL_SLOT_COUNT == RESERVED_SLOTS,
"global object slot counts are inconsistent");
// Ensure GlobalObjectData is only one dereference away.
static_assert(GLOBAL_DATA_SLOT < MAX_FIXED_SLOTS,
"GlobalObjectData should be stored in a fixed slot for "
"performance reasons");
using ProtoKind = GlobalObjectData::ProtoKind;
GlobalObjectData* maybeData() {
Value v = getReservedSlot(GLOBAL_DATA_SLOT);
return static_cast<GlobalObjectData*>(v.toPrivate());
}
const GlobalObjectData* maybeData() const {
Value v = getReservedSlot(GLOBAL_DATA_SLOT);
return static_cast<const GlobalObjectData*>(v.toPrivate());
}
GlobalObjectData& data() { return *maybeData(); }
const GlobalObjectData& data() const { return *maybeData(); }
void initBuiltinProto(ProtoKind kind, JSObject* proto) {
MOZ_ASSERT(proto);
// Catch double-initialization; however if this is too much of a burden due
// to OOM handling it could be removed.
MOZ_ASSERT(!hasBuiltinProto(kind));
data().builtinProtos[kind].init(proto);
}
void setBuiltinProto(ProtoKind kind, JSObject* proto) {
MOZ_ASSERT(proto);
data().builtinProtos[kind].set(proto);
}
bool hasBuiltinProto(ProtoKind kind) const {
return bool(data().builtinProtos[kind]);
}
JSObject* maybeBuiltinProto(ProtoKind kind) const {
return data().builtinProtos[kind];
}
JSObject& getBuiltinProto(ProtoKind kind) const {
MOZ_ASSERT(hasBuiltinProto(kind));
return *data().builtinProtos[kind];
}
public:
GlobalLexicalEnvironmentObject& lexicalEnvironment() {
return *data().lexicalEnvironment;
}
GlobalScope& emptyGlobalScope() const;
void traceData(JSTracer* trc, GlobalObject* global) {
data().trace(trc, global);
}
void releaseData(JS::GCContext* gcx);
void addSizeOfData(mozilla::MallocSizeOf mallocSizeOf,
JS::ClassInfo* info) const {
if (maybeData()) {
data().addSizeOfIncludingThis(mallocSizeOf, info);
}
}
void setOriginalEval(JSFunction* evalFun) {
MOZ_ASSERT(!data().eval);
data().eval.init(evalFun);
}
bool hasConstructor(JSProtoKey key) const {
return bool(data().builtinConstructors[key].constructor);
}
JSObject& getConstructor(JSProtoKey key) const {
MOZ_ASSERT(hasConstructor(key));
return *maybeGetConstructor(key);
}
static bool skipDeselectedConstructor(JSContext* cx, JSProtoKey key);
private:
enum class IfClassIsDisabled { DoNothing, Throw };
static bool resolveConstructor(JSContext* cx, Handle<GlobalObject*> global,
JSProtoKey key, IfClassIsDisabled mode);
public:
static bool ensureConstructor(JSContext* cx, Handle<GlobalObject*> global,
JSProtoKey key) {
if (global->isStandardClassResolved(key)) {
return true;
}
return resolveConstructor(cx, global, key, IfClassIsDisabled::Throw);
}
static JSObject* getOrCreateConstructor(JSContext* cx, JSProtoKey key) {
MOZ_ASSERT(key != JSProto_Null);
Handle<GlobalObject*> global = cx->global();
if (!GlobalObject::ensureConstructor(cx, global, key)) {
return nullptr;
}
return &global->getConstructor(key);
}
static JSObject* getOrCreatePrototype(JSContext* cx, JSProtoKey key) {
MOZ_ASSERT(key != JSProto_Null);
Handle<GlobalObject*> global = cx->global();
if (!GlobalObject::ensureConstructor(cx, global, key)) {
return nullptr;
}
return &global->getPrototype(key);
}
static JS::Handle<JSObject*> getOrCreatePrototypeHandle(JSContext* cx,
JSProtoKey key) {
MOZ_ASSERT(key != JSProto_Null);
Handle<GlobalObject*> global = cx->global();
if (!GlobalObject::ensureConstructor(cx, global, key)) {
return nullptr;
}
return global->getPrototypeHandle(key);
}
JSObject* maybeGetConstructor(JSProtoKey protoKey) const {
MOZ_ASSERT(JSProto_Null < protoKey);
MOZ_ASSERT(protoKey < JSProto_LIMIT);
return data().builtinConstructors[protoKey].constructor;
}
JSObject* maybeGetPrototype(JSProtoKey protoKey) const {
MOZ_ASSERT(JSProto_Null < protoKey);
MOZ_ASSERT(protoKey < JSProto_LIMIT);
return data().builtinConstructors[protoKey].prototype;
}
static bool maybeResolveGlobalThis(JSContext* cx,
Handle<GlobalObject*> global,
bool* resolved);
void setConstructor(JSProtoKey key, JSObject* obj) {
MOZ_ASSERT(obj);
data().builtinConstructors[key].constructor = obj;
}
bool hasPrototype(JSProtoKey key) const {
return bool(data().builtinConstructors[key].prototype);
}
JSObject& getPrototype(JSProtoKey key) const {
MOZ_ASSERT(hasPrototype(key));
return *maybeGetPrototype(key);
}
JS::Handle<JSObject*> getPrototypeHandle(JSProtoKey protoKey) const {
MOZ_ASSERT(hasPrototype(protoKey));
MOZ_ASSERT(JSProto_Null < protoKey);
MOZ_ASSERT(protoKey < JSProto_LIMIT);
return Handle<JSObject*>::fromMarkedLocation(
&data().builtinConstructors[protoKey].prototype.get());
}
void setPrototype(JSProtoKey key, JSObject* obj) {
MOZ_ASSERT(obj);
data().builtinConstructors[key].prototype = obj;
}
/*
* Lazy standard classes need a way to indicate they have been initialized.
* Otherwise, when we delete them, we might accidentally recreate them via
* a lazy initialization. We use the presence of an object in the constructor
* array to indicate that they've been initialized.
*
* Note: A few builtin objects, like JSON and Math, are not constructors,
* so getConstructor is a bit of a misnomer.
*/
bool isStandardClassResolved(JSProtoKey key) const {
return hasConstructor(key);
}
private:
bool classIsInitialized(JSProtoKey key) const {
bool inited = hasConstructor(key);
MOZ_ASSERT(inited == hasPrototype(key));
return inited;
}
bool functionObjectClassesInitialized() const {
bool inited = classIsInitialized(JSProto_Function);
MOZ_ASSERT(inited == classIsInitialized(JSProto_Object));
return inited;
}
// Disallow use of unqualified JSObject::create in GlobalObject.
static GlobalObject* create(...) = delete;
friend struct ::JSRuntime;
static GlobalObject* createInternal(JSContext* cx, const JSClass* clasp);
public:
static GlobalObject* new_(JSContext* cx, const JSClass* clasp,
JSPrincipals* principals,
JS::OnNewGlobalHookOption hookOption,
const JS::RealmOptions& options);
/*
* Create a constructor function with the specified name and length using
* ctor, a method which creates objects with the given class.
*/
static JSFunction* createConstructor(
JSContext* cx, JSNative ctor, JSAtom* name, unsigned length,
gc::AllocKind kind = gc::AllocKind::FUNCTION,
const JSJitInfo* jitInfo = nullptr);
/*
* Create an object to serve as [[Prototype]] for instances of the given
* class, using |Object.prototype| as its [[Prototype]]. Users creating
* prototype objects with particular internal structure (e.g. reserved
* slots guaranteed to contain values of particular types) must immediately
* complete the minimal initialization to make the returned object safe to
* touch.
*/
static NativeObject* createBlankPrototype(
JSContext* cx, Handle<GlobalObject*> global, const JSClass* clasp,
ObjectFlags objFlags = ObjectFlags());
/*
* Identical to createBlankPrototype, but uses proto as the [[Prototype]]
* of the returned blank prototype.
*/
static NativeObject* createBlankPrototypeInheriting(JSContext* cx,
const JSClass* clasp,
HandleObject proto);
template <typename T>
static T* createBlankPrototypeInheriting(JSContext* cx, HandleObject proto) {
NativeObject* res = createBlankPrototypeInheriting(cx, &T::class_, proto);
return res ? &res->template as<T>() : nullptr;
}
template <typename T>
static T* createBlankPrototype(JSContext* cx, Handle<GlobalObject*> global,
ObjectFlags objFlags = ObjectFlags()) {
NativeObject* res = createBlankPrototype(cx, global, &T::class_, objFlags);
return res ? &res->template as<T>() : nullptr;
}
// Object, Function, and eval are eagerly resolved when creating the global.
JSObject& getObjectPrototype() {
MOZ_ASSERT(functionObjectClassesInitialized());
return getPrototype(JSProto_Object);
}
Handle<JSObject*> getObjectPrototypeHandle() {
MOZ_ASSERT(functionObjectClassesInitialized());
return getPrototypeHandle(JSProto_Object);
}
JSObject& getFunctionConstructor() {
MOZ_ASSERT(functionObjectClassesInitialized());
return getConstructor(JSProto_Function);
}
JSObject& getFunctionPrototype() {
MOZ_ASSERT(functionObjectClassesInitialized());
return getPrototype(JSProto_Function);
}
Handle<JSObject*> getFunctionPrototypeHandle() {
MOZ_ASSERT(functionObjectClassesInitialized());
return getPrototypeHandle(JSProto_Function);
}
JSFunction& getEvalFunction() {
MOZ_ASSERT(data().eval);
return *data().eval;
}
static NativeObject* getOrCreateArrayPrototype(JSContext* cx,
Handle<GlobalObject*> global) {
if (!ensureConstructor(cx, global, JSProto_Array)) {
return nullptr;
}
return &global->getPrototype(JSProto_Array).as<NativeObject>();
}
NativeObject* maybeGetArrayPrototype() {
if (classIsInitialized(JSProto_Array)) {
return &getPrototype(JSProto_Array).as<NativeObject>();
}
return nullptr;
}
static JSObject* getOrCreateBooleanPrototype(JSContext* cx,
Handle<GlobalObject*> global) {
if (!ensureConstructor(cx, global, JSProto_Boolean)) {
return nullptr;
}
return &global->getPrototype(JSProto_Boolean);
}
static JSObject* getOrCreateNumberPrototype(JSContext* cx,
Handle<GlobalObject*> global) {
if (!ensureConstructor(cx, global, JSProto_Number)) {
return nullptr;
}
return &global->getPrototype(JSProto_Number);
}
static JSObject* getOrCreateStringPrototype(JSContext* cx,
Handle<GlobalObject*> global) {
if (!ensureConstructor(cx, global, JSProto_String)) {
return nullptr;
}
return &global->getPrototype(JSProto_String);
}
static JSObject* getOrCreateSymbolPrototype(JSContext* cx,
Handle<GlobalObject*> global) {
if (!ensureConstructor(cx, global, JSProto_Symbol)) {
return nullptr;
}
return &global->getPrototype(JSProto_Symbol);
}
static JSObject* getOrCreateBigIntPrototype(JSContext* cx,
Handle<GlobalObject*> global) {
if (!ensureConstructor(cx, global, JSProto_BigInt)) {
return nullptr;
}
return &global->getPrototype(JSProto_BigInt);
}
#ifdef ENABLE_RECORD_TUPLE
static JSObject* getOrCreateRecordPrototype(JSContext* cx,
Handle<GlobalObject*> global) {
if (!ensureConstructor(cx, global, JSProto_Record)) {
return nullptr;
}
return &global->getPrototype(JSProto_Record);
}
static JSObject* getOrCreateTuplePrototype(JSContext* cx,
Handle<GlobalObject*> global) {
if (!ensureConstructor(cx, global, JSProto_Tuple)) {
return nullptr;
}
return &global->getPrototype(JSProto_Tuple);
}
#endif
static JSObject* getOrCreatePromisePrototype(JSContext* cx,
Handle<GlobalObject*> global) {
if (!ensureConstructor(cx, global, JSProto_Promise)) {
return nullptr;
}
return &global->getPrototype(JSProto_Promise);
}
static JSObject* getOrCreateRegExpPrototype(JSContext* cx,
Handle<GlobalObject*> global) {
if (!ensureConstructor(cx, global, JSProto_RegExp)) {
return nullptr;
}
return &global->getPrototype(JSProto_RegExp);
}
static JSObject* getOrCreateRegExpConstructor(JSContext* cx,
Handle<GlobalObject*> global) {
if (!ensureConstructor(cx, global, JSProto_RegExp)) {
return nullptr;
}
return &global->getConstructor(JSProto_RegExp);
}
JSObject* maybeGetRegExpPrototype() {
if (classIsInitialized(JSProto_RegExp)) {
return &getPrototype(JSProto_RegExp);
}
return nullptr;
}
static JSObject* getOrCreateSavedFramePrototype(
JSContext* cx, Handle<GlobalObject*> global) {
if (!ensureConstructor(cx, global, JSProto_SavedFrame)) {
return nullptr;
}
return &global->getPrototype(JSProto_SavedFrame);
}
static JSObject* getOrCreateArrayBufferConstructor(
JSContext* cx, Handle<GlobalObject*> global) {
if (!ensureConstructor(cx, global, JSProto_ArrayBuffer)) {
return nullptr;
}
return &global->getConstructor(JSProto_ArrayBuffer);
}
static JSObject* getOrCreateArrayBufferPrototype(
JSContext* cx, Handle<GlobalObject*> global) {
if (!ensureConstructor(cx, global, JSProto_ArrayBuffer)) {
return nullptr;
}
return &global->getPrototype(JSProto_ArrayBuffer);
}
static JSObject* getOrCreateSharedArrayBufferPrototype(
JSContext* cx, Handle<GlobalObject*> global) {
if (!ensureConstructor(cx, global, JSProto_SharedArrayBuffer)) {
return nullptr;
}
return &global->getPrototype(JSProto_SharedArrayBuffer);
}
static JSObject* getOrCreateSharedArrayBufferConstructor(
JSContext* cx, Handle<GlobalObject*> global) {
if (!ensureConstructor(cx, global, JSProto_SharedArrayBuffer)) {
return nullptr;
}
return &global->getConstructor(JSProto_SharedArrayBuffer);
}
static JSObject* getOrCreateCustomErrorPrototype(JSContext* cx,
Handle<GlobalObject*> global,
JSExnType exnType) {
JSProtoKey key = GetExceptionProtoKey(exnType);
if (!ensureConstructor(cx, global, key)) {
return nullptr;
}
return &global->getPrototype(key);
}
static JSFunction* getOrCreateErrorConstructor(JSContext* cx,
Handle<GlobalObject*> global) {
if (!ensureConstructor(cx, global, JSProto_Error)) {
return nullptr;
}
return &global->getConstructor(JSProto_Error).as<JSFunction>();
}
static JSObject* getOrCreateErrorPrototype(JSContext* cx,
Handle<GlobalObject*> global) {
return getOrCreateCustomErrorPrototype(cx, global, JSEXN_ERR);
}
static JSFunction* getOrCreateTypedArrayConstructor(
JSContext* cx, Handle<GlobalObject*> global) {
if (!ensureConstructor(cx, global, JSProto_TypedArray)) {
return nullptr;
}
return &global->getConstructor(JSProto_TypedArray).as<JSFunction>();
}
static JSObject* getOrCreateTypedArrayPrototype(
JSContext* cx, Handle<GlobalObject*> global) {
if (!ensureConstructor(cx, global, JSProto_TypedArray)) {
return nullptr;
}
return &global->getPrototype(JSProto_TypedArray);
}
private:
using ObjectInitOp = bool (*)(JSContext*, Handle<GlobalObject*>);
using ObjectInitWithTagOp = bool (*)(JSContext*, Handle<GlobalObject*>,
Handle<JSAtom*>);
static JSObject* getOrCreateBuiltinProto(JSContext* cx,
Handle<GlobalObject*> global,
ProtoKind kind, ObjectInitOp init) {
if (JSObject* proto = global->maybeBuiltinProto(kind)) {
return proto;
}
return createBuiltinProto(cx, global, kind, init);
}
static JSObject* getOrCreateBuiltinProto(JSContext* cx,
Handle<GlobalObject*> global,
ProtoKind kind, Handle<JSAtom*> tag,
ObjectInitWithTagOp init) {
if (JSObject* proto = global->maybeBuiltinProto(kind)) {
return proto;
}
return createBuiltinProto(cx, global, kind, tag, init);
}
static JSObject* createBuiltinProto(JSContext* cx,
Handle<GlobalObject*> global,
ProtoKind kind, ObjectInitOp init);
static JSObject* createBuiltinProto(JSContext* cx,
Handle<GlobalObject*> global,
ProtoKind kind, Handle<JSAtom*> tag,
ObjectInitWithTagOp init);
static JSObject* createIteratorPrototype(JSContext* cx,
Handle<GlobalObject*> global);
public:
NativeObject* maybeGetIteratorPrototype() {
if (!hasPrototype(JSProto_Iterator)) {
return nullptr;
}
return &(getPrototype(JSProto_Iterator).as<NativeObject>());
}
static JSObject* getOrCreateIteratorPrototype(JSContext* cx,
Handle<GlobalObject*> global) {
if (!ensureConstructor(cx, global, JSProto_Iterator)) {
return nullptr;
}
return &global->getPrototype(JSProto_Iterator);
}
static NativeObject* getOrCreateArrayIteratorPrototype(
JSContext* cx, Handle<GlobalObject*> global);
NativeObject* maybeGetArrayIteratorPrototype() {
if (JSObject* obj = maybeBuiltinProto(ProtoKind::ArrayIteratorProto)) {
return &obj->as<NativeObject>();
}
return nullptr;
}
static JSObject* getOrCreateStringIteratorPrototype(
JSContext* cx, Handle<GlobalObject*> global);
static JSObject* getOrCreateRegExpStringIteratorPrototype(
JSContext* cx, Handle<GlobalObject*> global);
void setGeneratorObjectPrototype(JSObject* obj) {
setBuiltinProto(ProtoKind::GeneratorObjectProto, obj);
}
static JSObject* getOrCreateGeneratorObjectPrototype(
JSContext* cx, Handle<GlobalObject*> global) {
if (!ensureConstructor(cx, global, JSProto_GeneratorFunction)) {
return nullptr;
}
return &global->getBuiltinProto(ProtoKind::GeneratorObjectProto);
}
static JSObject* getOrCreateGeneratorFunctionPrototype(
JSContext* cx, Handle<GlobalObject*> global) {
if (!ensureConstructor(cx, global, JSProto_GeneratorFunction)) {
return nullptr;
}
return &global->getPrototype(JSProto_GeneratorFunction);
}
static JSObject* getOrCreateGeneratorFunction(JSContext* cx,
Handle<GlobalObject*> global) {
if (!ensureConstructor(cx, global, JSProto_GeneratorFunction)) {
return nullptr;
}
return &global->getConstructor(JSProto_GeneratorFunction);
}
static JSObject* getOrCreateAsyncFunctionPrototype(
JSContext* cx, Handle<GlobalObject*> global) {
if (!ensureConstructor(cx, global, JSProto_AsyncFunction)) {
return nullptr;
}
return &global->getPrototype(JSProto_AsyncFunction);
}
static JSObject* getOrCreateAsyncFunction(JSContext* cx,
Handle<GlobalObject*> global) {
if (!ensureConstructor(cx, global, JSProto_AsyncFunction)) {
return nullptr;
}
return &global->getConstructor(JSProto_AsyncFunction);
}
static JSObject* createAsyncIteratorPrototype(JSContext* cx,
Handle<GlobalObject*> global);
static JSObject* getOrCreateAsyncIteratorPrototype(
JSContext* cx, Handle<GlobalObject*> global) {
if (JSObject* proto =
global->maybeBuiltinProto(ProtoKind::AsyncIteratorProto)) {
return proto;
}
return createAsyncIteratorPrototype(cx, global);
}
static JSObject* getOrCreateAsyncFromSyncIteratorPrototype(
JSContext* cx, Handle<GlobalObject*> global) {
return getOrCreateBuiltinProto(cx, global,
ProtoKind::AsyncFromSyncIteratorProto,
initAsyncFromSyncIteratorProto);
}
static JSObject* getOrCreateAsyncGenerator(JSContext* cx,
Handle<GlobalObject*> global) {
if (!ensureConstructor(cx, global, JSProto_AsyncGeneratorFunction)) {
return nullptr;
}
return &global->getPrototype(JSProto_AsyncGeneratorFunction);
}
static JSObject* getOrCreateAsyncGeneratorFunction(
JSContext* cx, Handle<GlobalObject*> global) {
if (!ensureConstructor(cx, global, JSProto_AsyncGeneratorFunction)) {
return nullptr;
}
return &global->getConstructor(JSProto_AsyncGeneratorFunction);
}
void setAsyncGeneratorPrototype(JSObject* obj) {
initBuiltinProto(ProtoKind::AsyncGeneratorProto, obj);
}
static JSObject* getOrCreateAsyncGeneratorPrototype(
JSContext* cx, Handle<GlobalObject*> global) {
if (!ensureConstructor(cx, global, JSProto_AsyncGeneratorFunction)) {
return nullptr;
}
return &global->getBuiltinProto(ProtoKind::AsyncGeneratorProto);
}
static JSObject* getOrCreateMapIteratorPrototype(
JSContext* cx, Handle<GlobalObject*> global) {
return getOrCreateBuiltinProto(cx, global, ProtoKind::MapIteratorProto,
initMapIteratorProto);
}
static JSObject* getOrCreateSetIteratorPrototype(
JSContext* cx, Handle<GlobalObject*> global) {
return getOrCreateBuiltinProto(cx, global, ProtoKind::SetIteratorProto,
initSetIteratorProto);
}
static JSObject* getOrCreateSegmentsPrototype(JSContext* cx,
Handle<GlobalObject*> global) {
return getOrCreateBuiltinProto(cx, global, ProtoKind::SegmentsProto,
initSegmentsProto);
}
static JSObject* getOrCreateSegmentIteratorPrototype(
JSContext* cx, Handle<GlobalObject*> global) {
return getOrCreateBuiltinProto(cx, global, ProtoKind::SegmentIteratorProto,
initSegmentIteratorProto);
}
static JSObject* getOrCreateDataViewPrototype(JSContext* cx,
Handle<GlobalObject*> global) {
if (!ensureConstructor(cx, global, JSProto_DataView)) {
return nullptr;
}
return &global->getPrototype(JSProto_DataView);
}
static JSObject* getOrCreatePromiseConstructor(JSContext* cx,
Handle<GlobalObject*> global) {
if (!ensureConstructor(cx, global, JSProto_Promise)) {
return nullptr;
}
return &global->getConstructor(JSProto_Promise);
}
static NativeObject* getOrCreateWrapForValidIteratorPrototype(
JSContext* cx, Handle<GlobalObject*> global);
static NativeObject* getOrCreateIteratorHelperPrototype(
JSContext* cx, Handle<GlobalObject*> global);
static NativeObject* getOrCreateAsyncIteratorHelperPrototype(
JSContext* cx, Handle<GlobalObject*> global);
static bool initAsyncIteratorHelperProto(JSContext* cx,
Handle<GlobalObject*> global);
NativeObject& getIntrinsicsHolder() const {
MOZ_ASSERT(data().intrinsicsHolder);
return *data().intrinsicsHolder;
}
static bool createIntrinsicsHolder(JSContext* cx,
Handle<GlobalObject*> global);
NativeObject* getComputedIntrinsicsHolder() {
return data().computedIntrinsicsHolder;
}
void setComputedIntrinsicsHolder(NativeObject* holder) {
data().computedIntrinsicsHolder = holder;
}
// If a self-hosting intrinsic with the given |name| exists, it's stored in
// |*vp| and this function returns true. Else it returns false.
bool maybeGetIntrinsicValue(PropertyName* name, Value* vp, JSContext* cx) {
NativeObject& holder = getIntrinsicsHolder();
if (mozilla::Maybe<PropertyInfo> prop = holder.lookup(cx, name)) {
*vp = holder.getSlot(prop->slot());
return true;
}
return false;
}
static bool getIntrinsicValue(JSContext* cx, Handle<GlobalObject*> global,
Handle<PropertyName*> name,
MutableHandleValue value) {
// `undefined` in self-hosted JS code should be emitted as JSOp::Undefined.
MOZ_ASSERT(name != cx->names().undefined);
if (global->maybeGetIntrinsicValue(name, value.address(), cx)) {
return true;
}
return getIntrinsicValueSlow(cx, global, name, value);
}
static bool getIntrinsicValueSlow(JSContext* cx, Handle<GlobalObject*> global,
Handle<PropertyName*> name,
MutableHandleValue value);
static bool addIntrinsicValue(JSContext* cx, Handle<GlobalObject*> global,
Handle<PropertyName*> name, HandleValue value);
static inline bool setIntrinsicValue(JSContext* cx,
Handle<GlobalObject*> global,
Handle<PropertyName*> name,
HandleValue value);
static bool getSelfHostedFunction(JSContext* cx, Handle<GlobalObject*> global,
Handle<PropertyName*> selfHostedName,
Handle<JSAtom*> name, unsigned nargs,
MutableHandleValue funVal);
static RegExpStatics* getRegExpStatics(JSContext* cx,
Handle<GlobalObject*> global);
static JSObject* getOrCreateThrowTypeError(JSContext* cx,
Handle<GlobalObject*> global);
RegExpRealm& regExpRealm() { return data().regExpRealm; }
// Infallibly test whether the given value is the eval function for this
// global.
bool valueIsEval(const Value& val);
static ArgumentsObject* getOrCreateArgumentsTemplateObject(JSContext* cx,
bool mapped);
ArgumentsObject* maybeArgumentsTemplateObject(bool mapped) const;
static MapObject* getOrCreateMapTemplateObject(JSContext* cx);
static SetObject* getOrCreateSetTemplateObject(JSContext* cx);
static const size_t IterResultObjectValueSlot = 0;
static const size_t IterResultObjectDoneSlot = 1;
static js::PlainObject* getOrCreateIterResultTemplateObject(JSContext* cx);
static js::PlainObject* getOrCreateIterResultWithoutPrototypeTemplateObject(
JSContext* cx);
private:
enum class WithObjectPrototype { No, Yes };
static js::PlainObject* createIterResultTemplateObject(
JSContext* cx, WithObjectPrototype withProto);
public:
static ScriptSourceObject* getOrCreateSelfHostingScriptSourceObject(
JSContext* cx, Handle<GlobalObject*> global);
// Implemented in vm/Iteration.cpp.
template <ProtoKind Kind, const JSClass* ProtoClass,
const JSFunctionSpec* Methods, const bool needsFuseProperty = false>
static bool initObjectIteratorProto(JSContext* cx,
Handle<GlobalObject*> global,
Handle<JSAtom*> tag);
// Implemented in vm/AsyncIteration.cpp.
static bool initAsyncIteratorProto(JSContext* cx,
Handle<GlobalObject*> global);
static bool initAsyncFromSyncIteratorProto(JSContext* cx,
Handle<GlobalObject*> global);
// Implemented in builtin/MapObject.cpp.
static bool initMapIteratorProto(JSContext* cx, Handle<GlobalObject*> global);
static bool initSetIteratorProto(JSContext* cx, Handle<GlobalObject*> global);
// Implemented in builtin/intl/Segmenter.cpp.
static bool initSegmentsProto(JSContext* cx, Handle<GlobalObject*> global);
static bool initSegmentIteratorProto(JSContext* cx,
Handle<GlobalObject*> global);
static bool initStandardClasses(JSContext* cx, Handle<GlobalObject*> global);
// Disallow GC as it may mutate the vector.
Realm::DebuggerVector& getDebuggers(const JS::AutoRequireNoGC& nogc) const {
return realm()->getDebuggers(nogc);
}
bool hasDebuggers() const { return realm()->hasDebuggers(); }
inline NativeObject* getForOfPICObject() { return data().forOfPICChain; }
static NativeObject* getOrCreateForOfPICObject(JSContext* cx,
Handle<GlobalObject*> global);
JSObject* maybeWindowProxy() const { return data().windowProxy; }
void setWindowProxy(JSObject* windowProxy) {
// Note: the global must always be associated with the same WindowProxy.
// CacheIR optimizations rely on this by baking in the WindowProxy for the
// global.
MOZ_ASSERT(!data().windowProxy);
data().windowProxy.init(windowProxy);
}
ArrayObject* getSourceURLsHolder() const { return data().sourceURLsHolder; }
void setSourceURLsHolder(ArrayObject* holder) {
data().sourceURLsHolder = holder;
}
void clearSourceURLSHolder() { setSourceURLsHolder(nullptr); }
SharedShape* maybeArrayShapeWithDefaultProto() const {
return data().arrayShapeWithDefaultProto;
}
static SharedShape* getArrayShapeWithDefaultProto(JSContext* cx) {
if (SharedShape* shape = cx->global()->data().arrayShapeWithDefaultProto;
MOZ_LIKELY(shape)) {
return shape;
}
return createArrayShapeWithDefaultProto(cx);
}
static SharedShape* createArrayShapeWithDefaultProto(JSContext* cx);
static SharedShape* getPlainObjectShapeWithDefaultProto(JSContext* cx,
gc::AllocKind kind) {
PlainObjectSlotsKind slotsKind = PlainObjectSlotsKindFromAllocKind(kind);
SharedShape* shape =
cx->global()->data().plainObjectShapesWithDefaultProto[slotsKind];
if (MOZ_LIKELY(shape)) {
return shape;
}
return createPlainObjectShapeWithDefaultProto(cx, kind);
}
static SharedShape* createPlainObjectShapeWithDefaultProto(
JSContext* cx, gc::AllocKind kind);
static SharedShape* getEmptyPlainObjectShape(JSContext* cx) {
const PlainObjectSlotsKind kind = PlainObjectSlotsKind::Slots0;
SharedShape* shape =
cx->global()->data().plainObjectShapesWithDefaultProto[kind];
MOZ_ASSERT(shape); // This is created on initialization.
return shape;
}
static SharedShape* getFunctionShapeWithDefaultProto(JSContext* cx,
bool extended) {
GlobalObjectData& data = cx->global()->data();
SharedShape* shape = extended ? data.extendedFunctionShapeWithDefaultProto
: data.functionShapeWithDefaultProto;
if (MOZ_LIKELY(shape)) {
return shape;
}
return createFunctionShapeWithDefaultProto(cx, extended);
}
static SharedShape* createFunctionShapeWithDefaultProto(JSContext* cx,
bool extended);
SharedShape* maybeBoundFunctionShapeWithDefaultProto() const {
return data().boundFunctionShapeWithDefaultProto;
}
void setBoundFunctionShapeWithDefaultProto(SharedShape* shape) {
data().boundFunctionShapeWithDefaultProto = shape;
}
static PropertyIteratorObject* getOrCreateEmptyIterator(JSContext* cx);
// Returns an object that represents the realm, used by embedder.
static JSObject* getOrCreateRealmKeyObject(JSContext* cx,
Handle<GlobalObject*> global);
gc::FinalizationRegistryGlobalData* getOrCreateFinalizationRegistryData();
gc::FinalizationRegistryGlobalData* maybeFinalizationRegistryData() const {
return data().finalizationRegistryData.get();
}
static size_t offsetOfGlobalDataSlot() {
return getFixedSlotOffset(GLOBAL_DATA_SLOT);
}
uint32_t generationCount() const { return data().generationCount; }
const void* addressOfGenerationCount() const {
return &data().generationCount;
}
void bumpGenerationCount() {
MOZ_RELEASE_ASSERT(data().generationCount < UINT32_MAX);
data().generationCount++;
}
};
/*
* Unless otherwise specified, define ctor.prototype = proto as non-enumerable,
* non-configurable, and non-writable; and define proto.constructor = ctor as
* non-enumerable but configurable and writable.
*/
extern bool LinkConstructorAndPrototype(
JSContext* cx, JSObject* ctor, JSObject* proto,
unsigned prototypeAttrs = JSPROP_PERMANENT | JSPROP_READONLY,
unsigned constructorAttrs = 0);
/*
* Define properties and/or functions on any object. Either ps or fs, or both,
* may be null.
*/
extern bool DefinePropertiesAndFunctions(JSContext* cx, HandleObject obj,
const JSPropertySpec* ps,
const JSFunctionSpec* fs);
extern bool DefineToStringTag(JSContext* cx, HandleObject obj, JSAtom* tag);
/*
* Convenience templates to generic constructor and prototype creation functions
* for ClassSpecs.
*/
template <JSNative ctor, unsigned length, gc::AllocKind kind,
const JSJitInfo* jitInfo = nullptr>
JSObject* GenericCreateConstructor(JSContext* cx, JSProtoKey key) {
// Note - We duplicate the trick from ClassName() so that we don't need to
// include vm/JSAtomUtils-inl.h here.
PropertyName* name = (&cx->names().Null)[key];
return GlobalObject::createConstructor(cx, ctor, name, length, kind, jitInfo);
}
template <typename T>
JSObject* GenericCreatePrototype(JSContext* cx, JSProtoKey key) {
static_assert(
!std::is_same_v<T, PlainObject>,
"creating Object.prototype is very special and isn't handled here");
MOZ_ASSERT(&T::class_ == ProtoKeyToClass(key),
"type mismatch--probably too much copy/paste in your ClassSpec");
MOZ_ASSERT(
InheritanceProtoKeyForStandardClass(key) == JSProto_Object,
"subclasses (of anything but Object) can't use GenericCreatePrototype");
return GlobalObject::createBlankPrototype(cx, cx->global(), &T::protoClass_);
}
inline JSProtoKey StandardProtoKeyOrNull(const JSObject* obj) {
return JSCLASS_CACHED_PROTO_KEY(obj->getClass());
}
JSObject* NewTenuredObjectWithFunctionPrototype(JSContext* cx,
Handle<GlobalObject*> global);
} // namespace js
template <>
inline bool JSObject::is<js::GlobalObject>() const {
return !!(getClass()->flags & JSCLASS_IS_GLOBAL);
}
#endif /* vm_GlobalObject_h */