[JSC] Add Symbol.prototype.description getter
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 1 Jun 2018 19:06:36 +0000 (19:06 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 1 Jun 2018 19:06:36 +0000 (19:06 +0000)
https://bugs.webkit.org/show_bug.cgi?id=186053

Reviewed by Keith Miller.

JSTests:

* stress/symbol-description.js: Added.
(shouldBe):
(shouldThrow):

Source/JavaScriptCore:

Symbol.prototype.description accessor  is now stage 3[1].
This adds a getter to retrieve [[Description]] value from Symbol.
Previously, Symbol#toString() returns `Symbol(${description})` value.
So users need to extract `description` part if they want it.

[1]: https://tc39.github.io/proposal-Symbol-description/

* runtime/Symbol.cpp:
(JSC::Symbol::description const):
* runtime/Symbol.h:
* runtime/SymbolPrototype.cpp:
(JSC::tryExtractSymbol):
(JSC::symbolProtoGetterDescription):
(JSC::symbolProtoFuncToString):
(JSC::symbolProtoFuncValueOf):

LayoutTests:

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

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@232404 268f45cc-cd09-0410-ab3c-d52691b4dbfc

JSTests/ChangeLog
JSTests/stress/symbol-description.js [new file with mode: 0644]
LayoutTests/ChangeLog
LayoutTests/js/Object-getOwnPropertyNames-expected.txt
LayoutTests/js/script-tests/Object-getOwnPropertyNames.js
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/runtime/Symbol.cpp
Source/JavaScriptCore/runtime/Symbol.h
Source/JavaScriptCore/runtime/SymbolPrototype.cpp

index 9e513dc..e37f8db 100644 (file)
@@ -1,3 +1,14 @@
+2018-05-29  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [JSC] Add Symbol.prototype.description getter
+        https://bugs.webkit.org/show_bug.cgi?id=186053
+
+        Reviewed by Keith Miller.
+
+        * stress/symbol-description.js: Added.
+        (shouldBe):
+        (shouldThrow):
+
 2018-05-30  Yusuke Suzuki  <utatane.tea@gmail.com>
 
         [DFG] InById should be converted to MatchStructure
diff --git a/JSTests/stress/symbol-description.js b/JSTests/stress/symbol-description.js
new file mode 100644 (file)
index 0000000..27e3979
--- /dev/null
@@ -0,0 +1,83 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error(`bad value: ${String(actual)}`);
+}
+
+function shouldThrow(func, errorMessage) {
+    var errorThrown = false;
+    var error = null;
+    try {
+        func();
+    } catch (e) {
+        errorThrown = true;
+        error = e;
+    }
+    if (!errorThrown)
+        throw new Error('not thrown');
+    if (String(error) !== errorMessage)
+        throw new Error(`bad error: ${String(error)}`);
+}
+
+var s0 = Symbol("Cocoa");
+var s1 = Symbol("Cappuccino");
+
+shouldBe(s0.description, "Cocoa");
+shouldBe(s0.toString(), "Symbol(Cocoa)");
+shouldBe(s1.description, "Cappuccino");
+shouldBe(s1.toString(), "Symbol(Cappuccino)");
+
+var o0 = Object(s0);
+var o1 = Object(s1);
+
+shouldBe(o0.description, "Cocoa");
+shouldBe(o0.toString(), "Symbol(Cocoa)");
+shouldBe(o1.description, "Cappuccino");
+shouldBe(o1.toString(), "Symbol(Cappuccino)");
+
+var descriptor = Object.getOwnPropertyDescriptor(Symbol.prototype, "description");
+shouldBe(descriptor.enumerable, false);
+shouldBe(descriptor.configurable, true);
+shouldBe(descriptor.set, undefined);
+shouldBe(typeof descriptor.get, "function");
+
+shouldThrow(() => {
+    "use strict";
+    s0.description = "Matcha";
+}, `TypeError: Attempted to assign to readonly property.`);
+shouldThrow(() => {
+    "use strict";
+    o0.description = "Matcha";
+}, `TypeError: Attempted to assign to readonly property.`);
+
+shouldThrow(() => {
+    descriptor.get.call({});
+}, `TypeError: Symbol.prototype.description requires that |this| be a symbol or a symbol object`);
+
+shouldThrow(() => {
+    descriptor.get.call(null);
+}, `TypeError: Symbol.prototype.description requires that |this| be a symbol or a symbol object`);
+
+shouldThrow(() => {
+    descriptor.get.call(undefined);
+}, `TypeError: Symbol.prototype.description requires that |this| be a symbol or a symbol object`);
+
+shouldThrow(() => {
+    descriptor.get.call(42);
+}, `TypeError: Symbol.prototype.description requires that |this| be a symbol or a symbol object`);
+
+shouldThrow(() => {
+    descriptor.get.call("Hello");
+}, `TypeError: Symbol.prototype.description requires that |this| be a symbol or a symbol object`);
+
+shouldThrow(() => {
+    descriptor.get.call(42.195);
+}, `TypeError: Symbol.prototype.description requires that |this| be a symbol or a symbol object`);
+
+shouldThrow(() => {
+    descriptor.get.call(false);
+}, `TypeError: Symbol.prototype.description requires that |this| be a symbol or a symbol object`);
+
+shouldBe(descriptor.get.call(s0), "Cocoa");
+shouldBe(descriptor.get.call(o0), "Cocoa");
+o0.__proto__ = {};
+shouldBe(descriptor.get.call(o0), "Cocoa");
index 5bc4336..3bff230 100644 (file)
@@ -1,3 +1,13 @@
+2018-05-29  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [JSC] Add Symbol.prototype.description getter
+        https://bugs.webkit.org/show_bug.cgi?id=186053
+
+        Reviewed by Keith Miller.
+
+        * js/Object-getOwnPropertyNames-expected.txt:
+        * js/script-tests/Object-getOwnPropertyNames.js:
+
 2018-06-01  Brent Fulgham  <bfulgham@apple.com>
 
         Unskip fast/html/marquee-reparent-check.html on macOS debug.
