Title: [197531] trunk
Revision
197531
Author
[email protected]
Date
2016-03-03 16:47:55 -0800 (Thu, 03 Mar 2016)

Log Message

[ES6] Add support for Symbol.toPrimitive
https://bugs.webkit.org/show_bug.cgi?id=154877

Reviewed by Saam Barati.

Source/_javascript_Core:

This patch adds suport for Symbol.toPrimitive. Since we don't currently
generate snippits for one side of a binary operation we only need to change
the JSObject::ToPrimitive function and update some optimizations in the DFG
that need to know how conversions to primitive values should work. As of
ES6, the date prototype is also no longer special cased in the ToPrimitive
operation. Instead, Date.prototype has a Symbol.species function that
replicates the old behavior.

* bytecode/ObjectPropertyConditionSet.cpp:
(JSC::generateConditionsForPropertyMissConcurrently):
* bytecode/ObjectPropertyConditionSet.h:
* dfg/DFGGraph.cpp:
(JSC::DFG::Graph::watchConditions):
(JSC::DFG::Graph::canOptimizeStringObjectAccess):
* dfg/DFGGraph.h:
* runtime/CommonIdentifiers.h:
* runtime/DatePrototype.cpp:
(JSC::DatePrototype::finishCreation):
(JSC::dateProtoFuncToPrimitiveSymbol):
* runtime/Error.cpp:
(JSC::throwTypeError):
* runtime/Error.h:
* runtime/JSCJSValueInlines.h:
(JSC::toPreferredPrimitiveType):
* runtime/JSObject.cpp:
(JSC::callToPrimitiveFunction):
(JSC::JSObject::ordinaryToPrimitive):
(JSC::JSObject::defaultValue):
(JSC::JSObject::toPrimitive):
(JSC::JSObject::getPrimitiveNumber):
(JSC::callDefaultValueFunction): Deleted.
(JSC::throwTypeError): Deleted.
* runtime/JSObject.h:
(JSC::JSObject::toPrimitive): Deleted.
* runtime/SmallStrings.h:
* runtime/SymbolPrototype.cpp:
(JSC::SymbolPrototype::finishCreation):
* runtime/SymbolPrototype.h:
(JSC::SymbolPrototype::create):
* tests/es6.yaml:
* tests/stress/date-symbol-toprimitive.js: Added.
* tests/stress/ropes-symbol-toprimitive.js: Added.
(ropify):
(String.prototype.Symbol.toPrimitive):
* tests/stress/symbol-toprimitive.js: Added.
(foo.Symbol.toPrimitive):
(catch):

LayoutTests:

Update test for Symbol.toPrimitive.

* js/Object-getOwnPropertyNames-expected.txt:
* js/script-tests/Object-getOwnPropertyNames.js:

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (197530 => 197531)


--- trunk/LayoutTests/ChangeLog	2016-03-04 00:39:41 UTC (rev 197530)
+++ trunk/LayoutTests/ChangeLog	2016-03-04 00:47:55 UTC (rev 197531)
@@ -1,3 +1,15 @@
+2016-03-03  Keith Miller  <[email protected]>
+
+        [ES6] Add support for Symbol.toPrimitive
+        https://bugs.webkit.org/show_bug.cgi?id=154877
+
+        Reviewed by Saam Barati.
+
+        Update test for Symbol.toPrimitive.
+
+        * js/Object-getOwnPropertyNames-expected.txt:
+        * js/script-tests/Object-getOwnPropertyNames.js:
+
 2016-03-02  Ryosuke Niwa  <[email protected]>
 
         Disallow custom elements inside template elements and share the registry for windowless documents

Modified: trunk/LayoutTests/http/tests/security/cross-frame-access-custom-expected.txt (197530 => 197531)


--- trunk/LayoutTests/http/tests/security/cross-frame-access-custom-expected.txt	2016-03-04 00:39:41 UTC (rev 197530)
+++ trunk/LayoutTests/http/tests/security/cross-frame-access-custom-expected.txt	2016-03-04 00:47:55 UTC (rev 197531)
@@ -17,6 +17,7 @@
 CONSOLE MESSAGE: line 82: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a frame with origin "http://localhost:8000". Protocols, domains, and ports must match.
 CONSOLE MESSAGE: line 81: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a frame with origin "http://localhost:8000". Protocols, domains, and ports must match.
 CONSOLE MESSAGE: line 107: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a frame with origin "http://localhost:8000". Protocols, domains, and ports must match.
+CONSOLE MESSAGE: line 107: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a frame with origin "http://localhost:8000". Protocols, domains, and ports must match.
 
 
 ----- tests for getting/setting interesting properties -----

Modified: trunk/LayoutTests/http/tests/security/cross-frame-access-location-get-expected.txt (197530 => 197531)


--- trunk/LayoutTests/http/tests/security/cross-frame-access-location-get-expected.txt	2016-03-04 00:39:41 UTC (rev 197530)
+++ trunk/LayoutTests/http/tests/security/cross-frame-access-location-get-expected.txt	2016-03-04 00:47:55 UTC (rev 197531)
@@ -1,5 +1,6 @@
 CONSOLE MESSAGE: line 107: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a frame with origin "http://localhost:8000". Protocols, domains, and ports must match.
 CONSOLE MESSAGE: line 107: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a frame with origin "http://localhost:8000". Protocols, domains, and ports must match.
