[ESNext][BigInt] We don't support BigInt literal as PropertyName
authorticaiolima@gmail.com <ticaiolima@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 13 Feb 2020 21:59:12 +0000 (21:59 +0000)
committerticaiolima@gmail.com <ticaiolima@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 13 Feb 2020 21:59:12 +0000 (21:59 +0000)
https://bugs.webkit.org/show_bug.cgi?id=206888

Reviewed by Ross Kirsling.

JSTests:

* stress/big-int-as-property-name.js: Added.

Source/JavaScriptCore:

According to spec (https://tc39.es/ecma262/#prod-PropertyName),
BigInt literals are valid property names. Given that, we should not
throw a SyntaxError when using BigInt literals on destructuring
pattern, method declaration, object literals, etc.
This patch is adding BigInt literal as a valid syntax to PropertyName.

* parser/Parser.cpp:
(JSC::Parser<LexerType>::parseDestructuringPattern):
(JSC::Parser<LexerType>::parseClass):
(JSC::Parser<LexerType>::parseInstanceFieldInitializerSourceElements):
(JSC::Parser<LexerType>::parseProperty):
(JSC::Parser<LexerType>::parseGetterSetter):
* parser/ParserArena.cpp:
(JSC::IdentifierArena::makeBigIntDecimalIdentifier):
* parser/ParserArena.h:

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

JSTests/ChangeLog
JSTests/stress/big-int-as-property-name.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/parser/Parser.cpp
Source/JavaScriptCore/parser/ParserArena.cpp
Source/JavaScriptCore/parser/ParserArena.h

index f9e11bd..72f141d 100644 (file)
@@ -1,3 +1,12 @@
+2020-02-13  Caio Lima  <ticaiolima@gmail.com>
+
+        [ESNext][BigInt] We don't support BigInt literal as PropertyName
+        https://bugs.webkit.org/show_bug.cgi?id=206888
+
+        Reviewed by Ross Kirsling.
+
+        * stress/big-int-as-property-name.js: Added.
+
 2020-02-10  Mark Lam  <mark.lam@apple.com>
 
         Placate exception check validator in GenericArguments<Type>::put().
diff --git a/JSTests/stress/big-int-as-property-name.js b/JSTests/stress/big-int-as-property-name.js
new file mode 100644 (file)
index 0000000..01e4caf
--- /dev/null
@@ -0,0 +1,94 @@
+//@ runDefault("--useBigInt=true", "--useClassFields=true")
+
+let assert = {
+    sameValue(a, e) {
+        if (a !== e)
+            throw new Error("Expected: " + e + " but got: " + a);
+    },
+    sameArray(a, e) {
+        if (a.length != e.length)
+            throw new Error("Got array: " + a + " but expected: " + e);
+
+        for (let i = 0; i < a.length; i++) {
+            if (a[i] !== e[i])
+                throw new Error("Got array: " + a + " but expected: " + e);
+        }
+    }
+};
+
+// Object Literal
+
+let o = {
+    30n: "30",
+    0o1n: "1",
+    0b10n: "2",
+    0xan: "10",
+    1_2_3n: "1_2_3"
+};
+assert.sameValue(o["1"], "1");
+assert.sameValue(o["2"], "2");
+assert.sameValue(o["10"], "10");
+assert.sameValue(o["123"], "1_2_3");
+
+// MethodDeclaration
+
+o = {
+  0b1n() {},
+  *0o2n() {},
+  async 0x3n() {},
+  async* 4n() {},
+  get 5n() {},
+  set 6n(x) {},
+  7n: function () {}
+};
+
+assert.sameArray(Object.getOwnPropertyNames(o), ["1", "2", "3", "4", "5", "6", "7"]);
+
+assert.sameValue(o[1].name, "1");
+assert.sameValue(o[2].name, "2");
+assert.sameValue(o[3].name, "3");
+assert.sameValue(o[4].name, "4");
+assert.sameValue(Object.getOwnPropertyDescriptor(o, 5).get.name, "get 5");
+assert.sameValue(Object.getOwnPropertyDescriptor(o, 6).set.name, "set 6");
+assert.sameValue(o[7].name, "7");
+
+{
+    class C {
+        0b1n() {};
+        *0o2n() {};
+        async 0x3n() {};
+        async* 4n() {};
+        get 5n() {};
+        set 6n(x) {};
+    }
+
+    let c = C.prototype;
+    assert.sameArray(Object.getOwnPropertyNames(c), ["1", "2", "3", "4", "5", "6", "constructor"]);
+    assert.sameValue(c[1].name, "1");
+    assert.sameValue(c[2].name, "2");
+    assert.sameValue(c[3].name, "3");
+    assert.sameValue(c[4].name, "4");
+    assert.sameValue(Object.getOwnPropertyDescriptor(c, 5).get.name, "get 5");
+    assert.sameValue(Object.getOwnPropertyDescriptor(c, 6).set.name, "set 6");
+}
+
+// Field declaration
+{
+    class C {
+        0o1n = "baz";
+        0b10n = function () {};
+    }
+
+    let c = new C();
+    assert.sameValue(c[1n], "baz");
+    assert.sameValue(c[1], "baz");
+    assert.sameValue(c["1"], "baz");
+
+    assert.sameValue(c[2].name, "2");
+}
+
+// Destructuring
+
+let {1n: a} = {1n: "foo"};
+assert.sameValue(a, "foo");
+
index 9c2f4ca..9bc7e84 100644 (file)
@@ -1,3 +1,26 @@
+2020-02-13  Caio Lima  <ticaiolima@gmail.com>
+
+        [ESNext][BigInt] We don't support BigInt literal as PropertyName
+        https://bugs.webkit.org/show_bug.cgi?id=206888
+
+        Reviewed by Ross Kirsling.
+
+        According to spec (https://tc39.es/ecma262/#prod-PropertyName),
+        BigInt literals are valid property names. Given that, we should not
+        throw a SyntaxError when using BigInt literals on destructuring
+        pattern, method declaration, object literals, etc.
+        This patch is adding BigInt literal as a valid syntax to PropertyName.
+
+        * parser/Parser.cpp:
+        (JSC::Parser<LexerType>::parseDestructuringPattern):
+        (JSC::Parser<LexerType>::parseClass):
+        (JSC::Parser<LexerType>::parseInstanceFieldInitializerSourceElements):
+        (JSC::Parser<LexerType>::parseProperty):
+        (JSC::Parser<LexerType>::parseGetterSetter):
+        * parser/ParserArena.cpp:
+        (JSC::IdentifierArena::makeBigIntDecimalIdentifier):
+        * parser/ParserArena.h:
+
 2020-02-12  Mark Lam  <mark.lam@apple.com>
 
         Add options for debugging WASM code.
index c2fa607..2a191c8 100644 (file)
@@ -1222,6 +1222,9 @@ template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::parseDe
                     propertyName = m_token.m_data.ident;
                     wasString = true;
                     break;
+                case BIGINT:
+                    propertyName = &m_parserArena.identifierArena().makeBigIntDecimalIdentifier(const_cast<VM&>(m_vm), *m_token.m_data.bigIntString, m_token.m_data.radix);
+                    break;
                 case OPENBRACKET:
                     next();
                     propertyExpression = parseAssignmentExpression(context);
@@ -2892,6 +2895,11 @@ parseMethod:
             ASSERT(ident);
             next();
             break;
+        case BIGINT:
+            ident = &m_parserArena.identifierArena().makeBigIntDecimalIdentifier(const_cast<VM&>(m_vm), *m_token.m_data.bigIntString, m_token.m_data.radix);
+            ASSERT(ident);
+            next();
+            break;
         case IDENT:
             if (UNLIKELY(*m_token.m_data.ident == m_vm.propertyNames->async && !m_token.m_data.escaped)) {
                 if (!isGeneratorMethodParseMode(parseMode) && !isAsyncMethodParseMode(parseMode)) {
@@ -2912,7 +2920,7 @@ parseMethod:
             ident = m_token.m_data.ident;
             ASSERT(ident);
             next();
-            if (parseMode == SourceParseMode::MethodMode && (matchIdentifierOrKeyword() || match(STRING) || match(DOUBLE) || match(INTEGER) || match(OPENBRACKET))) {
+            if (parseMode == SourceParseMode::MethodMode && (matchIdentifierOrKeyword() || match(STRING) || match(DOUBLE) || match(INTEGER) || match(BIGINT) || match(OPENBRACKET))) {
                 isGetter = *ident == propertyNames.get;
                 isSetter = *ident == propertyNames.set;
             }
@@ -3040,6 +3048,11 @@ template <class TreeBuilder> TreeSourceElements Parser<LexerType>::parseInstance
             ASSERT(ident);
             next();
             break;
+        case BIGINT:
+            ident = &m_parserArena.identifierArena().makeBigIntDecimalIdentifier(const_cast<VM&>(m_vm), *m_token.m_data.bigIntString, m_token.m_data.radix);
+            ASSERT(ident);
+            next();
+            break;
         case DOUBLE:
         case INTEGER:
             ident = &m_parserArena.identifierArena().makeNumericIdentifier(const_cast<VM&>(m_vm), m_token.m_data.doubleValue);
@@ -4146,6 +4159,23 @@ namedProperty:
         context.setEndOffset(node, m_lexer->currentOffset());
         return context.createProperty(const_cast<VM&>(m_vm), m_parserArena, propertyName, node, PropertyNode::Constant, PropertyNode::Unknown, complete, SuperBinding::NotNeeded, ClassElementTag::No);
     }
+    case BIGINT: {
+        const Identifier* ident = &m_parserArena.identifierArena().makeBigIntDecimalIdentifier(const_cast<VM&>(m_vm), *m_token.m_data.bigIntString, m_token.m_data.radix);
+        next();
+
+        if (match(OPENPAREN)) {
+            auto method = parsePropertyMethod(context, ident, parseMode);
+            propagateError();
+            return context.createProperty(ident, method, PropertyNode::Constant, PropertyNode::Unknown, complete, SuperBinding::Needed, InferName::Allowed, ClassElementTag::No);
+        }
+        failIfTrue(parseMode != SourceParseMode::MethodMode, "Expected a parenthesis for argument list");
+
+        consumeOrFail(COLON, "Expected ':' after property name");
+        TreeExpression node = parseAssignmentExpression(context);
+        failIfFalse(node, "Cannot parse expression for property declaration");
+        context.setEndOffset(node, m_lexer->currentOffset());
+        return context.createProperty(ident, node, PropertyNode::Constant, PropertyNode::Unknown, complete, SuperBinding::NotNeeded, InferName::Allowed, ClassElementTag::No);
+    }
     case OPENBRACKET: {
         next();
         auto propertyName = parseAssignmentExpression(context);
@@ -4214,6 +4244,9 @@ template <class TreeBuilder> TreeProperty Parser<LexerType>::parseGetterSetter(T
     } else if (match(DOUBLE) || match(INTEGER)) {
         numericPropertyName = m_token.m_data.doubleValue;
         next();
+    } else if (match(BIGINT)) {
+        stringPropertyName = &m_parserArena.identifierArena().makeBigIntDecimalIdentifier(const_cast<VM&>(m_vm), *m_token.m_data.bigIntString, m_token.m_data.radix);
+        next();
     } else if (match(OPENBRACKET)) {
         next();
         computedPropertyName = parseAssignmentExpression(context);
index 26be041..84fe399 100644 (file)
@@ -26,6 +26,8 @@
 #include "config.h"
 #include "ParserArena.h"
 
+#include "CatchScope.h"
+#include "JSBigInt.h"
 #include "Nodes.h"
 #include "JSCInlines.h"
 
@@ -76,4 +78,24 @@ void ParserArena::allocateFreeablePool()
     ASSERT(freeablePool() == pool);
 }
 
+const Identifier& IdentifierArena::makeBigIntDecimalIdentifier(VM& vm, const Identifier& identifier, uint8_t radix)
+{
+    if (radix == 10)
+        return identifier;
+
+    // FIXME: We are allocating a JSBigInt just to be able to use
+    // JSBigInt::tryGetString when radix is not 10.
+    // This creates some GC pressure, but since these identifiers
+    // will only be created when BigInt literal is used as a property name,
+    // it wont be much problematic, given such cases are very rare.
+    // There is a lot of optimizations we can apply here when necessary.
+    // https://bugs.webkit.org/show_bug.cgi?id=207627
+
+    auto scope = DECLARE_CATCH_SCOPE(vm);
+    JSBigInt* bigInt = JSBigInt::parseInt(nullptr, vm, identifier.string(), radix, JSBigInt::ErrorParseMode::ThrowExceptions, JSBigInt::ParseIntSign::Unsigned);
+    scope.assertNoException();
+    m_identifiers.append(Identifier::fromString(vm, JSBigInt::tryGetString(vm, bigInt, 10)));
+    return m_identifiers.last();
+}
+
 }
index 36dbbd4..4d9c19d 100644 (file)
@@ -50,6 +50,7 @@ namespace JSC {
         ALWAYS_INLINE const Identifier& makeIdentifierLCharFromUChar(VM&, const UChar* characters, size_t length);
         ALWAYS_INLINE const Identifier& makeIdentifier(VM&, SymbolImpl*);
 
+        const Identifier& makeBigIntDecimalIdentifier(VM&, const Identifier&, uint8_t radix);
         const Identifier& makeNumericIdentifier(VM&, double number);
 
     public: