[ES6] Modules' `export default function/class` should be declaration
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 23 Aug 2016 18:14:30 +0000 (18:14 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 23 Aug 2016 18:14:30 +0000 (18:14 +0000)
https://bugs.webkit.org/show_bug.cgi?id=160499

Reviewed by Saam Barati.

JSTests:

Add several module tests. And flip the failed tests flags in test262.

* modules/export-default-function-name-in-assignment-expression.js: Added.
(export.default):
* modules/export-default-function-name-in-class-declaration.js: Added.
* modules/export-default-function-name-in-function-declaration.js: Added.
(export.default):
* modules/export-default-function-name-in-generator-declaration.js: Added.
(export.default):
* stress/method-name.js: Added.
(testSyntax):
(testSyntaxError):
(testSyntaxError.Hello.prototype.hello.hello):
(testSyntaxError.Hello):
(SyntaxError.Unexpected.identifier.string_appeared_here.Expected.an.opening.string_appeared_here.before.a.method.testSyntaxError.let.obj.hello.hello):
(testSyntaxError.Hello.prototype.get hello):
(testSyntaxError.Hello.prototype.set hello):
* test262.yaml:

Source/JavaScriptCore:

Previously, we parsed the following cases as FunctionExpression and ClassExpression.

    ```
    export default function () { }
    export default class { }
    ```

But, as per ES6 spec, the above `function ...` and `class ...` parts should be parsed
as function declaration and class declaration. This has big difference; the instantiation
of the function declarations are done in the function prologue.

In this patch, we correctly parse the above cases as declaration. To handle no-named
declarations, we add a new flag, DeclarationDefaultContext. This indicates [Default]
flag in the ES6 spec's BNF.

Furthermore, this patch also fixes the following name related bugs.

1. The bug related to "export default"'s function name. If the name is not provided (like the above case), the name of the function becomes
"default", not "*default*". This is special handling in ES6 spec. We handle this in JSFunction's reifyName.

2. `class Hello { hello hello() { } }` is accepted. We introduced FunctionRequirements::Unnamed and fix this bug.

* parser/ModuleScopeData.h:
(JSC::ModuleScopeData::exportBinding):
Exported names are already guranteed uniqueness by m_exportedNames. Not necessary to use set here. Use vector instead.

* parser/Parser.cpp:
(JSC::Parser<LexerType>::parseFunctionInfo):
If we pass FunctionRequirements::NoRequirements, we need to initialize functionInfo.name / classInfo.className
with the default fallback name. For example, in the above `export default` case, we initialize it with `*default*`.

(JSC::Parser<LexerType>::parseFunctionDeclaration):
(JSC::Parser<LexerType>::parseClassDeclaration):
(JSC::Parser<LexerType>::parseClass):
(JSC::Parser<LexerType>::parseExportDeclaration):
(JSC::Parser<LexerType>::parsePropertyMethod):
(JSC::Parser<LexerType>::parseGetterSetter):
(JSC::Parser<LexerType>::parseClassExpression):
(JSC::Parser<LexerType>::parseFunctionExpression):
(JSC::Parser<LexerType>::parsePrimaryExpression):
(JSC::Parser<LexerType>::parseArrowFunctionExpression):
* parser/Parser.h:
* runtime/JSFunction.cpp:
(JSC::JSFunction::reifyName):

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

16 files changed:
JSTests/ChangeLog
JSTests/modules/export-default-function-name-in-assignment-expression.js [new file with mode: 0644]
JSTests/modules/export-default-function-name-in-class-declaration.js [new file with mode: 0644]
JSTests/modules/export-default-function-name-in-function-declaration.js [new file with mode: 0644]
JSTests/modules/export-default-function-name-in-generator-declaration.js [new file with mode: 0644]
JSTests/stress/method-name.js [new file with mode: 0644]
JSTests/test262.yaml
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/inspector/JSInjectedScriptHost.cpp
Source/JavaScriptCore/parser/ModuleScopeData.h
Source/JavaScriptCore/parser/Parser.cpp
Source/JavaScriptCore/parser/Parser.h
Source/JavaScriptCore/runtime/FunctionPrototype.cpp
Source/JavaScriptCore/runtime/JSFunction.cpp
Source/JavaScriptCore/runtime/JSFunction.h
Source/JavaScriptCore/runtime/LazyClassStructure.cpp

index 0fb5251..1c46159 100644 (file)
@@ -1,5 +1,31 @@
 2016-08-22  Yusuke Suzuki  <utatane.tea@gmail.com>
 
+        [ES6] Modules' `export default function/class` should be declaration
+        https://bugs.webkit.org/show_bug.cgi?id=160499
+
+        Reviewed by Saam Barati.
+
+        Add several module tests. And flip the failed tests flags in test262.
+
+        * modules/export-default-function-name-in-assignment-expression.js: Added.
+        (export.default):
+        * modules/export-default-function-name-in-class-declaration.js: Added.
+        * modules/export-default-function-name-in-function-declaration.js: Added.
+        (export.default):
+        * modules/export-default-function-name-in-generator-declaration.js: Added.
+        (export.default):
+        * stress/method-name.js: Added.
+        (testSyntax):
+        (testSyntaxError):
+        (testSyntaxError.Hello.prototype.hello.hello):
+        (testSyntaxError.Hello):
+        (SyntaxError.Unexpected.identifier.string_appeared_here.Expected.an.opening.string_appeared_here.before.a.method.testSyntaxError.let.obj.hello.hello):
+        (testSyntaxError.Hello.prototype.get hello):
+        (testSyntaxError.Hello.prototype.set hello):
+        * test262.yaml:
+
+2016-08-22  Yusuke Suzuki  <utatane.tea@gmail.com>
+
         [ES6] Module should not allow HTML comments
         https://bugs.webkit.org/show_bug.cgi?id=161041
 
diff --git a/JSTests/modules/export-default-function-name-in-assignment-expression.js b/JSTests/modules/export-default-function-name-in-assignment-expression.js
new file mode 100644 (file)
index 0000000..aac0729
--- /dev/null
@@ -0,0 +1,8 @@
+import func from "./export-default-function-name-in-assignment-expression.js"
+import { shouldBe } from "./resources/assert.js";
+
+export default (function () { });
+
+// https://tc39.github.io/ecma262/#sec-exports-runtime-semantics-evaluation
+shouldBe(func.name, 'default');
+shouldBe(func.toString(), `function () { }`);
diff --git a/JSTests/modules/export-default-function-name-in-class-declaration.js b/JSTests/modules/export-default-function-name-in-class-declaration.js
new file mode 100644 (file)
index 0000000..1a882e2
--- /dev/null
@@ -0,0 +1,8 @@
+import cls from "./export-default-function-name-in-class-declaration.js"
+import { shouldBe } from "./resources/assert.js";
+
+export default class { }
+
+// https://tc39.github.io/ecma262/#sec-exports-runtime-semantics-evaluation
+shouldBe(cls.name, 'default');
+shouldBe(cls.toString(), `class { }`);
diff --git a/JSTests/modules/export-default-function-name-in-function-declaration.js b/JSTests/modules/export-default-function-name-in-function-declaration.js
new file mode 100644 (file)
index 0000000..c990adb
--- /dev/null
@@ -0,0 +1,8 @@
+import func from "./export-default-function-name-in-function-declaration.js"
+import { shouldBe } from "./resources/assert.js";
+
+export default function () { }
+
+// https://tc39.github.io/ecma262/#sec-exports-runtime-semantics-evaluation
+shouldBe(func.name, 'default');
+shouldBe(func.toString(), `function () { }`);
diff --git a/JSTests/modules/export-default-function-name-in-generator-declaration.js b/JSTests/modules/export-default-function-name-in-generator-declaration.js
new file mode 100644 (file)
index 0000000..12891db
--- /dev/null
@@ -0,0 +1,8 @@
+import func from "./export-default-function-name-in-generator-declaration.js"
+import { shouldBe } from "./resources/assert.js";
+
+export default function * () { }
+
+// https://tc39.github.io/ecma262/#sec-exports-runtime-semantics-evaluation
+shouldBe(func.name, 'default');
+// shouldBe(func.toString(), `function * () { }`);
diff --git a/JSTests/stress/method-name.js b/JSTests/stress/method-name.js
new file mode 100644 (file)
index 0000000..45ee5ba
--- /dev/null
@@ -0,0 +1,46 @@
+function testSyntax(script) {
+    try {
+        eval(script);
+    } catch (error) {
+        if (error instanceof SyntaxError)
+            throw new Error("Bad error: " + String(error));
+    }
+}
+
+function testSyntaxError(script, message) {
+    var error = null;
+    try {
+        eval(script);
+    } catch (e) {
+        error = e;
+    }
+    if (!error)
+        throw new Error("Expected syntax error not thrown");
+
+    if (String(error) !== message)
+        throw new Error("Bad error: " + String(error));
+}
+
+testSyntaxError(`
+class Hello {
+    hello hello() { }
+}
+`, `SyntaxError: Unexpected identifier 'hello'. Expected an opening '(' before a method's parameter list.`);
+
+testSyntaxError(`
+let obj = {
+    hello hello() { }
+}
+`, `SyntaxError: Unexpected identifier 'hello'. Expected a ':' following the property name 'hello'.`);
+
+testSyntaxError(`
+class Hello {
+    get hello hello() { }
+}
+`, `SyntaxError: Unexpected identifier 'hello'. Expected a parameter list for getter definition.`);
+
+testSyntaxError(`
+class Hello {
+    set hello hello(v) { }
+}
+`, `SyntaxError: Unexpected identifier 'hello'. Expected a parameter list for setter definition.`);
index 7602215..d88c829 100644 (file)
 - path: test262/test/language/module-code/eval-export-dflt-cls-anon-semi.js
   cmd: runTest262 :normal, "NoException", ["../../../harness/assert.js", "../../../harness/sta.js"], [:module]
 - path: test262/test/language/module-code/eval-export-dflt-cls-anon.js
-  cmd: runTest262 :fail, "NoException", ["../../../harness/assert.js", "../../../harness/sta.js"], [:module]
+  cmd: runTest262 :normal, "NoException", ["../../../harness/assert.js", "../../../harness/sta.js"], [:module]
 - path: test262/test/language/module-code/eval-export-dflt-cls-name-meth.js
   cmd: runTest262 :normal, "NoException", ["../../../harness/assert.js", "../../../harness/sta.js"], [:module]
 - path: test262/test/language/module-code/eval-export-dflt-cls-named-semi.js
 - path: test262/test/language/module-code/eval-export-dflt-cls-named.js
   cmd: runTest262 :normal, "NoException", ["../../../harness/assert.js", "../../../harness/sta.js"], [:module]
 - path: test262/test/language/module-code/eval-export-dflt-expr-cls-anon.js
-  cmd: runTest262 :fail, "NoException", ["../../../harness/assert.js", "../../../harness/sta.js"], [:module]
+  cmd: runTest262 :normal, "NoException", ["../../../harness/assert.js", "../../../harness/sta.js"], [:module]
 - path: test262/test/language/module-code/eval-export-dflt-expr-cls-name-meth.js
   cmd: runTest262 :normal, "NoException", ["../../../harness/assert.js", "../../../harness/sta.js"], [:module]
 - path: test262/test/language/module-code/eval-export-dflt-expr-cls-named.js
 - path: test262/test/language/module-code/eval-export-dflt-expr-err-get-value.js
   cmd: runTest262 :normal, "ReferenceError", ["../../../harness/assert.js", "../../../harness/sta.js"], [:module]
 - path: test262/test/language/module-code/eval-export-dflt-expr-fn-anon.js
-  cmd: runTest262 :fail, "NoException", ["../../../harness/assert.js", "../../../harness/sta.js"], [:module]
+  cmd: runTest262 :normal, "NoException", ["../../../harness/assert.js", "../../../harness/sta.js"], [:module]
 - path: test262/test/language/module-code/eval-export-dflt-expr-fn-named.js
   cmd: runTest262 :normal, "NoException", ["../../../harness/assert.js", "../../../harness/sta.js"], [:module]
 - path: test262/test/language/module-code/eval-export-dflt-expr-gen-anon.js
-  cmd: runTest262 :fail, "NoException", ["../../../harness/assert.js", "../../../harness/sta.js"], [:module]
+  cmd: runTest262 :normal, "NoException", ["../../../harness/assert.js", "../../../harness/sta.js"], [:module]
 - path: test262/test/language/module-code/eval-export-dflt-expr-gen-named.js
   cmd: runTest262 :normal, "NoException", ["../../../harness/assert.js", "../../../harness/sta.js"], [:module]
 - path: test262/test/language/module-code/eval-export-dflt-expr-in.js
 - path: test262/test/language/module-code/instn-named-bndng-dflt-expr.js
   cmd: runTest262 :normal, "NoException", ["../../../harness/assert.js", "../../../harness/sta.js"], [:module]
 - path: test262/test/language/module-code/instn-named-bndng-dflt-fun-anon.js
-  cmd: runTest262 :fail, "NoException", ["../../../harness/assert.js", "../../../harness/sta.js"], [:module]
+  cmd: runTest262 :normal, "NoException", ["../../../harness/assert.js", "../../../harness/sta.js"], [:module]
 - path: test262/test/language/module-code/instn-named-bndng-dflt-fun-named.js
   cmd: runTest262 :normal, "NoException", ["../../../harness/assert.js", "../../../harness/sta.js"], [:module]
 - path: test262/test/language/module-code/instn-named-bndng-dflt-gen-anon.js
-  cmd: runTest262 :fail, "NoException", ["../../../harness/assert.js", "../../../harness/sta.js"], [:module]
+  cmd: runTest262 :normal, "NoException", ["../../../harness/assert.js", "../../../harness/sta.js"], [:module]
 - path: test262/test/language/module-code/instn-named-bndng-dflt-gen-named.js
   cmd: runTest262 :normal, "NoException", ["../../../harness/assert.js", "../../../harness/sta.js"], [:module]
 - path: test262/test/language/module-code/instn-named-bndng-dflt-named.js
 - path: test262/test/language/module-code/parse-err-hoist-lex-gen.js
   cmd: runTest262 :fail, "SyntaxError", ["../../../harness/assert.js", "../../../harness/sta.js"], [:module]
 - path: test262/test/language/module-code/parse-err-invoke-anon-fun-decl.js
-  cmd: runTest262 :fail, "SyntaxError", ["../../../harness/assert.js", "../../../harness/sta.js"], [:module]
+  cmd: runTest262 :normal, "SyntaxError", ["../../../harness/assert.js", "../../../harness/sta.js"], [:module]
 - path: test262/test/language/module-code/parse-err-invoke-anon-gen-decl.js
-  cmd: runTest262 :fail, "SyntaxError", ["../../../harness/assert.js", "../../../harness/sta.js"], [:module]
+  cmd: runTest262 :normal, "SyntaxError", ["../../../harness/assert.js", "../../../harness/sta.js"], [:module]
 - path: test262/test/language/module-code/parse-err-reference.js
   cmd: runTest262 :normal, "ReferenceError", ["../../../harness/assert.js", "../../../harness/sta.js"], [:module]
 - path: test262/test/language/module-code/parse-err-semi-dflt-expr.js
index c6dd0e2..b0f492d 100644 (file)
@@ -1,3 +1,55 @@
+2016-08-22  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [ES6] Modules' `export default function/class` should be declaration
+        https://bugs.webkit.org/show_bug.cgi?id=160499
+
+        Reviewed by Saam Barati.
+
+        Previously, we parsed the following cases as FunctionExpression and ClassExpression.
+
+            ```
+            export default function () { }
+            export default class { }
+            ```
+
+        But, as per ES6 spec, the above `function ...` and `class ...` parts should be parsed
+        as function declaration and class declaration. This has big difference; the instantiation
+        of the function declarations are done in the function prologue.
+
+        In this patch, we correctly parse the above cases as declaration. To handle no-named
+        declarations, we add a new flag, DeclarationDefaultContext. This indicates [Default]
+        flag in the ES6 spec's BNF.
+
+        Furthermore, this patch also fixes the following name related bugs.
+
+        1. The bug related to "export default"'s function name. If the name is not provided (like the above case), the name of the function becomes
+        "default", not "*default*". This is special handling in ES6 spec. We handle this in JSFunction's reifyName.
+
+        2. `class Hello { hello hello() { } }` is accepted. We introduced FunctionRequirements::Unnamed and fix this bug.
+
+        * parser/ModuleScopeData.h:
+        (JSC::ModuleScopeData::exportBinding):
+        Exported names are already guranteed uniqueness by m_exportedNames. Not necessary to use set here. Use vector instead.
+
+        * parser/Parser.cpp:
+        (JSC::Parser<LexerType>::parseFunctionInfo):
+        If we pass FunctionRequirements::NoRequirements, we need to initialize functionInfo.name / classInfo.className
+        with the default fallback name. For example, in the above `export default` case, we initialize it with `*default*`.
+
+        (JSC::Parser<LexerType>::parseFunctionDeclaration):
+        (JSC::Parser<LexerType>::parseClassDeclaration):
+        (JSC::Parser<LexerType>::parseClass):
+        (JSC::Parser<LexerType>::parseExportDeclaration):
+        (JSC::Parser<LexerType>::parsePropertyMethod):
+        (JSC::Parser<LexerType>::parseGetterSetter):
+        (JSC::Parser<LexerType>::parseClassExpression):
+        (JSC::Parser<LexerType>::parseFunctionExpression):
+        (JSC::Parser<LexerType>::parsePrimaryExpression):
+        (JSC::Parser<LexerType>::parseArrowFunctionExpression):
+        * parser/Parser.h:
+        * runtime/JSFunction.cpp:
+        (JSC::JSFunction::reifyName):
+
 2016-08-23  Saam Barati  <sbarati@apple.com>
 
         JIT::updateTopCallframe() in the baseline JIT should use PC instead of PC+1
index ae49cf7..b4418bc 100644 (file)
@@ -228,7 +228,7 @@ JSValue JSInjectedScriptHost::functionDetails(ExecState* exec)
     JSObject* result = constructEmptyObject(exec);
     result->putDirect(vm, Identifier::fromString(exec, "location"), location);
 
-    String name = function->name();
+    String name = function->name(vm);
     if (!name.isEmpty())
         result->putDirect(vm, Identifier::fromString(exec, "name"), jsString(exec, name));
 
index 8221af4..2a1eca2 100644 (file)
@@ -35,7 +35,7 @@ class ModuleScopeData : public RefCounted<ModuleScopeData> {
 WTF_MAKE_NONCOPYABLE(ModuleScopeData);
 WTF_MAKE_FAST_ALLOCATED;
 public:
-    typedef HashMap<RefPtr<UniquedStringImpl>, IdentifierSet, IdentifierRepHash, HashTraits<RefPtr<UniquedStringImpl>>> IdentifierAliasMap;
+    typedef HashMap<RefPtr<UniquedStringImpl>, Vector<RefPtr<UniquedStringImpl>>, IdentifierRepHash, HashTraits<RefPtr<UniquedStringImpl>>> IdentifierAliasMap;
 
     static Ref<ModuleScopeData> create() { return adoptRef(*new ModuleScopeData); }
 
@@ -48,7 +48,7 @@ public:
 
     void exportBinding(const Identifier& localName, const Identifier& exportedName)
     {
-        m_exportedBindings.add(localName.impl(), IdentifierSet()).iterator->value.add(exportedName.impl());
+        m_exportedBindings.add(localName.impl(), Vector<RefPtr<UniquedStringImpl>>()).iterator->value.append(exportedName.impl());
     }
 
     void exportBinding(const Identifier& localName)
index 1245f54..54139a0 100644 (file)
@@ -1930,7 +1930,7 @@ template <class TreeBuilder> typename TreeBuilder::FormalParameterList Parser<Le
 }
 
 template <typename LexerType>
-template <class TreeBuilder> bool Parser<LexerType>::parseFunctionInfo(TreeBuilder& context, FunctionRequirements requirements, SourceParseMode mode, bool nameIsInContainingScope, ConstructorKind constructorKind, SuperBinding expectedSuperBinding, int functionKeywordStart, ParserFunctionInfo<TreeBuilder>& functionInfo, FunctionDefinitionType functionDefinitionType)
+template <class TreeBuilder> bool Parser<LexerType>::parseFunctionInfo(TreeBuilder& context, FunctionNameRequirements requirements, SourceParseMode mode, bool nameIsInContainingScope, ConstructorKind constructorKind, SuperBinding expectedSuperBinding, int functionKeywordStart, ParserFunctionInfo<TreeBuilder>& functionInfo, FunctionDefinitionType functionDefinitionType)
 {
     RELEASE_ASSERT(isFunctionParseMode(mode));
 
@@ -2072,18 +2072,22 @@ template <class TreeBuilder> bool Parser<LexerType>::parseFunctionInfo(TreeBuild
         if (functionDefinitionType == FunctionDefinitionType::Expression && mode == SourceParseMode::NormalFunctionMode)
             upperScopeIsGenerator = false;
 
-        if (matchSpecIdentifier(upperScopeIsGenerator)) {
-            functionInfo.name = m_token.m_data.ident;
-            m_parserState.lastFunctionName = functionInfo.name;
-            next();
-            if (!nameIsInContainingScope)
-                failIfTrueIfStrict(functionScope->declareCallee(functionInfo.name) & DeclarationResult::InvalidStrictMode, "'", functionInfo.name->impl(), "' is not a valid ", stringForFunctionMode(mode), " name in strict mode");
-        } else if (requirements == FunctionNeedsName) {
-            if (match(OPENPAREN) && mode == SourceParseMode::NormalFunctionMode)
-                semanticFail("Function statements must have a name");
-            semanticFailureDueToKeyword(stringForFunctionMode(mode), " name");
-            failDueToUnexpectedToken();
-            return false;
+        if (requirements != FunctionNameRequirements::Unnamed) {
+            ASSERT_WITH_MESSAGE(!(requirements == FunctionNameRequirements::None && !functionInfo.name), "When specifying FunctionNameRequirements::None, we need to initialize functionInfo.name with the default value in the caller side.");
+            if (matchSpecIdentifier(upperScopeIsGenerator)) {
+                functionInfo.name = m_token.m_data.ident;
+                m_parserState.lastFunctionName = functionInfo.name;
+                next();
+                if (!nameIsInContainingScope)
+                    failIfTrueIfStrict(functionScope->declareCallee(functionInfo.name) & DeclarationResult::InvalidStrictMode, "'", functionInfo.name->impl(), "' is not a valid ", stringForFunctionMode(mode), " name in strict mode");
+            } else if (requirements == FunctionNameRequirements::Named) {
+                if (match(OPENPAREN) && mode == SourceParseMode::NormalFunctionMode)
+                    semanticFail("Function statements must have a name");
+                semanticFailureDueToKeyword(stringForFunctionMode(mode), " name");
+                failDueToUnexpectedToken();
+                return false;
+            }
+            ASSERT(functionInfo.name);
         }
 
         startLocation = tokenLocation();
@@ -2163,7 +2167,8 @@ template <class TreeBuilder> bool Parser<LexerType>::parseFunctionInfo(TreeBuild
     restoreParserState(oldState);
     failIfFalse(functionInfo.body, "Cannot parse the body of this ", stringForFunctionMode(mode));
     context.setEndOffset(functionInfo.body, m_lexer->currentOffset());
-    if (functionScope->strictMode() && functionInfo.name) {
+    if (functionScope->strictMode() && requirements != FunctionNameRequirements::Unnamed) {
+        ASSERT(functionInfo.name);
         RELEASE_ASSERT(mode == SourceParseMode::NormalFunctionMode || mode == SourceParseMode::MethodMode || mode == SourceParseMode::ArrowFunctionMode || mode == SourceParseMode::GeneratorBodyMode || mode == SourceParseMode::GeneratorWrapperFunctionMode);
         semanticFailIfTrue(m_vm->propertyNames->arguments == *functionInfo.name, "'", functionInfo.name->impl(), "' is not a valid function name in strict mode");
         semanticFailIfTrue(m_vm->propertyNames->eval == *functionInfo.name, "'", functionInfo.name->impl(), "' is not a valid function name in strict mode");
@@ -2221,18 +2226,45 @@ static NO_RETURN_DUE_TO_CRASH FunctionMetadataNode* getMetadata(ParserFunctionIn
 static FunctionMetadataNode* getMetadata(ParserFunctionInfo<ASTBuilder>& info) { return info.body; }
 
 template <typename LexerType>
-template <class TreeBuilder> TreeStatement Parser<LexerType>::parseFunctionDeclaration(TreeBuilder& context, ExportType exportType)
+template <class TreeBuilder> TreeStatement Parser<LexerType>::parseFunctionDeclaration(TreeBuilder& context, ExportType exportType, DeclarationDefaultContext declarationDefaultContext)
 {
     ASSERT(match(FUNCTION));
     JSTokenLocation location(tokenLocation());
     unsigned functionKeywordStart = tokenStart();
     next();
-    ParserFunctionInfo<TreeBuilder> functionInfo;
     SourceParseMode parseMode = SourceParseMode::NormalFunctionMode;
     if (consume(TIMES))
         parseMode = SourceParseMode::GeneratorWrapperFunctionMode;
-    failIfFalse((parseFunctionInfo(context, FunctionNeedsName, parseMode, true, ConstructorKind::None, SuperBinding::NotNeeded, functionKeywordStart, functionInfo, FunctionDefinitionType::Declaration)), "Cannot parse this function");
-    failIfFalse(functionInfo.name, "Function statements must have a name");
+
+    ParserFunctionInfo<TreeBuilder> functionInfo;
+    FunctionNameRequirements requirements = FunctionNameRequirements::Named;
+    if (declarationDefaultContext == DeclarationDefaultContext::ExportDefault) {
+        // Under the "export default" context, function declaration does not require the function name.
+        //
+        //     ExportDeclaration:
+        //         ...
+        //         export default HoistableDeclaration[~Yield, +Default]
+        //         ...
+        //
+        //     HoistableDeclaration[Yield, Default]:
+        //         FunctionDeclaration[?Yield, ?Default]
+        //         GeneratorDeclaration[?Yield, ?Default]
+        //
+        //     FunctionDeclaration[Yield, Default]:
+        //         ...
+        //         [+Default] function ( FormalParameters[~Yield] ) { FunctionBody[~Yield] }
+        //
+        //     GeneratorDeclaration[Yield, Default]:
+        //         ...
+        //         [+Default] function * ( FormalParameters[+Yield] ) { GeneratorBody }
+        //
+        // In this case, we use "*default*" as this function declaration's name.
+        requirements = FunctionNameRequirements::None;
+        functionInfo.name = &m_vm->propertyNames->builtinNames().starDefaultPrivateName();
+    }
+
+    failIfFalse((parseFunctionInfo(context, requirements, parseMode, true, ConstructorKind::None, SuperBinding::NotNeeded, functionKeywordStart, functionInfo, FunctionDefinitionType::Declaration)), "Cannot parse this function");
+    ASSERT(functionInfo.name);
 
     std::pair<DeclarationResultMask, ScopeRef> functionDeclaration = declareFunction(functionInfo.name);
     DeclarationResultMask declarationResult = functionDeclaration.first;
@@ -2240,6 +2272,7 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseFunctionDecla
     if (declarationResult & DeclarationResult::InvalidDuplicateDeclaration)
         internalFailWithMessage(false, "Cannot declare a function that shadows a let/const/class/function variable '", functionInfo.name->impl(), "' in strict mode");
     if (exportType == ExportType::Exported) {
+        ASSERT_WITH_MESSAGE(declarationDefaultContext != DeclarationDefaultContext::ExportDefault, "Export default case will export the name and binding in the caller.");
         semanticFailIfFalse(exportName(*functionInfo.name), "Cannot export a duplicate function name: '", functionInfo.name->impl(), "'");
         m_moduleScopeData->exportBinding(*functionInfo.name);
     }
@@ -2251,7 +2284,7 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseFunctionDecla
 }
 
 template <typename LexerType>
-template <class TreeBuilder> TreeStatement Parser<LexerType>::parseClassDeclaration(TreeBuilder& context, ExportType exportType)
+template <class TreeBuilder> TreeStatement Parser<LexerType>::parseClassDeclaration(TreeBuilder& context, ExportType exportType, DeclarationDefaultContext declarationDefaultContext)
 {
     ASSERT(match(CLASSTOKEN));
     JSTokenLocation location(tokenLocation());
@@ -2259,13 +2292,33 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseClassDeclarat
     unsigned classStartLine = tokenLine();
 
     ParserClassInfo<TreeBuilder> info;
-    TreeClassExpression classExpr = parseClass(context, FunctionNeedsName, info);
+    FunctionNameRequirements requirements = FunctionNameRequirements::Named;
+    if (declarationDefaultContext == DeclarationDefaultContext::ExportDefault) {
+        // Under the "export default" context, class declaration does not require the class name.
+        //
+        //     ExportDeclaration:
+        //         ...
+        //         export default ClassDeclaration[~Yield, +Default]
+        //         ...
+        //
+        //     ClassDeclaration[Yield, Default]:
+        //         ...
+        //         [+Default] class ClassTail[?Yield]
+        //
+        // In this case, we use "*default*" as this class declaration's name.
+        requirements = FunctionNameRequirements::None;
+        info.className = &m_vm->propertyNames->builtinNames().starDefaultPrivateName();
+    }
+
+    TreeClassExpression classExpr = parseClass(context, requirements, info);
     failIfFalse(classExpr, "Failed to parse class");
+    ASSERT(info.className);
 
     DeclarationResultMask declarationResult = declareVariable(info.className, DeclarationType::LetDeclaration);
     if (declarationResult & DeclarationResult::InvalidDuplicateDeclaration)
         internalFailWithMessage(false, "Cannot declare a class twice: '", info.className->impl(), "'");
     if (exportType == ExportType::Exported) {
+        ASSERT_WITH_MESSAGE(declarationDefaultContext != DeclarationDefaultContext::ExportDefault, "Export default case will export the name and binding in the caller.");
         semanticFailIfFalse(exportName(*info.className), "Cannot export a duplicate class name: '", info.className->impl(), "'");
         m_moduleScopeData->exportBinding(*info.className);
     }
@@ -2277,7 +2330,7 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseClassDeclarat
 }
 
 template <typename LexerType>
-template <class TreeBuilder> TreeClassExpression Parser<LexerType>::parseClass(TreeBuilder& context, FunctionRequirements requirements, ParserClassInfo<TreeBuilder>& info)
+template <class TreeBuilder> TreeClassExpression Parser<LexerType>::parseClass(TreeBuilder& context, FunctionNameRequirements requirements, ParserClassInfo<TreeBuilder>& info)
 {
     ASSERT(match(CLASSTOKEN));
     JSTokenLocation location(tokenLocation());
@@ -2291,20 +2344,19 @@ template <class TreeBuilder> TreeClassExpression Parser<LexerType>::parseClass(T
     classScope->preventVarDeclarations();
     classScope->setStrictMode();
 
-    const Identifier* className = nullptr;
+    ASSERT_WITH_MESSAGE(requirements != FunctionNameRequirements::Unnamed, "Currently, there is no caller that uses FunctionNameRequirements::Unnamed for class syntax.");
+    ASSERT_WITH_MESSAGE(!(requirements == FunctionNameRequirements::None && !info.className), "When specifying FunctionNameRequirements::None, we need to initialize info.className with the default value in the caller side.");
     if (match(IDENT)) {
-        className = m_token.m_data.ident;
+        info.className = m_token.m_data.ident;
         next();
-        failIfTrue(classScope->declareLexicalVariable(className, true) & DeclarationResult::InvalidStrictMode, "'", className->impl(), "' is not a valid class name");
-    } else if (requirements == FunctionNeedsName) {
+        failIfTrue(classScope->declareLexicalVariable(info.className, true) & DeclarationResult::InvalidStrictMode, "'", info.className->impl(), "' is not a valid class name");
+    } else if (requirements == FunctionNameRequirements::Named) {
         if (match(OPENBRACE))
             semanticFail("Class statements must have a name");
         semanticFailureDueToKeyword("class name");
         failDueToUnexpectedToken();
-    } else
-        className = &m_vm->propertyNames->nullIdentifier;
-    ASSERT(className);
-    info.className = className;
+    }
+    ASSERT(info.className);
 
     TreeExpression parentClass = 0;
     if (consume(EXTENDS)) {
@@ -2401,8 +2453,8 @@ template <class TreeBuilder> TreeClassExpression Parser<LexerType>::parseClass(T
                 semanticFailIfTrue(*ident == m_vm->propertyNames->prototype, "Cannot declare a generator named 'prototype'");
                 semanticFailIfTrue(*ident == m_vm->propertyNames->constructor, "Cannot declare a generator named 'constructor'");
             }
-            failIfFalse((parseFunctionInfo(context, FunctionNoRequirements, parseMode, false, isConstructor ? constructorKind : ConstructorKind::None, SuperBinding::Needed, methodStart, methodInfo, FunctionDefinitionType::Method)), "Cannot parse this method");
-            methodInfo.name = isConstructor ? className : ident;
+            methodInfo.name = isConstructor ? info.className : ident;
+            failIfFalse((parseFunctionInfo(context, FunctionNameRequirements::Unnamed, parseMode, false, isConstructor ? constructorKind : ConstructorKind::None, SuperBinding::Needed, methodStart, methodInfo, FunctionDefinitionType::Method)), "Cannot parse this method");
 
             TreeExpression method = context.createMethodDefinition(methodLocation, methodInfo);
             if (isConstructor) {
@@ -2856,10 +2908,10 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseExportDeclara
         TreeStatement result = 0;
         bool isFunctionOrClassDeclaration = false;
         const Identifier* localName = nullptr;
-        SavePoint savePoint = createSavePoint();
 
         bool startsWithFunction = match(FUNCTION);
         if (startsWithFunction || match(CLASSTOKEN)) {
+            SavePoint savePoint = createSavePoint();
             isFunctionOrClassDeclaration = true;
             next();
 
@@ -2871,14 +2923,18 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseExportDeclara
             restoreSavePoint(savePoint);
         }
 
-        if (localName) {
-            if (match(FUNCTION)) {
+        if (!localName)
+            localName = &m_vm->propertyNames->builtinNames().starDefaultPrivateName();
+
+        if (isFunctionOrClassDeclaration) {
+            if (startsWithFunction) {
+                ASSERT(match(FUNCTION));
                 DepthManager statementDepth(&m_statementDepth);
                 m_statementDepth = 1;
-                result = parseFunctionDeclaration(context);
+                result = parseFunctionDeclaration(context, ExportType::NotExported, DeclarationDefaultContext::ExportDefault);
             } else {
                 ASSERT(match(CLASSTOKEN));
-                result = parseClassDeclaration(context);
+                result = parseClassDeclaration(context, ExportType::NotExported, DeclarationDefaultContext::ExportDefault);
             }
         } else {
             // export default expr;
@@ -2901,9 +2957,7 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseExportDeclara
 
             TreeExpression assignment = context.createAssignResolve(location, m_vm->propertyNames->builtinNames().starDefaultPrivateName(), expression, start, start, tokenEndPosition(), AssignmentContext::ConstDeclarationStatement);
             result = context.createExprStatement(location, assignment, start, tokenEndPosition());
-            if (!isFunctionOrClassDeclaration)
-                failIfFalse(autoSemiColon(), "Expected a ';' following a targeted export declaration");
-            localName = &m_vm->propertyNames->builtinNames().starDefaultPrivateName();
+            failIfFalse(autoSemiColon(), "Expected a ';' following a targeted export declaration");
         }
         failIfFalse(result, "Cannot parse the declaration");
 
@@ -3429,9 +3483,9 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parsePropertyMeth
     JSTokenLocation methodLocation(tokenLocation());
     unsigned methodStart = tokenStart();
     ParserFunctionInfo<TreeBuilder> methodInfo;
-    SourceParseMode parseMode = isGenerator ? SourceParseMode::GeneratorWrapperFunctionMode : SourceParseMode::MethodMode;
-    failIfFalse((parseFunctionInfo(context, FunctionNoRequirements, parseMode, false, ConstructorKind::None, SuperBinding::Needed, methodStart, methodInfo, FunctionDefinitionType::Method)), "Cannot parse this method");
     methodInfo.name = methodName;
+    SourceParseMode parseMode = isGenerator ? SourceParseMode::GeneratorWrapperFunctionMode : SourceParseMode::MethodMode;
+    failIfFalse((parseFunctionInfo(context, FunctionNameRequirements::Unnamed, parseMode, false, ConstructorKind::None, SuperBinding::Needed, methodStart, methodInfo, FunctionDefinitionType::Method)), "Cannot parse this method");
     return context.createMethodDefinition(methodLocation, methodInfo);
 }
 
@@ -3466,10 +3520,10 @@ template <class TreeBuilder> TreeProperty Parser<LexerType>::parseGetterSetter(T
     ParserFunctionInfo<TreeBuilder> info;
     if (type & PropertyNode::Getter) {
         failIfFalse(match(OPENPAREN), "Expected a parameter list for getter definition");
-        failIfFalse((parseFunctionInfo(context, FunctionNoRequirements, SourceParseMode::GetterMode, false, constructorKind, SuperBinding::Needed, getterOrSetterStartOffset, info, FunctionDefinitionType::Method)), "Cannot parse getter definition");
+        failIfFalse((parseFunctionInfo(context, FunctionNameRequirements::Unnamed, SourceParseMode::GetterMode, false, constructorKind, SuperBinding::Needed, getterOrSetterStartOffset, info, FunctionDefinitionType::Method)), "Cannot parse getter definition");
     } else {
         failIfFalse(match(OPENPAREN), "Expected a parameter list for setter definition");
-        failIfFalse((parseFunctionInfo(context, FunctionNoRequirements, SourceParseMode::SetterMode, false, constructorKind, SuperBinding::Needed, getterOrSetterStartOffset, info, FunctionDefinitionType::Method)), "Cannot parse setter definition");
+        failIfFalse((parseFunctionInfo(context, FunctionNameRequirements::Unnamed, SourceParseMode::SetterMode, false, constructorKind, SuperBinding::Needed, getterOrSetterStartOffset, info, FunctionDefinitionType::Method)), "Cannot parse setter definition");
     }
 
     if (stringPropertyName)
@@ -3671,6 +3725,15 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseArrayLiteral
 }
 
 template <typename LexerType>
+template <class TreeBuilder> TreeClassExpression Parser<LexerType>::parseClassExpression(TreeBuilder& context)
+{
+    ASSERT(match(CLASSTOKEN));
+    ParserClassInfo<TreeBuilder> info;
+    info.className = &m_vm->propertyNames->nullIdentifier;
+    return parseClass(context, FunctionNameRequirements::None, info);
+}
+
+template <typename LexerType>
 template <class TreeBuilder> TreeExpression Parser<LexerType>::parseFunctionExpression(TreeBuilder& context)
 {
     ASSERT(match(FUNCTION));
@@ -3682,7 +3745,7 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseFunctionExpr
     SourceParseMode parseMode = SourceParseMode::NormalFunctionMode;
     if (consume(TIMES))
         parseMode = SourceParseMode::GeneratorWrapperFunctionMode;
-    failIfFalse((parseFunctionInfo(context, FunctionNoRequirements, parseMode, false, ConstructorKind::None, SuperBinding::NotNeeded, functionKeywordStart, functionInfo, FunctionDefinitionType::Expression)), "Cannot parse function expression");
+    failIfFalse((parseFunctionInfo(context, FunctionNameRequirements::None, parseMode, false, ConstructorKind::None, SuperBinding::NotNeeded, functionKeywordStart, functionInfo, FunctionDefinitionType::Expression)), "Cannot parse function expression");
     return context.createFunctionExpr(location, functionInfo);
 }
 
@@ -3751,10 +3814,8 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parsePrimaryExpre
     switch (m_token.m_type) {
     case FUNCTION:
         return parseFunctionExpression(context);
-    case CLASSTOKEN: {
-        ParserClassInfo<TreeBuilder> info;
-        return parseClass(context, FunctionNoRequirements, info);
-    }
+    case CLASSTOKEN:
+        return parseClassExpression(context);
     case OPENBRACE:
         if (strictMode())
             return parseStrictObjectLiteral(context);
@@ -4078,7 +4139,7 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseArrowFunctio
     location = tokenLocation();
     ParserFunctionInfo<TreeBuilder> info;
     info.name = &m_vm->propertyNames->nullIdentifier;
-    failIfFalse((parseFunctionInfo(context, FunctionNoRequirements, SourceParseMode::ArrowFunctionMode, true, ConstructorKind::None, SuperBinding::NotNeeded, functionKeywordStart, info, FunctionDefinitionType::Expression)), "Cannot parse arrow function expression");
+    failIfFalse((parseFunctionInfo(context, FunctionNameRequirements::Unnamed, SourceParseMode::ArrowFunctionMode, true, ConstructorKind::None, SuperBinding::NotNeeded, functionKeywordStart, info, FunctionDefinitionType::Expression)), "Cannot parse arrow function expression");
 
     return context.createArrowFunctionExpr(location, info);
 }
index 1cad7c9..3920404 100644 (file)
@@ -70,7 +70,7 @@ COMPILE_ASSERT(LastUntaggedToken < 64, LessThan64UntaggedTokens);
 
 enum SourceElementsMode { CheckForStrictMode, DontCheckForStrictMode };
 enum FunctionBodyType { ArrowFunctionBodyExpression, ArrowFunctionBodyBlock, StandardFunctionBodyBlock };
-enum FunctionRequirements { FunctionNoRequirements, FunctionNeedsName };
+enum class FunctionNameRequirements { None, Named, Unnamed };
 
 enum class DestructuringKind {
     DestructureToVariables,
@@ -101,6 +101,10 @@ enum DeclarationResult {
 
 typedef uint8_t DeclarationResultMask;
 
+enum class DeclarationDefaultContext {
+    Standard,
+    ExportDefault,
+};
 
 template <typename T> inline bool isEvalNode() { return false; }
 template <> inline bool isEvalNode<EvalNode>() { return true; }
@@ -1355,8 +1359,8 @@ private:
     template <class TreeBuilder> TreeStatement parseStatementListItem(TreeBuilder&, const Identifier*& directive, unsigned* directiveLiteralLength);
     template <class TreeBuilder> TreeStatement parseStatement(TreeBuilder&, const Identifier*& directive, unsigned* directiveLiteralLength = 0);
     enum class ExportType { Exported, NotExported };
-    template <class TreeBuilder> TreeStatement parseClassDeclaration(TreeBuilder&, ExportType = ExportType::NotExported);
-    template <class TreeBuilder> TreeStatement parseFunctionDeclaration(TreeBuilder&, ExportType = ExportType::NotExported);
+    template <class TreeBuilder> TreeStatement parseClassDeclaration(TreeBuilder&, ExportType = ExportType::NotExported, DeclarationDefaultContext = DeclarationDefaultContext::Standard);
+    template <class TreeBuilder> TreeStatement parseFunctionDeclaration(TreeBuilder&, ExportType = ExportType::NotExported, DeclarationDefaultContext = DeclarationDefaultContext::Standard);
     template <class TreeBuilder> TreeStatement parseVariableDeclaration(TreeBuilder&, DeclarationType, ExportType = ExportType::NotExported);
     template <class TreeBuilder> TreeStatement parseDoWhileStatement(TreeBuilder&);
     template <class TreeBuilder> TreeStatement parseWhileStatement(TreeBuilder&);
@@ -1388,6 +1392,7 @@ private:
     template <class TreeBuilder> ALWAYS_INLINE TreeExpression parseArrayLiteral(TreeBuilder&);
     template <class TreeBuilder> ALWAYS_INLINE TreeExpression parseObjectLiteral(TreeBuilder&);
     template <class TreeBuilder> NEVER_INLINE TreeExpression parseStrictObjectLiteral(TreeBuilder&);
+    template <class TreeBuilder> ALWAYS_INLINE TreeClassExpression parseClassExpression(TreeBuilder&);
     template <class TreeBuilder> ALWAYS_INLINE TreeExpression parseFunctionExpression(TreeBuilder&);
     template <class TreeBuilder> ALWAYS_INLINE TreeArguments parseArguments(TreeBuilder&);
     template <class TreeBuilder> ALWAYS_INLINE TreeExpression parseArgument(TreeBuilder&, ArgumentType&);
@@ -1416,14 +1421,14 @@ private:
     template <class TreeBuilder> TreeStatement parseExportDeclaration(TreeBuilder&);
 
     enum class FunctionDefinitionType { Expression, Declaration, Method };
-    template <class TreeBuilder> NEVER_INLINE bool parseFunctionInfo(TreeBuilder&, FunctionRequirements, SourceParseMode, bool nameIsInContainingScope, ConstructorKind, SuperBinding, int functionKeywordStart, ParserFunctionInfo<TreeBuilder>&, FunctionDefinitionType);
+    template <class TreeBuilder> NEVER_INLINE bool parseFunctionInfo(TreeBuilder&, FunctionNameRequirements, SourceParseMode, bool nameIsInContainingScope, ConstructorKind, SuperBinding, int functionKeywordStart, ParserFunctionInfo<TreeBuilder>&, FunctionDefinitionType);
     
     ALWAYS_INLINE bool isArrowFunctionParameters();
     
     template <class TreeBuilder, class FunctionInfoType> NEVER_INLINE typename TreeBuilder::FormalParameterList parseFunctionParameters(TreeBuilder&, SourceParseMode, FunctionInfoType&);
     template <class TreeBuilder> NEVER_INLINE typename TreeBuilder::FormalParameterList createGeneratorParameters(TreeBuilder&);
 
-    template <class TreeBuilder> NEVER_INLINE TreeClassExpression parseClass(TreeBuilder&, FunctionRequirements, ParserClassInfo<TreeBuilder>&);
+    template <class TreeBuilder> NEVER_INLINE TreeClassExpression parseClass(TreeBuilder&, FunctionNameRequirements, ParserClassInfo<TreeBuilder>&);
 
     template <class TreeBuilder> NEVER_INLINE typename TreeBuilder::TemplateString parseTemplateString(TreeBuilder& context, bool isTemplateHead, typename LexerType::RawStringsBuildMode, bool& elementIsTail);
     template <class TreeBuilder> NEVER_INLINE typename TreeBuilder::TemplateLiteral parseTemplateLiteral(TreeBuilder&, typename LexerType::RawStringsBuildMode);
index 9f5d0fe..4f4214c 100644 (file)
@@ -82,11 +82,12 @@ CallType FunctionPrototype::getCallData(JSCell*, CallData& callData)
 
 EncodedJSValue JSC_HOST_CALL functionProtoFuncToString(ExecState* exec)
 {
+    VM& vm = exec->vm();
     JSValue thisValue = exec->thisValue();
     if (thisValue.inherits(JSFunction::info())) {
         JSFunction* function = jsCast<JSFunction*>(thisValue);
         if (function->isHostOrBuiltinFunction())
-            return JSValue::encode(jsMakeNontrivialString(exec, "function ", function->name(), "() {\n    [native code]\n}"));
+            return JSValue::encode(jsMakeNontrivialString(exec, "function ", function->name(vm), "() {\n    [native code]\n}"));
 
         FunctionExecutable* executable = function->jsExecutable();
         if (executable->isClass()) {
@@ -99,7 +100,7 @@ EncodedJSValue JSC_HOST_CALL functionProtoFuncToString(ExecState* exec)
         StringView source = executable->source().provider()->getRange(
             executable->parametersStartOffset(),
             executable->parametersStartOffset() + executable->source().length());
-        return JSValue::encode(jsMakeNontrivialString(exec, functionHeader, function->name(), source));
+        return JSValue::encode(jsMakeNontrivialString(exec, functionHeader, function->name(vm), source));
     }
 
     if (thisValue.inherits(InternalFunction::info())) {
index fae1e37..b7517c1 100644 (file)
@@ -166,13 +166,16 @@ FunctionRareData* JSFunction::initializeRareData(ExecState* exec, size_t inlineC
     return m_rareData.get();
 }
 
-String JSFunction::name()
+String JSFunction::name(VM& vm)
 {
     if (isHostFunction()) {
         NativeExecutable* executable = jsCast<NativeExecutable*>(this->executable());
         return executable->name();
     }
-    return jsExecutable()->name().string();
+    const Identifier identifier = jsExecutable()->name();
+    if (identifier == vm.propertyNames->builtinNames().starDefaultPrivateName())
+        return emptyString();
+    return identifier.string();
 }
 
 String JSFunction::displayName(VM& vm)
@@ -192,7 +195,7 @@ const String JSFunction::calculatedDisplayName(VM& vm)
     if (!explicitName.isEmpty())
         return explicitName;
     
-    const String actualName = name();
+    const String actualName = name(vm);
     if (!actualName.isEmpty() || isHostOrBuiltinFunction())
         return actualName;
     
@@ -616,7 +619,15 @@ void JSFunction::reifyLength(ExecState* exec)
 
 void JSFunction::reifyName(ExecState* exec)
 {
-    String name = jsExecutable()->ecmaName().string();
+    const Identifier& ecmaName = jsExecutable()->ecmaName();
+    String name;
+    // https://tc39.github.io/ecma262/#sec-exports-runtime-semantics-evaluation
+    // When the ident is "*default*", we need to set "default" for the ecma name.
+    // This "*default*" name is never shown to users.
+    if (ecmaName == exec->propertyNames().builtinNames().starDefaultPrivateName())
+        name = exec->propertyNames().defaultKeyword.string();
+    else
+        name = ecmaName.string();
     reifyName(exec, name);
 }
 
index fba45af..fa946f2 100644 (file)
@@ -81,7 +81,7 @@ public:
     JS_EXPORT_PRIVATE static JSFunction* createBuiltinFunction(VM&, FunctionExecutable*, JSGlobalObject*);
     static JSFunction* createBuiltinFunction(VM&, FunctionExecutable*, JSGlobalObject*, const String& name);
 
-    JS_EXPORT_PRIVATE String name();
+    JS_EXPORT_PRIVATE String name(VM&);
     JS_EXPORT_PRIVATE String displayName(VM&);
     const String calculatedDisplayName(VM&);
 
index dbaf833..6ac59b8 100644 (file)
@@ -80,7 +80,7 @@ void LazyClassStructure::Initializer::setConstructor(JSObject* constructor)
     if (InternalFunction* internalFunction = jsDynamicCast<InternalFunction*>(constructor))
         name = internalFunction->name();
     else if (JSFunction* function = jsDynamicCast<JSFunction*>(constructor))
-        name = function->name();
+        name = function->name(vm);
     else
         RELEASE_ASSERT_NOT_REACHED();