+CONSOLE MESSAGE: line 107: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a frame with origin "http://localhost:8000". Protocols, domains, and ports must match.
 CONSOLE MESSAGE: line 55: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a frame with origin "http://localhost:8000". Protocols, domains, and ports must match.
 CONSOLE MESSAGE: line 55: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a frame with origin "http://localhost:8000". Protocols, domains, and ports must match.
 CONSOLE MESSAGE: line 55: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a frame with origin "http://localhost:8000". Protocols, domains, and ports must match.

Modified: trunk/LayoutTests/http/tests/security/cross-frame-access-object-setPrototypeOf-expected.txt (197530 => 197531)


--- trunk/LayoutTests/http/tests/security/cross-frame-access-object-setPrototypeOf-expected.txt	2016-03-04 00:39:41 UTC (rev 197530)
+++ trunk/LayoutTests/http/tests/security/cross-frame-access-object-setPrototypeOf-expected.txt	2016-03-04 00:47:55 UTC (rev 197531)
@@ -1,5 +1,6 @@
 CONSOLE MESSAGE: line 1: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a frame with origin "http://localhost:8000". Protocols, domains, and ports must match.
 CONSOLE MESSAGE: line 22: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a frame with origin "http://localhost:8000". Protocols, domains, and ports must match.
+CONSOLE MESSAGE: line 22: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a frame with origin "http://localhost:8000". Protocols, domains, and ports must match.
 CONSOLE MESSAGE: line 25: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a frame with origin "http://localhost:8000". Protocols, domains, and ports must match.
 This tests that you can't set the prototype of the window or history objects cross-origin using Object.setPrototypeOf().
 

Modified: trunk/LayoutTests/js/Object-getOwnPropertyNames-expected.txt (197530 => 197531)


--- trunk/LayoutTests/js/Object-getOwnPropertyNames-expected.txt	2016-03-04 00:39:41 UTC (rev 197530)
+++ trunk/LayoutTests/js/Object-getOwnPropertyNames-expected.txt	2016-03-04 00:47:55 UTC (rev 197531)
@@ -61,7 +61,7 @@
 PASS getSortedOwnPropertyNames(Error.prototype) is ['constructor', 'message', 'name', 'toString']
 PASS getSortedOwnPropertyNames(Math) is ['E','LN10','LN2','LOG10E','LOG2E','PI','SQRT1_2','SQRT2','abs','acos','acosh','asin','asinh','atan','atan2','atanh','cbrt','ceil','clz32','cos','cosh','exp','expm1','floor','fround','hypot','imul','log','log10','log1p','log2','max','min','pow','random','round','sign','sin','sinh','sqrt','tan','tanh','trunc']
 PASS getSortedOwnPropertyNames(JSON) is ['parse', 'stringify']
-PASS getSortedOwnPropertyNames(Symbol) is ['for', 'hasInstance', 'iterator', 'keyFor', 'length', 'name', 'prototype', 'search', 'species', 'toStringTag', 'unscopables']
+PASS getSortedOwnPropertyNames(Symbol) is ['for', 'hasInstance', 'iterator', 'keyFor', 'length', 'name', 'prototype', 'search', 'species', 'toPrimitive', 'toStringTag', 'unscopables']
 PASS getSortedOwnPropertyNames(Symbol.prototype) is ['constructor', 'toString', 'valueOf']
 PASS getSortedOwnPropertyNames(Map) is ['length', 'name', 'prototype']
 PASS getSortedOwnPropertyNames(Map.prototype) is ['clear', 'constructor', 'delete', 'entries', 'forEach', 'get', 'has', 'keys', 'set', 'size', 'values']

Modified: trunk/LayoutTests/js/script-tests/Object-getOwnPropertyNames.js (197530 => 197531)


--- trunk/LayoutTests/js/script-tests/Object-getOwnPropertyNames.js	2016-03-04 00:39:41 UTC (rev 197530)
+++ trunk/LayoutTests/js/script-tests/Object-getOwnPropertyNames.js	2016-03-04 00:47:55 UTC (rev 197531)
@@ -70,7 +70,7 @@
     "Error.prototype": "['constructor', 'message', 'name', 'toString']",
     "Math": "['E','LN10','LN2','LOG10E','LOG2E','PI','SQRT1_2','SQRT2','abs','acos','acosh','asin','asinh','atan','atan2','atanh','cbrt','ceil','clz32','cos','cosh','exp','expm1','floor','fround','hypot','imul','log','log10','log1p','log2','max','min','pow','random','round','sign','sin','sinh','sqrt','tan','tanh','trunc']",
     "JSON": "['parse', 'stringify']",