index 3eb84a7..99c039a 100644 (file)
@@ -63,7 +63,7 @@ PASS getSortedOwnPropertyNames(Error.prototype) is ['constructor', 'message', 'n
 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 ['asyncIterator','for', 'hasInstance', 'isConcatSpreadable', 'iterator', 'keyFor', 'length', 'match', 'name', 'prototype', 'replace', 'search', 'species', 'split', 'toPrimitive', 'toStringTag', 'unscopables']
-PASS getSortedOwnPropertyNames(Symbol.prototype) is ['constructor', 'toString', 'valueOf']
+PASS getSortedOwnPropertyNames(Symbol.prototype) is ['constructor', 'description', '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']
 PASS getSortedOwnPropertyNames(Set) is ['length', 'name', 'prototype']
index c730ed5..7e1ab5a 100644 (file)
@@ -72,7 +72,7 @@ var expectedPropertyNamesSet = {
     "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": "['asyncIterator','for', 'hasInstance', 'isConcatSpreadable', 'iterator', 'keyFor', 'length', 'match', 'name', 'prototype', 'replace', 'search', 'species', 'split', 'toPrimitive', 'toStringTag', 'unscopables']",
-    "Symbol.prototype": "['constructor', 'toString', 'valueOf']",
+    "Symbol.prototype": "['constructor', 'description', 'toString', 'valueOf']",
     "Map": "['length', 'name', 'prototype']",
     "Map.prototype": "['clear', 'constructor', 'delete', 'entries', 'forEach', 'get', 'has', 'keys', 'set', 'size', 'values']",
     "Set": "['length', 'name', 'prototype']",
index 5539718..17b1929 100644 (file)
@@ -1,3 +1,26 @@
+2018-05-29  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [JSC] Add Symbol.prototype.description getter
+        https://bugs.webkit.org/show_bug.cgi?id=186053
+
+        Reviewed by Keith Miller.
+
+        Symbol.prototype.description accessor  is now stage 3[1].
+        This adds a getter to retrieve [[Description]] value from Symbol.
+        Previously, Symbol#toString() returns `Symbol(${description})` value.
+        So users need to extract `description` part if they want it.
+
+        [1]: https://tc39.github.io/proposal-Symbol-description/
+
+        * runtime/Symbol.cpp:
+        (JSC::Symbol::description const):
+        * runtime/Symbol.h:
+        * runtime/SymbolPrototype.cpp:
+        (JSC::tryExtractSymbol):
+        (JSC::symbolProtoGetterDescription):
+        (JSC::symbolProtoFuncToString):
+        (JSC::symbolProtoFuncValueOf):
+
 2018-06-01  Yusuke Suzuki  <utatane.tea@gmail.com>
 
         [JSC] Correct values and members of JSBigInt appropriately
index 55aad74..d3e465a 100644 (file)
@@ -103,6 +103,11 @@ String Symbol::descriptiveString() const
     return makeString("Symbol(", String(privateName().uid()), ')');
 }
 
+String Symbol::description() const
+{
+    return privateName().uid();
+}
+
 Symbol* Symbol::create(VM& vm)
 {
     Symbol* symbol = new (NotNull, allocateCell<Symbol>(vm.heap)) Symbol(vm);
index 1a4a2a0..71023e6 100644 (file)
@@ -57,6 +57,7 @@ public:
 
     const PrivateName& privateName() const { return m_privateName; }
     String descriptiveString() const;
+    String description() const;
 
     JSValue toPrimitive(ExecState*, PreferredPrimitiveType) const;
     bool getPrimitiveNumber(ExecState*, double& number, JSValue&) const;
index 3d798c6..d8d372a 100644 (file)
@@ -34,6 +34,7 @@
 
 namespace JSC {
 
+static EncodedJSValue JSC_HOST_CALL symbolProtoGetterDescription(ExecState*);
 static EncodedJSValue JSC_HOST_CALL symbolProtoFuncToString(ExecState*);
 static EncodedJSValue JSC_HOST_CALL symbolProtoFuncValueOf(ExecState*);
 
@@ -47,6 +48,7 @@ const ClassInfo SymbolPrototype::s_info = { "Symbol", &Base::s_info, &symbolProt
 
 /* Source for SymbolPrototype.lut.h
 @begin symbolPrototypeTable
+  description       symbolProtoGetterDescription    DontEnum|Accessor 0
   toString          symbolProtoFuncToString         DontEnum|Function 0
   valueOf           symbolProtoFuncValueOf          DontEnum|Function 0
 @end
@@ -69,47 +71,58 @@ void SymbolPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject)
 
 // ------------------------------ Functions ---------------------------
 
+static const char* SymbolDescriptionTypeError = "Symbol.prototype.description requires that |this| be a symbol or a symbol object";
 static const char* SymbolToStringTypeError = "Symbol.prototype.toString requires that |this| be a symbol or a symbol object";
 static const char* SymbolValueOfTypeError = "Symbol.prototype.valueOf requires that |this| be a symbol or a symbol object";
 
-EncodedJSValue JSC_HOST_CALL symbolProtoFuncToString(ExecState* exec)
+inline Symbol* tryExtractSymbol(VM& vm, JSValue thisValue)
+{
+    if (thisValue.isSymbol())
+        return asSymbol(thisValue);
+
+    if (!thisValue.isObject())
+        return nullptr;
+    JSObject* thisObject = asObject(thisValue);
+    if (!thisObject->inherits<SymbolObject>(vm))
+        return nullptr;
+    return asSymbol(jsCast<SymbolObject*>(thisObject)->internalValue());
+}
+
+EncodedJSValue JSC_HOST_CALL symbolProtoGetterDescription(ExecState* exec)
 {
     VM& vm = exec->vm();
     auto scope = DECLARE_THROW_SCOPE(vm);
 
-    JSValue thisValue = exec->thisValue();
-    Symbol* symbol = nullptr;
-    if (thisValue.isSymbol())
-        symbol = asSymbol(thisValue);
-    else {
-        if (!thisValue.isObject())
-            return throwVMTypeError(exec, scope, SymbolToStringTypeError);
-        JSObject* thisObject = asObject(thisValue);
-        if (!thisObject->inherits<SymbolObject>(vm))
-            return throwVMTypeError(exec, scope, SymbolToStringTypeError);
-        symbol = asSymbol(jsCast<SymbolObject*>(thisObject)->internalValue());
-    }
-
-    return JSValue::encode(jsNontrivialString(exec, symbol->descriptiveString()));
+    Symbol* symbol = tryExtractSymbol(vm, exec->thisValue());
+    if (!symbol)
+        return throwVMTypeError(exec, scope, SymbolDescriptionTypeError);
+    scope.release();
+    return JSValue::encode(jsString(&vm, symbol->description()));
 }
 
-EncodedJSValue JSC_HOST_CALL symbolProtoFuncValueOf(ExecState* exec)
+EncodedJSValue JSC_HOST_CALL symbolProtoFuncToString(ExecState* exec)
 {
     VM& vm = exec->vm();
     auto scope = DECLARE_THROW_SCOPE(vm);
 
-    JSValue thisValue = exec->thisValue();
-    if (thisValue.isSymbol())
-        return JSValue::encode(thisValue);
+    Symbol* symbol = tryExtractSymbol(vm, exec->thisValue());
+    if (!symbol)
+        return throwVMTypeError(exec, scope, SymbolToStringTypeError);
+    scope.release();
+    return JSValue::encode(jsNontrivialString(&vm, symbol->descriptiveString()));
+}
 
-    if (!thisValue.isObject())
-        return throwVMTypeError(exec, scope, SymbolValueOfTypeError);
+EncodedJSValue JSC_HOST_CALL symbolProtoFuncValueOf(ExecState* exec)
+{
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
 
-    JSObject* thisObject = asObject(thisValue);
-    if (!thisObject->inherits<SymbolObject>(vm))
+    Symbol* symbol = tryExtractSymbol(vm, exec->thisValue());
+    if (!symbol)
         return throwVMTypeError(exec, scope, SymbolValueOfTypeError);
 
-    return JSValue::encode(jsCast<SymbolObject*>(thisObject)->internalValue());
+    scope.release();
+    return JSValue::encode(symbol);
 }
 
 } // namespace JSC