Bug 1328386 - Part 5: Add Intl.[[FallbackSymbol]] to support ECMA402, 4th edition legacy constructor semantics. r=Waldo
authorAndré Bargull <andre.bargull@gmail.com>
Mon, 23 Jan 2017 08:33:40 -0800
changeset 466648 985a0c1baa892278e078fb4d796fc87a730aaeb4
parent 466647 e1415ba91dd2620247fab41c5472f42b33cbc299
child 466649 91080dad0ff8d9211948e470500a12cbb3bcf515
push id42948
push userbmo:gasolin@mozilla.com
push dateThu, 26 Jan 2017 07:49:21 +0000
reviewersWaldo
bugs1328386
milestone54.0a1
Bug 1328386 - Part 5: Add Intl.[[FallbackSymbol]] to support ECMA402, 4th edition legacy constructor semantics. r=Waldo
js/src/builtin/Intl.js
js/src/builtin/SymbolObject.cpp
js/src/builtin/SymbolObject.h
js/src/builtin/Utilities.js
js/src/vm/GlobalObject.cpp
--- a/js/src/builtin/Intl.js
+++ b/js/src/builtin/Intl.js
@@ -1192,16 +1192,35 @@ function GetNumberOption(options, proper
     // Step 3.
     return fallback;
 }
 
 
 /********** Property access for Intl objects **********/
 
 
+// Symbols in the self-hosting compartment can't be cloned, use a separate
+// object to hold the actual symbol value.
+// TODO: Can we add support to clone symbols?
+var intlFallbackSymbolHolder = { value: undefined };
+
+/**
+ * The [[FallbackSymbol]] symbol of the %Intl% intrinsic object.
+ *
+ * This symbol is used to implement the legacy constructor semantics for
+ * Intl.DateTimeFormat and Intl.NumberFormat.
+ */
+function intlFallbackSymbol() {
+    var fallbackSymbol = intlFallbackSymbolHolder.value;
+    if (!fallbackSymbol)
+        intlFallbackSymbolHolder.value = fallbackSymbol = std_Symbol();
+    return fallbackSymbol;
+}
+
+
 /**
  * Weak map used to track the initialize-as-Intl status (and, if an object has
  * been so initialized, the Intl-specific internal properties) of all objects.
  * Presence of an object as a key within this map indicates that the object has
  * its [[initializedIntlObject]] internal property set to true.  The associated
  * value is an object whose structure is documented in |initializeIntlObject|
  * below.
  *
--- a/js/src/builtin/SymbolObject.cpp
+++ b/js/src/builtin/SymbolObject.cpp
@@ -45,51 +45,59 @@ const JSFunctionSpec SymbolObject::metho
 
 const JSFunctionSpec SymbolObject::staticMethods[] = {
     JS_FN("for", for_, 1, 0),
     JS_FN("keyFor", keyFor, 1, 0),
     JS_FS_END
 };
 
 JSObject*
-SymbolObject::initClass(JSContext* cx, HandleObject obj)
+SymbolObject::initClass(JSContext* cx, HandleObject obj, bool defineMembers)
 {
     Handle<GlobalObject*> global = obj.as<GlobalObject>();
 
     // This uses &JSObject::class_ because: "The Symbol prototype object is an
     // ordinary object. It is not a Symbol instance and does not have a
     // [[SymbolData]] internal slot." (ES6 rev 24, 19.4.3)
     RootedObject proto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
     if (!proto)
         return nullptr;
 
     RootedFunction ctor(cx, GlobalObject::createConstructor(cx, construct,
                                                             ClassName(JSProto_Symbol, cx), 0));
     if (!ctor)
         return nullptr;
 
-    // Define the well-known symbol properties, such as Symbol.iterator.
-    ImmutablePropertyNamePtr* names = cx->names().wellKnownSymbolNames();
-    RootedValue value(cx);
-    unsigned attrs = JSPROP_READONLY | JSPROP_PERMANENT;
-    WellKnownSymbols* wks = cx->runtime()->wellKnownSymbols;
-    for (size_t i = 0; i < JS::WellKnownSymbolLimit; i++) {
-        value.setSymbol(wks->get(i));
-        if (!NativeDefineProperty(cx, ctor, names[i], value, nullptr, nullptr, attrs))
-            return nullptr;
+    if (defineMembers) {
+        // Define the well-known symbol properties, such as Symbol.iterator.
+        ImmutablePropertyNamePtr* names = cx->names().wellKnownSymbolNames();
+        RootedValue value(cx);
+        unsigned attrs = JSPROP_READONLY | JSPROP_PERMANENT;
+        WellKnownSymbols* wks = cx->runtime()->wellKnownSymbols;
+        for (size_t i = 0; i < JS::WellKnownSymbolLimit; i++) {
+            value.setSymbol(wks->get(i));
+            if (!NativeDefineProperty(cx, ctor, names[i], value, nullptr, nullptr, attrs))
+                return nullptr;
+        }
     }
 
-    if (!LinkConstructorAndPrototype(cx, ctor, proto) ||
-        !DefinePropertiesAndFunctions(cx, proto, properties, methods) ||
-        !DefineToStringTag(cx, proto, cx->names().Symbol) ||
-        !DefinePropertiesAndFunctions(cx, ctor, nullptr, staticMethods) ||
-        !GlobalObject::initBuiltinConstructor(cx, global, JSProto_Symbol, ctor, proto))
-    {
+    if (!LinkConstructorAndPrototype(cx, ctor, proto))
         return nullptr;
+
+    if (defineMembers) {
+        if (!DefinePropertiesAndFunctions(cx, proto, properties, methods) ||
+            !DefineToStringTag(cx, proto, cx->names().Symbol) ||
+            !DefinePropertiesAndFunctions(cx, ctor, nullptr, staticMethods))
+        {
+            return nullptr;
+        }
     }
+
+    if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_Symbol, ctor, proto))
+        return nullptr;
     return proto;
 }
 
 // ES6 rev 24 (2014 Apr 27) 19.4.1.1 and 19.4.1.2
 bool
 SymbolObject::construct(JSContext* cx, unsigned argc, Value* vp)
 {
     // According to a note in the draft standard, "Symbol has ordinary
@@ -225,10 +233,16 @@ SymbolObject::toPrimitive(JSContext* cx,
     // The specification gives exactly the same algorithm for @@toPrimitive as
     // for valueOf, so reuse the valueOf implementation.
     return CallNonGenericMethod<IsSymbol, valueOf_impl>(cx, args);
 }
 
 JSObject*
 js::InitSymbolClass(JSContext* cx, HandleObject obj)
 {
-    return SymbolObject::initClass(cx, obj);
+    return SymbolObject::initClass(cx, obj, true);
 }
+
+JSObject*
+js::InitBareSymbolCtor(JSContext* cx, HandleObject obj)
+{
+    return SymbolObject::initClass(cx, obj, false);
+}
--- a/js/src/builtin/SymbolObject.h
+++ b/js/src/builtin/SymbolObject.h
@@ -17,17 +17,17 @@ class SymbolObject : public NativeObject
     /* Stores this Symbol object's [[PrimitiveValue]]. */
     static const unsigned PRIMITIVE_VALUE_SLOT = 0;
 
   public:
     static const unsigned RESERVED_SLOTS = 1;
 
     static const Class class_;
 