-    "Symbol": "['for', 'hasInstance', 'iterator', 'keyFor', 'length', 'name', 'prototype', 'search', 'species', 'toStringTag', 'unscopables']",
+    "Symbol": "['for', 'hasInstance', 'iterator', 'keyFor', 'length', 'name', 'prototype', 'search', 'species', 'toPrimitive', 'toStringTag', 'unscopables']",
     "Symbol.prototype": "['constructor', 'toString', 'valueOf']",
     "Map": "['length', 'name', 'prototype']",
     "Map.prototype": "['clear', 'constructor', 'delete', 'entries', 'forEach', 'get', 'has', 'keys', 'set', 'size', 'values']",

Modified: trunk/Source/_javascript_Core/ChangeLog (197530 => 197531)


--- trunk/Source/_javascript_Core/ChangeLog	2016-03-04 00:39:41 UTC (rev 197530)
+++ trunk/Source/_javascript_Core/ChangeLog	2016-03-04 00:47:55 UTC (rev 197531)
@@ -1,3 +1,58 @@
+2016-03-03  Keith Miller  <[email protected]>
+
+        [ES6] Add support for Symbol.toPrimitive
+        https://bugs.webkit.org/show_bug.cgi?id=154877
+
+        Reviewed by Saam Barati.
+
+        This patch adds suport for Symbol.toPrimitive. Since we don't currently
+        generate snippits for one side of a binary operation we only need to change
+        the JSObject::ToPrimitive function and update some optimizations in the DFG
+        that need to know how conversions to primitive values should work. As of
+        ES6, the date prototype is also no longer special cased in the ToPrimitive
+        operation. Instead, Date.prototype has a Symbol.species function that
+        replicates the old behavior.
+
+        * bytecode/ObjectPropertyConditionSet.cpp:
+        (JSC::generateConditionsForPropertyMissConcurrently):
+        * bytecode/ObjectPropertyConditionSet.h:
+        * dfg/DFGGraph.cpp:
+        (JSC::DFG::Graph::watchConditions):
+        (JSC::DFG::Graph::canOptimizeStringObjectAccess):
+        * dfg/DFGGraph.h:
+        * runtime/CommonIdentifiers.h:
+        * runtime/DatePrototype.cpp:
+        (JSC::DatePrototype::finishCreation):
+        (JSC::dateProtoFuncToPrimitiveSymbol):
+        * runtime/Error.cpp:
+        (JSC::throwTypeError):
+        * runtime/Error.h:
+        * runtime/JSCJSValueInlines.h:
+        (JSC::toPreferredPrimitiveType):
+        * runtime/JSObject.cpp:
+        (JSC::callToPrimitiveFunction):
+        (JSC::JSObject::ordinaryToPrimitive):
+        (JSC::JSObject::defaultValue):
+        (JSC::JSObject::toPrimitive):
+        (JSC::JSObject::getPrimitiveNumber):
+        (JSC::callDefaultValueFunction): Deleted.
+        (JSC::throwTypeError): Deleted.
+        * runtime/JSObject.h:
+        (JSC::JSObject::toPrimitive): Deleted.
+        * runtime/SmallStrings.h:
+        * runtime/SymbolPrototype.cpp:
+        (JSC::SymbolPrototype::finishCreation):
+        * runtime/SymbolPrototype.h:
+        (JSC::SymbolPrototype::create):
+        * tests/es6.yaml:
+        * tests/stress/date-symbol-toprimitive.js: Added.
+        * tests/stress/ropes-symbol-toprimitive.js: Added.
+        (ropify):
+        (String.prototype.Symbol.toPrimitive):
+        * tests/stress/symbol-toprimitive.js: Added.
+        (foo.Symbol.toPrimitive):
+        (catch):
+
 2016-03-03  Filip Pizlo  <[email protected]>
 
         DFG should be able to compile StringReplace

Modified: trunk/Source/_javascript_Core/bytecode/ObjectPropertyConditionSet.cpp (197530 => 197531)


--- trunk/Source/_javascript_Core/bytecode/ObjectPropertyConditionSet.cpp	2016-03-04 00:39:41 UTC (rev 197530)
+++ trunk/Source/_javascript_Core/bytecode/ObjectPropertyConditionSet.cpp	2016-03-04 00:47:55 UTC (rev 197531)
@@ -349,6 +349,20 @@
         });
 }
 
+ObjectPropertyConditionSet generateConditionsForPropertyMissConcurrently(
+    VM& vm, JSGlobalObject* globalObject, Structure* headStructure, UniquedStringImpl* uid)
+{
+    return generateConditions(
+        vm, globalObject, headStructure, nullptr,
+        [&] (Vector<ObjectPropertyCondition>& conditions, JSObject* object) -> bool {
+            ObjectPropertyCondition result = generateCondition(vm, nullptr, object, uid, PropertyCondition::Absence);
+            if (!result)
+                return false;
+            conditions.append(result);
+            return true;
+        }, Concurrent);
+}
+
 ObjectPropertyConditionSet generateConditionsForPropertySetterMissConcurrently(
     VM& vm, JSGlobalObject* globalObject, Structure* headStructure, UniquedStringImpl* uid)
 {

Modified: trunk/Source/_javascript_Core/bytecode/ObjectPropertyConditionSet.h (197530 => 197531)


--- trunk/Source/_javascript_Core/bytecode/ObjectPropertyConditionSet.h	2016-03-04 00:39:41 UTC (rev 197530)
+++ trunk/Source/_javascript_Core/bytecode/ObjectPropertyConditionSet.h	2016-03-04 00:47:55 UTC (rev 197531)
@@ -166,6 +166,9 @@
     VM&, JSCell* owner, ExecState*, Structure* headStructure, JSObject* prototype,
     UniquedStringImpl* uid);
 
