Bug 1317422 - Implement JavaScript globalThis proposal. r=jandem
authorTom Schuster <evilpies@gmail.com>
Wed, 14 Nov 2018 15:57:03 +0000
changeset 446827 1e6a2dbfa104081bed1a4273aff4cbd2c29d3335
parent 446826 b6010c470851c2a92036ae40c76665048575a20c
child 446828 e368528459474eedea833311aea43cacc85c36ee
push id35052
push userapavel@mozilla.com
push dateSat, 17 Nov 2018 11:25:40 +0000
treeherdermozilla-central@efc1da42132b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1317422
milestone65.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1317422 - Implement JavaScript globalThis proposal. r=jandem Differential Revision: https://phabricator.services.mozilla.com/D11322
js/public/Class.h
js/src/jsapi.cpp
js/src/tests/jstests.list
js/src/vm/CommonPropertyNames.h
js/src/vm/GlobalObject.cpp
js/src/vm/GlobalObject.h
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -863,17 +863,17 @@ static const uint32_t JSCLASS_FOREGROUND
 // with the following flags. Failure to use JSCLASS_GLOBAL_FLAGS was
 // previously allowed, but is now an ES5 violation and thus unsupported.
 //
 // JSCLASS_GLOBAL_APPLICATION_SLOTS is the number of slots reserved at
 // the beginning of every global object's slots for use by the
 // application.
 static const uint32_t JSCLASS_GLOBAL_APPLICATION_SLOTS = 5;
 static const uint32_t JSCLASS_GLOBAL_SLOT_COUNT =
-    JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 36;
+    JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 37;
 
 #define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n)                              \
     (JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n)))
 #define JSCLASS_GLOBAL_FLAGS                                                  \
     JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(0)
 #define JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(clasp)                              \
   (((clasp)->flags & JSCLASS_IS_GLOBAL)                                       \
    && JSCLASS_RESERVED_SLOTS(clasp) >= JSCLASS_GLOBAL_SLOT_COUNT)
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1003,23 +1003,27 @@ JS_ResolveStandardClass(JSContext* cx, H
     *resolved = false;
 
     if (!JSID_IS_ATOM(id)) {
         return true;
     }
 
     /* 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 DefineDataProperty(cx, global, id, UndefinedHandleValue,
                                   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);
 
     /* Try less frequently used top-level functions and constants. */
     if (!stdnm) {
         stdnm = LookupStdName(cx->names(), idAtom, builtin_property_names);
     }
 
@@ -1067,16 +1071,17 @@ JS_MayResolveStandardClass(const JSAtomS
     }
 
     JSAtom* atom = JSID_TO_ATOM(id);
 
     // This will return true even for deselected constructors.  (To do
     // 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);
 }
 
 JS_PUBLIC_API(bool)
 JS_EnumerateStandardClasses(JSContext* cx, HandleObject obj)
 {
     AssertHeapIsIdle();
--- a/js/src/tests/jstests.list
+++ b/js/src/tests/jstests.list
@@ -98,20 +98,16 @@ skip-if(!xulRuntime.shell) script test26
 skip script test262/built-ins/ThrowTypeError/name.js
 
 # Anonymous functions have own name property in SpiderMonkey
 skip script test262/language/expressions/assignment/fn-name-lhs-cover.js
 skip script test262/language/expressions/assignment/fn-name-lhs-member.js
 skip script test262/language/expressions/function/name.js
 skip script test262/language/expressions/generators/name.js
 
-# https://bugzilla.mozilla.org/show_bug.cgi?id=1317422
-skip script test262/built-ins/global/property-descriptor.js
-skip script test262/built-ins/global/global-object.js
-
 # https://bugzilla.mozilla.org/show_bug.cgi?id=1079853
 skip script test262/built-ins/TypedArray/prototype/every/BigInt/callbackfn-detachbuffer.js
 skip script test262/built-ins/TypedArray/prototype/every/callbackfn-detachbuffer.js
 skip script test262/built-ins/TypedArray/prototype/filter/BigInt/callbackfn-detachbuffer.js
 skip script test262/built-ins/TypedArray/prototype/filter/callbackfn-detachbuffer.js
 skip script test262/built-ins/TypedArray/prototype/find/BigInt/predicate-may-detach-buffer.js
 skip script test262/built-ins/TypedArray/prototype/find/predicate-may-detach-buffer.js
 skip script test262/built-ins/TypedArray/prototype/findIndex/BigInt/predicate-may-detach-buffer.js
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -162,16 +162,17 @@
     macro(getOwnPropertyDescriptor, getOwnPropertyDescriptor, "getOwnPropertyDescriptor") \
     macro(getOwnPropertyNames, getOwnPropertyNames, "getOwnPropertyNames") \
     macro(getPrefix, getPrefix, "get ") \
     macro(getPropertyDescriptor, getPropertyDescriptor, "getPropertyDescriptor") \
     macro(getPropertySuper, getPropertySuper, "getPropertySuper") \
     macro(getPrototypeOf, getPrototypeOf, "getPrototypeOf") \
     macro(GetTypeError, GetTypeError, "GetTypeError") \
     macro(global, global, "global") \
+    macro(globalThis, globalThis, "globalThis") \
     macro(group, group, "group") \
     macro(Handle, Handle, "Handle") \
     macro(has, has, "has") \
     macro(hasOwn, hasOwn, "hasOwn") \
     macro(hasOwnProperty, hasOwnProperty, "hasOwnProperty") \
     macro(highWaterMark, highWaterMark, "highWaterMark") \
     macro(hour, hour, "hour") \
     macro(if, if_, "if") \
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -304,16 +304,43 @@ GlobalObject::resolveConstructor(JSConte
         if (proto) {
             global->setPrototype(key, ObjectValue(*proto));
         }
     }
 
     return true;
 }
 
