diff --git a/js/public/Class.h b/js/public/Class.h index f4fa9ccaf..3d73bce44 100644 --- a/js/public/Class.h +++ b/js/public/Class.h @@ -779,7 +779,7 @@ struct JSClass { // application. #define JSCLASS_GLOBAL_APPLICATION_SLOTS 5 #define JSCLASS_GLOBAL_SLOT_COUNT \ - (JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 45) + (JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 46) #define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n) \ (JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n))) #define JSCLASS_GLOBAL_FLAGS \ diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index cb3945152..d75a3c33a 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -1029,13 +1029,17 @@ JS_ResolveStandardClass(JSContext* cx, HandleObject obj, HandleId id, bool* reso /* Check whether we're resolving 'undefined', and define it if so. */ JSAtom* idAtom = JSID_TO_ATOM(id); - JSAtom* undefinedAtom = cx->names().undefined; - if (idAtom == undefinedAtom) { + if (idAtom == cx->names().undefined) { *resolved = true; return DefineProperty(cx, global, id, UndefinedHandleValue, nullptr, nullptr, JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_RESOLVING); } + // Resolve a "globalThis" self-referential property if necessary. + if (idAtom == cx->names().globalThis) { + return GlobalObject::maybeResolveGlobalThis(cx, global, resolved); + } + /* Try for class constructors/prototypes named by well-known atoms. */ stdnm = LookupStdName(cx->names(), idAtom, standard_class_names); @@ -1088,6 +1092,7 @@ JS_MayResolveStandardClass(const JSAtomState& names, jsid id, JSObject* maybeObj // better, we need a JSContext here; it's fine as it is.) return atom == names.undefined || + atom == names.globalThis || LookupStdName(names, atom, standard_class_names) || LookupStdName(names, atom, builtin_property_names); } diff --git a/js/src/vm/CommonPropertyNames.h b/js/src/vm/CommonPropertyNames.h index 420ee7535..b4a2de6f3 100644 --- a/js/src/vm/CommonPropertyNames.h +++ b/js/src/vm/CommonPropertyNames.h @@ -160,6 +160,7 @@ macro(getPropertyDescriptor, getPropertyDescriptor, "getPropertyDescriptor") \ macro(getPrototypeOf, getPrototypeOf, "getPrototypeOf") \ macro(global, global, "global") \ + macro(globalThis, globalThis, "globalThis") \ macro(Handle, Handle, "Handle") \ macro(has, has, "has") \ macro(hasOwn, hasOwn, "hasOwn") \ diff --git a/js/src/vm/GlobalObject.cpp b/js/src/vm/GlobalObject.cpp index a8d401af4..f6a53eef4 100644 --- a/js/src/vm/GlobalObject.cpp +++ b/js/src/vm/GlobalObject.cpp @@ -296,6 +296,32 @@ GlobalObject::initBuiltinConstructor(JSContext* cx, Handle global return true; } +// Resolve a "globalThis" self-referential property if necessary, +// per a stage-3 proposal. https://github.com/tc39/ecma262/pull/702 +// +// We could also do this in |FinishObjectClassInit| to trim the global +// resolve hook. Unfortunately, |ToWindowProxyIfWindow| doesn't work then: +// the browser's |nsGlobalWindow::SetNewDocument| invokes Object init +// *before* it sets the global's WindowProxy using |js::SetWindowProxy|. +// +// Refactoring global object creation code to support this approach is a +// challenge for another day. +/* static */ bool +GlobalObject::maybeResolveGlobalThis(JSContext* cx, Handle global, bool* resolved) +{ + if (global->getSlot(GLOBAL_THIS_RESOLVED).isUndefined()) { + RootedValue v(cx, ObjectValue(*ToWindowProxyIfWindow(global))); + if (!DefineProperty(cx, global, cx->names().globalThis, v, nullptr, nullptr, JSPROP_RESOLVING)) { + return false; + } + + *resolved = true; + global->setSlot(GLOBAL_THIS_RESOLVED, BooleanValue(true)); + } + + return true; +} + GlobalObject* GlobalObject::createInternal(JSContext* cx, const Class* clasp) { @@ -419,6 +445,12 @@ GlobalObject::initStandardClasses(JSContext* cx, Handle global) return false; } + // Resolve a "globalThis" self-referential property if necessary. + bool resolved; + if (!GlobalObject::maybeResolveGlobalThis(cx, global, &resolved)) { + return false; + } + for (size_t k = 0; k < JSProto_LIMIT; ++k) { if (!ensureConstructor(cx, global, static_cast(k))) return false; diff --git a/js/src/vm/GlobalObject.h b/js/src/vm/GlobalObject.h index 3fd2762f8..720f63e69 100644 --- a/js/src/vm/GlobalObject.h +++ b/js/src/vm/GlobalObject.h @@ -121,6 +121,7 @@ class GlobalObject : public NativeObject FOR_OF_PIC_CHAIN, MODULE_RESOLVE_HOOK, WINDOW_PROXY, + GLOBAL_THIS_RESOLVED, /* Total reserved-slot count for global objects. */ RESERVED_SLOTS @@ -171,6 +172,8 @@ class GlobalObject : public NativeObject static bool initBuiltinConstructor(JSContext* cx, Handle global, JSProtoKey key, HandleObject ctor, HandleObject proto); + static bool maybeResolveGlobalThis(JSContext* cx, Handle global, bool* resolved); + void setConstructor(JSProtoKey key, const Value& v) { MOZ_ASSERT(key <= JSProto_LIMIT); setSlot(APPLICATION_SLOTS + key, v);