+
+ObjectPropertyConditionSet generateConditionsForPropertyMissConcurrently(
+    VM&, JSGlobalObject*, Structure* headStructure, UniquedStringImpl* uid);
 ObjectPropertyConditionSet generateConditionsForPropertySetterMissConcurrently(
     VM&, JSGlobalObject*, Structure* headStructure, UniquedStringImpl* uid);
 

Modified: trunk/Source/_javascript_Core/dfg/DFGGraph.cpp (197530 => 197531)


--- trunk/Source/_javascript_Core/dfg/DFGGraph.cpp	2016-03-04 00:39:41 UTC (rev 197530)
+++ trunk/Source/_javascript_Core/dfg/DFGGraph.cpp	2016-03-04 00:47:55 UTC (rev 197531)
@@ -890,6 +890,18 @@
     return true;
 }
 
+bool Graph::watchConditions(const ObjectPropertyConditionSet& keys)
+{
+    if (!keys.isValid())
+        return false;
+
+    for (const ObjectPropertyCondition& key : keys) {
+        if (!watchCondition(key))
+            return false;
+    }
+    return true;
+}
+
 bool Graph::isSafeToLoad(JSObject* base, PropertyOffset offset)
 {
     return m_safeToLoad.contains(std::make_pair(base, offset));
@@ -1518,6 +1530,9 @@
     if (stringPrototypeStructure->isDictionary())
         return false;
 
+    if (!watchConditions(generateConditionsForPropertyMissConcurrently(m_vm, globalObjectFor(codeOrigin), stringObjectStructure, m_vm.propertyNames->toPrimitiveSymbol.impl())))
+        return false;
+
     // We're being conservative here. We want DFG's ToString on StringObject to be
     // used in both numeric contexts (that would call valueOf()) and string contexts
     // (that would call toString()). We don't want the DFG to have to distinguish

Modified: trunk/Source/_javascript_Core/dfg/DFGGraph.h (197530 => 197531)


--- trunk/Source/_javascript_Core/dfg/DFGGraph.h	2016-03-04 00:39:41 UTC (rev 197530)
+++ trunk/Source/_javascript_Core/dfg/DFGGraph.h	2016-03-04 00:47:55 UTC (rev 197531)
@@ -650,6 +650,7 @@
     // this also makes it cheap to query if the condition holds. Also makes sure that the GC knows
     // what's going on.
     bool watchCondition(const ObjectPropertyCondition&);
+    bool watchConditions(const ObjectPropertyConditionSet&);
 
     // Checks if it's known that loading from the given object at the given offset is fine. This is
     // computed by tracking which conditions we track with watchCondition().

Modified: trunk/Source/_javascript_Core/runtime/CommonIdentifiers.h (197530 => 197531)


--- trunk/Source/_javascript_Core/runtime/CommonIdentifiers.h	2016-03-04 00:39:41 UTC (rev 197530)
+++ trunk/Source/_javascript_Core/runtime/CommonIdentifiers.h	2016-03-04 00:47:55 UTC (rev 197531)
@@ -276,13 +276,13 @@
     macro(match) \
     macro(replace) \
     macro(split) \
-    macro(toPrimitive)
 
 #define JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_WELL_KNOWN_SYMBOL(macro) \
     macro(hasInstance) \
     macro(iterator) \
     macro(search) \
     macro(species) \
+    macro(toPrimitive) \
     macro(toStringTag) \
     macro(unscopables)
 

Modified: trunk/Source/_javascript_Core/runtime/DatePrototype.cpp (197530 => 197531)


--- trunk/Source/_javascript_Core/runtime/DatePrototype.cpp	2016-03-04 00:39:41 UTC (rev 197530)
+++ trunk/Source/_javascript_Core/runtime/DatePrototype.cpp	2016-03-04 00:47:55 UTC (rev 197531)
@@ -111,6 +111,7 @@
 EncodedJSValue JSC_HOST_CALL dateProtoFuncToLocaleDateString(ExecState*);
 EncodedJSValue JSC_HOST_CALL dateProtoFuncToLocaleString(ExecState*);
 EncodedJSValue JSC_HOST_CALL dateProtoFuncToLocaleTimeString(ExecState*);
+EncodedJSValue JSC_HOST_CALL dateProtoFuncToPrimitiveSymbol(ExecState*);
 EncodedJSValue JSC_HOST_CALL dateProtoFuncToString(ExecState*);
 EncodedJSValue JSC_HOST_CALL dateProtoFuncToTimeString(ExecState*);
 EncodedJSValue JSC_HOST_CALL dateProtoFuncToUTCString(ExecState*);
@@ -499,6 +500,7 @@
     UNUSED_PARAM(globalObject);
 #endif // ENABLE(INTL)
 
+    JSC_NATIVE_FUNCTION(vm.propertyNames->toPrimitiveSymbol, dateProtoFuncToPrimitiveSymbol, DontEnum | ReadOnly, 1);
     // The constructor will be added later, after DateConstructor has been built.
 }
 