+
+// Resolve a "globalThis" self-referential property if necessary,
+// per a stage-3 proposal. https://github.com/tc39/ecma262/pull/702
+//
+// We could also do this in |FinishObjectClassInit| to trim the global
+// resolve hook.  Unfortunately, |ToWindowProxyIfWindow| doesn't work then:
+// the browser's |nsGlobalWindow::SetNewDocument| invokes Object init
+// *before* it sets the global's WindowProxy using |js::SetWindowProxy|.
+//
+// Refactoring global object creation code to support this approach is a
+// challenge for another day.
+/* static */ bool
+GlobalObject::maybeResolveGlobalThis(JSContext* cx, Handle<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;
+}
+
 /* static */ 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());
@@ -608,16 +635,22 @@ GlobalObject::initStandardClasses(JSCont
 {
     /* Define a top-level property 'undefined' with the undefined value. */
     if (!DefineDataProperty(cx, global, cx->names().undefined, UndefinedHandleValue,
                             JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_RESOLVING))
     {
         return false;
     }
 
+    // Resolve a "globalThis" self-referential property if necessary.
+    bool resolved;
+    if (!GlobalObject::maybeResolveGlobalThis(cx, global, &resolved)) {
+        return false;
+    }
+
     for (size_t k = 0; k < JSProto_LIMIT; ++k) {
         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;
             }
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -101,16 +101,17 @@ class GlobalObject : public NativeObject
         EXPORT_ENTRY_PROTO,
         REQUESTED_MODULE_PROTO,
         REGEXP_STATICS,
         RUNTIME_CODEGEN_ENABLED,
         DEBUGGERS,
         INTRINSICS,
         FOR_OF_PIC_CHAIN,
         WINDOW_PROXY,
+        GLOBAL_THIS_RESOLVED,
 
         /* 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
@@ -184,16 +185,18 @@ class GlobalObject : public NativeObject
 
     JSObject* maybeGetPrototype(JSProtoKey protoKey) const {
         MOZ_ASSERT(JSProto_Null < protoKey);
         MOZ_ASSERT(protoKey < JSProto_LIMIT);
         const Value& v = getPrototype(protoKey);
         return v.isObject() ? &v.toObject() : nullptr;
     }
 
+    static bool maybeResolveGlobalThis(JSContext* cx, Handle<GlobalObject*> global, bool* resolved);
+
     void setConstructor(JSProtoKey key, const Value& v) {
         setSlot(constructorSlot(key), v);
     }
 
     Value getPrototype(JSProtoKey key) const {
         return getSlot(prototypeSlot(key));
     }