From 92371354579a2482b2f19daa57079f777c2ead35 Mon Sep 17 00:00:00 2001 From: Xuan Huang Date: Fri, 20 Nov 2020 19:16:25 -0800 Subject: [PATCH] Add Symbol.prototype.description Summary: This diff add a new getter `description` to `Symbol.prototype`, which returned the [[Description]] slot w/o wrapping into `Symbol(...)`. Noted that this is NOT spec-conformant as for the `Symbol(undefined)`. Reviewed By: avp Differential Revision: D25002865 fbshipit-source-id: 25bddb8366c447f3995dd822dac3bb37099f3d84 --- doc/Features.md | 1 + include/hermes/VM/NativeFunctions.def | 1 + include/hermes/VM/PredefinedStrings.def | 1 + lib/VM/JSLib/Symbol.cpp | 30 +++++++++++++++++++++++++ test/hermes/symbol.js | 20 +++++++++++++++++ 5 files changed, 53 insertions(+) diff --git a/doc/Features.md b/doc/Features.md index 79f54cb5e1a..5423a571362 100644 --- a/doc/Features.md +++ b/doc/Features.md @@ -31,6 +31,7 @@ Hermes plans to target ECMAScript 2015 (ES6), with some carefully considered exc - `Intl` API glue for Android has been added, in order to enable community contribution of a complete, spec-compliant implementation. - Promise is opt-in (via `-Xes6-promise` flag or `withES6Promise` runtime config). - Async function (`async` and `await`). +- `Symbol.prototype.description` (it's not fully spec-conformant yet. `Symbol().description` should be `undefined` but it's currently `''`). ## Excluded From Support diff --git a/include/hermes/VM/NativeFunctions.def b/include/hermes/VM/NativeFunctions.def index ff77573e8ce..9fdc67d9c40 100644 --- a/include/hermes/VM/NativeFunctions.def +++ b/include/hermes/VM/NativeFunctions.def @@ -330,6 +330,7 @@ NATIVE_FUNCTION(symbolConstructor) NATIVE_FUNCTION(symbolFor) NATIVE_FUNCTION(symbolKeyFor) NATIVE_FUNCTION(symbolPrototypeToString) +NATIVE_FUNCTION(symbolPrototypeDescriptionGetter) NATIVE_FUNCTION(symbolPrototypeValueOf) NATIVE_FUNCTION(throwTypeError) NATIVE_FUNCTION(typedArrayBaseConstructor) diff --git a/include/hermes/VM/PredefinedStrings.def b/include/hermes/VM/PredefinedStrings.def index 87e146d0514..c162e7c265b 100644 --- a/include/hermes/VM/PredefinedStrings.def +++ b/include/hermes/VM/PredefinedStrings.def @@ -28,6 +28,7 @@ STR(symbol, "symbol") STR(object, "object") STR(function, "function") STR(toString, "toString") +STR(description, "description") STR(toLocaleString, "toLocaleString") STR(hasOwnProperty, "hasOwnProperty") STR(isPrototypeOf, "isPrototypeOf") diff --git a/lib/VM/JSLib/Symbol.cpp b/lib/VM/JSLib/Symbol.cpp index b8567fafd13..423df1ec0bb 100644 --- a/lib/VM/JSLib/Symbol.cpp +++ b/lib/VM/JSLib/Symbol.cpp @@ -128,6 +128,15 @@ Handle createSymbolConstructor(Runtime *runtime) { // Symbol.prototype.xxx methods. void *ctx = nullptr; + defineAccessor( + runtime, + symbolPrototype, + Predefined::getSymbolID(Predefined::description), + ctx, + symbolPrototypeDescriptionGetter, + nullptr, + false, + true); defineMethod( runtime, symbolPrototype, @@ -223,6 +232,27 @@ symbolKeyFor(void *, Runtime *runtime, NativeArgs args) { return HermesValue::encodeUndefinedValue(); } +/// ES10.0 19.4.3.2 get Symbol.prototype.description +/// TODO(T79770380): make the Symbol(undefined) case spec-conformant. +CallResult +symbolPrototypeDescriptionGetter(void *, Runtime *runtime, NativeArgs args) { + MutableHandle sym{runtime}; + // 1. Let s be the this value. + // 2. Let sym be ? thisSymbolValue(s). + if (args.getThisArg().isSymbol()) { + sym = args.vmcastThis().get(); + } else if (auto symHandle = args.dyncastThis()) { + sym = JSSymbol::getPrimitiveSymbol(*symHandle, runtime).get(); + } else { + return runtime->raiseTypeError( + "Symbol.prototype.description can only be called on Symbol"); + } + + // 3. Return sym.[[Description]]. + StringPrimitive *desc = runtime->getStringPrimFromSymbolID(*sym); + return HermesValue::encodeStringValue(desc); +} + CallResult symbolPrototypeToString(void *, Runtime *runtime, NativeArgs args) { MutableHandle sym{runtime}; diff --git a/test/hermes/symbol.js b/test/hermes/symbol.js index a33ced185c3..86c4110a395 100644 --- a/test/hermes/symbol.js +++ b/test/hermes/symbol.js @@ -21,6 +21,26 @@ gc(); print(String(sym)); // CHECK-NEXT: Symbol(44912) +print('description'); +// CHECK-LABEL: description +print(Symbol().description); +// CHECK-EMPTY: +print(Symbol('asdf').description); +// CHECK-NEXT: asdf +print(Symbol({toString: function() {return 'asdf'}}).description) +// CHECK-NEXT: asdf +print(Object(Symbol('asdf')).description) +// CHECK-NEXT: asdf +try { print(Symbol.prototype.description.call(3)); } +catch (e) { print('caught', e.name); } +// CHECK-NEXT: caught TypeError +print(Symbol.iterator.description) // well-known Symbols +// CHECK-NEXT: Symbol.iterator + +// TODO(The below cases need to return `undefined` to be fully conformant.) +// print(Symbol().description) +// print(Symbol(undefined).description) + print('toString'); // CHECK-LABEL: toString print(Symbol().toString());