@@ -597,6 +599,27 @@
     return JSValue::encode(formatLocaleDate(exec, thisDateObj, thisDateObj->internalNumber(), LocaleTime));
 }
 
+EncodedJSValue JSC_HOST_CALL dateProtoFuncToPrimitiveSymbol(ExecState* exec)
+{
+    JSValue thisValue = exec->thisValue();
+    if (!thisValue.isObject())
+        return throwVMTypeError(exec, "Date.prototype[Symbol.toPrimitive] expected |this| to be an object.");
+    JSObject* thisObject = jsCast<JSObject*>(thisValue);
+
+    if (!exec->argumentCount())
+        return throwVMTypeError(exec, "Date.prototype[Symbol.toPrimitive] expected a first argument.");
+
+    JSValue hintValue = exec->uncheckedArgument(0);
+    PreferredPrimitiveType type = toPreferredPrimitiveType(exec, hintValue);
+    if (exec->hadException())
+        return JSValue::encode(JSValue());
+
+    if (type == NoPreference)
+        type = PreferString;
+
+    return JSValue::encode(thisObject->ordinaryToPrimitive(exec, type));
+}
+
 EncodedJSValue JSC_HOST_CALL dateProtoFuncGetTime(ExecState* exec)
 {
     JSValue thisValue = exec->thisValue();

Modified: trunk/Source/_javascript_Core/runtime/Error.cpp (197530 => 197531)


--- trunk/Source/_javascript_Core/runtime/Error.cpp	2016-03-04 00:39:41 UTC (rev 197530)
+++ trunk/Source/_javascript_Core/runtime/Error.cpp	2016-03-04 00:47:55 UTC (rev 197531)
@@ -219,6 +219,11 @@
     return exec->vm().throwException(exec, createTypeError(exec));
 }
 
+JSObject* throwTypeError(ExecState* exec, const String& message)
+{
+    return exec->vm().throwException(exec, createTypeError(exec, message));
+}
+
 JSObject* throwSyntaxError(ExecState* exec)
 {
     return exec->vm().throwException(exec, createSyntaxError(exec, ASCIILiteral("Syntax error")));

Modified: trunk/Source/_javascript_Core/runtime/Error.h (197530 => 197531)


--- trunk/Source/_javascript_Core/runtime/Error.h	2016-03-04 00:39:41 UTC (rev 197530)
+++ trunk/Source/_javascript_Core/runtime/Error.h	2016-03-04 00:47:55 UTC (rev 197531)
@@ -74,6 +74,7 @@
 // Convenience wrappers, create an throw an exception with a default message.
 JS_EXPORT_PRIVATE JSObject* throwConstructorCannotBeCalledAsFunctionTypeError(ExecState*, const char* constructorName);
 JS_EXPORT_PRIVATE JSObject* throwTypeError(ExecState*);
+JS_EXPORT_PRIVATE JSObject* throwTypeError(ExecState*, const String& errorMessage);
 JS_EXPORT_PRIVATE JSObject* throwSyntaxError(ExecState*);
 JS_EXPORT_PRIVATE JSObject* throwSyntaxError(ExecState*, const String& errorMessage);
 inline JSObject* throwRangeError(ExecState* state, const String& errorMessage) { return state->vm().throwException(state, createRangeError(state, errorMessage)); }

Modified: trunk/Source/_javascript_Core/runtime/JSCJSValueInlines.h (197530 => 197531)


--- trunk/Source/_javascript_Core/runtime/JSCJSValueInlines.h	2016-03-04 00:39:41 UTC (rev 197530)
+++ trunk/Source/_javascript_Core/runtime/JSCJSValueInlines.h	2016-03-04 00:47:55 UTC (rev 197531)
@@ -628,6 +628,28 @@
     return isCell() ? asCell()->toPrimitive(exec, preferredType) : asValue();
 }
 
+inline PreferredPrimitiveType toPreferredPrimitiveType(ExecState* exec, JSValue value)
+{
+    if (!value.isString()) {
+        throwTypeError(exec, "Primitive hint is not a string.");
+        return NoPreference;
+    }
+
+    StringImpl* hintString = jsCast<JSString*>(value)->value(exec).impl();
+    if (exec->hadException())
+        return NoPreference;
+
+    if (WTF::equal(hintString, "default"))
+        return NoPreference;
+    if (WTF::equal(hintString, "number"))
+        return PreferNumber;
+    if (WTF::equal(hintString, "string"))
+        return PreferString;
+
+    throwTypeError(exec, "Expected primitive hint to match one of 'default', 'number', 'string'.");
+    return NoPreference;
+}
+
 inline bool JSValue::getPrimitiveNumber(ExecState* exec, double& number, JSValue& value)
 {
     if (isInt32()) {

Modified: trunk/Source/_javascript_Core/runtime/JSObject.cpp (197530 => 197531)


--- trunk/Source/_javascript_Core/runtime/JSObject.cpp	2016-03-04 00:39:41 UTC (rev 197530)
+++ trunk/Source/_javascript_Core/runtime/JSObject.cpp	2016-03-04 00:47:55 UTC (rev 197531)
@@ -1426,55 +1426,67 @@
     }
 }
 