-    static JSObject* initClass(JSContext* cx, js::HandleObject obj);
+    static JSObject* initClass(JSContext* cx, js::HandleObject obj, bool defineMembers);
 
     /*
      * Creates a new Symbol object boxing the given primitive Symbol.  The
      * object's [[Prototype]] is determined from context.
      */
     static SymbolObject* create(JSContext* cx, JS::HandleSymbol symbol);
 
     JS::Symbol* unbox() const {
@@ -55,11 +55,14 @@ class SymbolObject : public NativeObject
     static const JSPropertySpec properties[];
     static const JSFunctionSpec methods[];
     static const JSFunctionSpec staticMethods[];
 };
 
 extern JSObject*
 InitSymbolClass(JSContext* cx, HandleObject obj);
 
+extern JSObject*
+InitBareSymbolCtor(JSContext* cx, HandleObject obj);
+
 } /* namespace js */
 
 #endif /* builtin_SymbolObject_h */
--- a/js/src/builtin/Utilities.js
+++ b/js/src/builtin/Utilities.js
@@ -38,16 +38,18 @@
 
 // All C++-implemented standard builtins library functions used in self-hosted
 // code are installed via the std_functions JSFunctionSpec[] in
 // SelfHosting.cpp.
 //
 // Do not create an alias to a self-hosted builtin, otherwise it will be cloned
 // twice.
 //
+// Symbol is a bare constructor without properties or methods.
+var std_Symbol = Symbol;
 // WeakMap is a bare constructor without properties or methods.
 var std_WeakMap = WeakMap;
 // StopIteration is a bare constructor without properties or methods.
 var std_StopIteration = StopIteration;
 
 
 /********** List specification type **********/
 
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -525,16 +525,17 @@ GlobalObject::initSelfHostingBuiltins(JS
     {
         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) &&
            InitBareWeakMapCtor(cx, global) &&
            InitStopIterationClass(cx, global) &&
            DefineFunctions(cx, global, builtins, AsIntrinsic);
 }
 
 /* static */ bool
 GlobalObject::isRuntimeCodeGenEnabled(JSContext* cx, Handle<GlobalObject*> global)
 {