-static ALWAYS_INLINE JSValue callDefaultValueFunction(ExecState* exec, const JSObject* object, PropertyName propertyName)
+enum class TypeHintMode { TakesHint, DoesNotTakeHint };
+
+template<TypeHintMode mode = TypeHintMode::DoesNotTakeHint>
+static ALWAYS_INLINE JSValue callToPrimitiveFunction(ExecState* exec, const JSObject* object, PropertyName propertyName, PreferredPrimitiveType hint)
 {
     JSValue function = object->get(exec, propertyName);
+    if (exec->hadException())
+        return exec->exception();
+    if (function.isUndefined() && mode == TypeHintMode::TakesHint)
+        return JSValue();
     CallData callData;
     CallType callType = getCallData(function, callData);
     if (callType == CallTypeNone)
         return exec->exception();
 
-    // Prevent "toString" and "valueOf" from observing execution if an exception
-    // is pending.
-    if (exec->hadException())
-        return exec->exception();
+    MarkedArgumentBuffer callArgs;
+    if (mode == TypeHintMode::TakesHint) {
+        JSString* hintString;
+        switch (hint) {
+        case NoPreference:
+            hintString = exec->vm().smallStrings.defaultString();
+            break;
+        case PreferNumber:
+            hintString = exec->vm().smallStrings.numberString();
+            break;
+        case PreferString:
+            hintString = exec->vm().smallStrings.stringString();
+            break;
+        }
+        callArgs.append(hintString);
+    }
 
-    JSValue result = call(exec, function, callType, callData, const_cast<JSObject*>(object), exec->emptyList());
+    JSValue result = call(exec, function, callType, callData, const_cast<JSObject*>(object), callArgs);
     ASSERT(!result.isGetterSetter());
     if (exec->hadException())
         return exec->exception();
     if (result.isObject())
-        return JSValue();
+        return mode == TypeHintMode::DoesNotTakeHint ? JSValue() : throwTypeError(exec, "Symbol.toPrimitive returned an object");
     return result;
 }
 
-bool JSObject::getPrimitiveNumber(ExecState* exec, double& number, JSValue& result) const
+// ECMA 7.1.1
+inline JSValue JSObject::ordinaryToPrimitive(ExecState* exec, PreferredPrimitiveType hint) const
 {
-    result = methodTable(exec->vm())->defaultValue(this, exec, PreferNumber);
-    number = result.toNumber(exec);
-    return !result.isString();
-}
-
-// ECMA 8.6.2.6
-JSValue JSObject::defaultValue(const JSObject* object, ExecState* exec, PreferredPrimitiveType hint)
-{
     // Make sure that whatever default value methods there are on object's prototype chain are
     // being watched.
-    object->structure()->startWatchingInternalPropertiesIfNecessaryForEntireChain(exec->vm());
-    
-    // Must call toString first for Date objects.
-    if ((hint == PreferString) || (hint != PreferNumber && object->prototype() == exec->lexicalGlobalObject()->datePrototype())) {
-        JSValue value = callDefaultValueFunction(exec, object, exec->propertyNames().toString);
+    this->structure()->startWatchingInternalPropertiesIfNecessaryForEntireChain(exec->vm());
+
+    JSValue value;
+    if (hint == PreferString) {
+        value = callToPrimitiveFunction(exec, this, exec->propertyNames().toString, hint);
         if (value)
             return value;
-        value = callDefaultValueFunction(exec, object, exec->propertyNames().valueOf);
+        value = callToPrimitiveFunction(exec, this, exec->propertyNames().valueOf, hint);
         if (value)
             return value;
     } else {
-        JSValue value = callDefaultValueFunction(exec, object, exec->propertyNames().valueOf);
+        value = callToPrimitiveFunction(exec, this, exec->propertyNames().valueOf, hint);
         if (value)
             return value;
-        value = callDefaultValueFunction(exec, object, exec->propertyNames().toString);
+        value = callToPrimitiveFunction(exec, this, exec->propertyNames().toString, hint);
         if (value)
             return value;
     }
@@ -1484,6 +1496,27 @@
     return exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("No default value")));
 }
 
+JSValue JSObject::defaultValue(const JSObject* object, ExecState* exec, PreferredPrimitiveType hint)
+{
+    return object->ordinaryToPrimitive(exec, hint);
+}
+
+JSValue JSObject::toPrimitive(ExecState* exec, PreferredPrimitiveType preferredType) const
+{
+    JSValue value = callToPrimitiveFunction<TypeHintMode::TakesHint>(exec, this, exec->propertyNames().toPrimitiveSymbol, preferredType);
+    if (value)
+        return value;
+
+    return this->methodTable(exec->vm())->defaultValue(this, exec, preferredType);
+}
+
+bool JSObject::getPrimitiveNumber(ExecState* exec, double& number, JSValue& result) const
+{
+    result = toPrimitive(exec, PreferNumber);
+    number = result.toNumber(exec);
+    return !result.isString();
+}
+
 const HashTableValue* JSObject::findPropertyHashEntry(PropertyName propertyName) const
 {
     for (const ClassInfo* info = classInfo(); info; info = info->parentClass) {
@@ -2912,11 +2945,6 @@
     return object->defineOwnNonIndexProperty(exec, propertyName, descriptor, throwException);
 }
 
-JSObject* throwTypeError(ExecState* exec, const String& message)
-{
-    return exec->vm().throwException(exec, createTypeError(exec, message));
-}
-
 void JSObject::convertToDictionary(VM& vm)
 {
     DeferredStructureTransitionWatchpointFire deferredWatchpointFire;

Modified: trunk/Source/_javascript_Core/runtime/JSObject.h (197530 => 197531)


--- trunk/Source/_javascript_Core/runtime/JSObject.h	2016-03-04 00:39:41 UTC (rev 197530)
+++ trunk/Source/_javascript_Core/runtime/JSObject.h	2016-03-04 00:47:55 UTC (rev 197531)
@@ -504,6 +504,7 @@
     JS_EXPORT_PRIVATE static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned propertyName);
 
     JS_EXPORT_PRIVATE static JSValue defaultValue(const JSObject*, ExecState*, PreferredPrimitiveType);
+    JS_EXPORT_PRIVATE JSValue ordinaryToPrimitive(ExecState*, PreferredPrimitiveType) const;
 
     JS_EXPORT_PRIVATE bool hasInstance(ExecState*, JSValue value, JSValue hasInstanceValue);
     bool hasInstance(ExecState*, JSValue);
@@ -517,7 +518,7 @@
     JS_EXPORT_PRIVATE static void getStructurePropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
     JS_EXPORT_PRIVATE static void getGenericPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
 
-    JSValue toPrimitive(ExecState*, PreferredPrimitiveType = NoPreference) const;
+    JS_EXPORT_PRIVATE JSValue toPrimitive(ExecState*, PreferredPrimitiveType = NoPreference) const;
     bool getPrimitiveNumber(ExecState*, double& number, JSValue&) const;
     JS_EXPORT_PRIVATE double toNumber(ExecState*) const;
     JS_EXPORT_PRIVATE JSString* toString(ExecState*) const;
@@ -1452,11 +1453,6 @@
     putDirect(vm, offset, value);
 }
 
-inline JSValue JSObject::toPrimitive(ExecState* exec, PreferredPrimitiveType preferredType) const
-{
-    return methodTable()->defaultValue(this, exec, preferredType);
-}
-
 ALWAYS_INLINE JSObject* Register::object() const
 {
     return asObject(jsValue());

Modified: trunk/Source/_javascript_Core/runtime/SmallStrings.h (197530 => 197531)


--- trunk/Source/_javascript_Core/runtime/SmallStrings.h	2016-03-04 00:39:41 UTC (rev 197530)
+++ trunk/Source/_javascript_Core/runtime/SmallStrings.h	2016-03-04 00:47:55 UTC (rev 197531)
@@ -31,6 +31,7 @@
 #include <wtf/Noncopyable.h>
 
 #define JSC_COMMON_STRINGS_EACH_NAME(macro) \
+    macro(default) \
     macro(boolean) \
     macro(false) \
     macro(function) \

Modified: trunk/Source/_javascript_Core/runtime/SymbolPrototype.cpp (197530 => 197531)


--- trunk/Source/_javascript_Core/runtime/SymbolPrototype.cpp	2016-03-04 00:39:41 UTC (rev 197530)
+++ trunk/Source/_javascript_Core/runtime/SymbolPrototype.cpp	2016-03-04 00:47:55 UTC (rev 197531)
@@ -56,11 +56,13 @@
 {
 }
 
-void SymbolPrototype::finishCreation(VM& vm)
+void SymbolPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject)
 {
     Base::finishCreation(vm);
     putDirectWithoutTransition(vm, vm.propertyNames->toStringTagSymbol, jsString(&vm, "Symbol"), DontEnum | ReadOnly);
     ASSERT(inherits(info()));
+
+    JSC_NATIVE_FUNCTION(vm.propertyNames->toPrimitiveSymbol, symbolProtoFuncValueOf, DontEnum | ReadOnly, 1);
 }
 
 bool SymbolPrototype::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot &slot)

Modified: trunk/Source/_javascript_Core/runtime/SymbolPrototype.h (197530 => 197531)


--- trunk/Source/_javascript_Core/runtime/SymbolPrototype.h	2016-03-04 00:39:41 UTC (rev 197530)
+++ trunk/Source/_javascript_Core/runtime/SymbolPrototype.h	2016-03-04 00:47:55 UTC (rev 197531)
@@ -38,10 +38,10 @@
     typedef JSNonFinalObject Base;
     static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot;
 
-    static SymbolPrototype* create(VM& vm, JSGlobalObject*, Structure* structure)
+    static SymbolPrototype* create(VM& vm, JSGlobalObject* globalObject, Structure* structure)
     {
         SymbolPrototype* prototype = new (NotNull, allocateCell<SymbolPrototype>(vm.heap)) SymbolPrototype(vm, structure);
-        prototype->finishCreation(vm);
+        prototype->finishCreation(vm, globalObject);
         return prototype;
     }
 
@@ -54,7 +54,7 @@
 
 protected:
     SymbolPrototype(VM&, Structure*);
-    void finishCreation(VM&);
+    void finishCreation(VM&, JSGlobalObject*);
 
 private:
     static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&);

Modified: trunk/Source/_javascript_Core/tests/es6.yaml (197530 => 197531)


--- trunk/Source/_javascript_Core/tests/es6.yaml	2016-03-04 00:39:41 UTC (rev 197530)
+++ trunk/Source/_javascript_Core/tests/es6.yaml	2016-03-04 00:47:55 UTC (rev 197531)
@@ -971,7 +971,7 @@
 - path: es6/Proxy_internal_get_calls_CreateListFromArrayLike.js
   cmd: runES6 :normal
 - path: es6/Proxy_internal_get_calls_Date.prototype.toJSON.js
-  cmd: runES6 :fail
+  cmd: runES6 :normal
 - path: es6/Proxy_internal_get_calls_Error.prototype.toString.js
   cmd: runES6 :normal
 - path: es6/Proxy_internal_get_calls_Function.prototype.bind.js
@@ -1009,13 +1009,13 @@
 - path: es6/Proxy_internal_get_calls_String.prototype.replace.js
   cmd: runES6 :fail
 - path: es6/Proxy_internal_get_calls_String.prototype.search.js
-  cmd: runES6 :fail
+  cmd: runES6 :normal
 - path: es6/Proxy_internal_get_calls_String.prototype.split.js
   cmd: runES6 :fail
 - path: es6/Proxy_internal_get_calls_String.raw.js
   cmd: runES6 :normal
 - path: es6/Proxy_internal_get_calls_ToPrimitive.js
-  cmd: runES6 :fail
+  cmd: runES6 :normal
 - path: es6/Proxy_internal_get_calls_ToPropertyDescriptor.js
   cmd: runES6 :fail
 - path: es6/Proxy_internal_getOwnPropertyDescriptor_calls_[[Set]].js
@@ -1211,7 +1211,7 @@
 - path: es6/well-known_symbols_Symbol.split.js
   cmd: runES6 :fail
 - path: es6/well-known_symbols_Symbol.toPrimitive.js
-  cmd: runES6 :fail
+  cmd: runES6 :normal
 - path: es6/well-known_symbols_Symbol.toStringTag.js
   cmd: runES6 :normal
 - path: es6/well-known_symbols_Symbol.toStringTag_misc._built-ins.js

Added: trunk/Source/_javascript_Core/tests/stress/date-symbol-toprimitive.js (0 => 197531)


--- trunk/Source/_javascript_Core/tests/stress/date-symbol-toprimitive.js	                        (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/date-symbol-toprimitive.js	2016-03-04 00:47:55 UTC (rev 197531)
@@ -0,0 +1,6 @@
+delete Date.prototype[Symbol.toPrimitive]
+
+let date = new Date();
+
+if (typeof (date + 1) !== "number")
+    throw "symbol was not deleted";

Added: trunk/Source/_javascript_Core/tests/stress/ropes-symbol-toprimitive.js (0 => 197531)


--- trunk/Source/_javascript_Core/tests/stress/ropes-symbol-toprimitive.js	                        (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/ropes-symbol-toprimitive.js	2016-03-04 00:47:55 UTC (rev 197531)
@@ -0,0 +1,28 @@
+function ropify(a,b,c) {
+    return a + b + c;
+}
+noInline(ropify);
+
+function ropify2(a,b,c) {
+    return a + b + c;
+}
+noInline(ropify2);
+
+let test = new String("test");
+
+for (let i = 0; i < 100000; i++) {
+    if (ropify("a", "b", test) !== "abtest")
+        throw "wrong on warmup";
+}
+
+String.prototype[Symbol.toPrimitive] = function() { return "changed"; }
+
+if (ropify("a", "b", test) !== "abchanged")
+    throw "watchpoint didn't fire";
+
+
+// Test we don't start doing the wrong thing if the prototype chain has been mucked with.
+for (let i = 0; i < 100000; i++) {
+    if (ropify2("a", "b", test) !== "abchanged")
+        throw "wrong on warmup";
+}

Added: trunk/Source/_javascript_Core/tests/stress/symbol-toprimitive.js (0 => 197531)


--- trunk/Source/_javascript_Core/tests/stress/symbol-toprimitive.js	                        (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/symbol-toprimitive.js	2016-03-04 00:47:55 UTC (rev 197531)
@@ -0,0 +1,18 @@
+// return object
+let foo = { }
+foo[Symbol.toPrimitive] = function() { return {} };
+
+for (i = 0; i < 100000; i++) {
+    let failed = true;
+    try {
+        foo >= 1;
+    } catch (e) {
+        if (e instanceof TypeError)
+            failed = false;
+    }
+
+    if (failed)
+        throw "should have thrown on return of object";
+}
+
+// The general use of Symbol.toPrimitive is covered in the ES6 tests.
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to