Diff
Modified: trunk/JSTests/ChangeLog (204841 => 204842)
--- trunk/JSTests/ChangeLog 2016-08-23 18:09:21 UTC (rev 204841)
+++ trunk/JSTests/ChangeLog 2016-08-23 18:14:30 UTC (rev 204842)
@@ -1,5 +1,31 @@
2016-08-22 Yusuke Suzuki <[email protected]>
+ [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 <[email protected]>
+
[ES6] Module should not allow HTML comments
https://bugs.webkit.org/show_bug.cgi?id=161041
Added: trunk/JSTests/modules/export-default-function-name-in-assignment-_expression_.js (0 => 204842)
--- trunk/JSTests/modules/export-default-function-name-in-assignment-_expression_.js (rev 0)
+++ trunk/JSTests/modules/export-default-function-name-in-assignment-_expression_.js 2016-08-23 18:14:30 UTC (rev 204842)
@@ -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 () { }`);
Added: trunk/JSTests/modules/export-default-function-name-in-class-declaration.js (0 => 204842)
--- trunk/JSTests/modules/export-default-function-name-in-class-declaration.js (rev 0)
+++ trunk/JSTests/modules/export-default-function-name-in-class-declaration.js 2016-08-23 18:14:30 UTC (rev 204842)
@@ -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 { }`);
Added: trunk/JSTests/modules/export-default-function-name-in-function-declaration.js (0 => 204842)
--- trunk/JSTests/modules/export-default-function-name-in-function-declaration.js (rev 0)
+++ trunk/JSTests/modules/export-default-function-name-in-function-declaration.js 2016-08-23 18:14:30 UTC (rev 204842)
@@ -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 () { }`);
Added: trunk/JSTests/modules/export-default-function-name-in-generator-declaration.js (0 => 204842)
--- trunk/JSTests/modules/export-default-function-name-in-generator-declaration.js (rev 0)
+++ trunk/JSTests/modules/export-default-function-name-in-generator-declaration.js 2016-08-23 18:14:30 UTC (rev 204842)
@@ -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 * () { }`);
Added: trunk/JSTests/stress/method-name.js (0 => 204842)
--- trunk/JSTests/stress/method-name.js (rev 0)
+++ trunk/JSTests/stress/method-name.js 2016-08-23 18:14:30 UTC (rev 204842)
@@ -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.`);
Modified: trunk/JSTests/test262.yaml (204841 => 204842)
--- trunk/JSTests/test262.yaml 2016-08-23 18:09:21 UTC (rev 204841)
+++ trunk/JSTests/test262.yaml 2016-08-23 18:14:30 UTC (rev 204842)
@@ -70868,7 +70868,7 @@
- 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
@@ -70876,7 +70876,7 @@
- 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
@@ -70886,11 +70886,11 @@
- 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
@@ -71072,11 +71072,11 @@
- 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
@@ -71504,9 +71504,9 @@
- 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
Modified: trunk/Source/_javascript_Core/ChangeLog (204841 => 204842)
--- trunk/Source/_javascript_Core/ChangeLog 2016-08-23 18:09:21 UTC (rev 204841)
+++ trunk/Source/_javascript_Core/ChangeLog 2016-08-23 18:14:30 UTC (rev 204842)
@@ -1,3 +1,55 @@
+2016-08-22 Yusuke Suzuki <[email protected]>
+
+ [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 <[email protected]>
JIT::updateTopCallframe() in the baseline JIT should use PC instead of PC+1
Modified: trunk/Source/_javascript_Core/inspector/JSInjectedScriptHost.cpp (204841 => 204842)
--- trunk/Source/_javascript_Core/inspector/JSInjectedScriptHost.cpp 2016-08-23 18:09:21 UTC (rev 204841)
+++ trunk/Source/_javascript_Core/inspector/JSInjectedScriptHost.cpp 2016-08-23 18:14:30 UTC (rev 204842)
@@ -228,7 +228,7 @@
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));
Modified: trunk/Source/_javascript_Core/parser/ModuleScopeData.h (204841 => 204842)
--- trunk/Source/_javascript_Core/parser/ModuleScopeData.h 2016-08-23 18:09:21 UTC (rev 204841)
+++ trunk/Source/_javascript_Core/parser/ModuleScopeData.h 2016-08-23 18:14:30 UTC (rev 204842)
@@ -35,7 +35,7 @@
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 @@
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)
Modified: trunk/Source/_javascript_Core/parser/Parser.cpp (204841 => 204842)
--- trunk/Source/_javascript_Core/parser/Parser.cpp 2016-08-23 18:09:21 UTC (rev 204841)
+++ trunk/Source/_javascript_Core/parser/Parser.cpp 2016-08-23 18:14:30 UTC (rev 204842)
@@ -1930,7 +1930,7 @@
}
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 @@
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 @@
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,19 +2226,46 @@
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;
failIfTrueIfStrict(declarationResult & DeclarationResult::InvalidStrictMode, "Cannot declare a function named '", functionInfo.name->impl(), "' in strict mode");
@@ -2240,6 +2272,7 @@
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 <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 @@
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 <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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
JSTokenLocation methodLocation(tokenLocation());
unsigned methodStart = tokenStart();
ParserFunctionInfo<TreeBuilder> methodInfo;
+ methodInfo.name = methodName;
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;
+ 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 @@
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 <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 @@
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 @@
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 @@
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);
}
Modified: trunk/Source/_javascript_Core/parser/Parser.h (204841 => 204842)
--- trunk/Source/_javascript_Core/parser/Parser.h 2016-08-23 18:09:21 UTC (rev 204841)
+++ trunk/Source/_javascript_Core/parser/Parser.h 2016-08-23 18:14:30 UTC (rev 204842)
@@ -70,7 +70,7 @@
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 @@
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 @@
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 @@
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,7 +1421,7 @@
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();
@@ -1423,7 +1428,7 @@
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);
Modified: trunk/Source/_javascript_Core/runtime/FunctionPrototype.cpp (204841 => 204842)
--- trunk/Source/_javascript_Core/runtime/FunctionPrototype.cpp 2016-08-23 18:09:21 UTC (rev 204841)
+++ trunk/Source/_javascript_Core/runtime/FunctionPrototype.cpp 2016-08-23 18:14:30 UTC (rev 204842)
@@ -82,11 +82,12 @@
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 @@
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())) {
Modified: trunk/Source/_javascript_Core/runtime/JSFunction.cpp (204841 => 204842)
--- trunk/Source/_javascript_Core/runtime/JSFunction.cpp 2016-08-23 18:09:21 UTC (rev 204841)
+++ trunk/Source/_javascript_Core/runtime/JSFunction.cpp 2016-08-23 18:14:30 UTC (rev 204842)
@@ -166,13 +166,16 @@
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 @@
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::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);
}
Modified: trunk/Source/_javascript_Core/runtime/JSFunction.h (204841 => 204842)
--- trunk/Source/_javascript_Core/runtime/JSFunction.h 2016-08-23 18:09:21 UTC (rev 204841)
+++ trunk/Source/_javascript_Core/runtime/JSFunction.h 2016-08-23 18:14:30 UTC (rev 204842)
@@ -81,7 +81,7 @@
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&);
Modified: trunk/Source/_javascript_Core/runtime/LazyClassStructure.cpp (204841 => 204842)
--- trunk/Source/_javascript_Core/runtime/LazyClassStructure.cpp 2016-08-23 18:09:21 UTC (rev 204841)
+++ trunk/Source/_javascript_Core/runtime/LazyClassStructure.cpp 2016-08-23 18:14:30 UTC (rev 204842)
@@ -80,7 +80,7 @@
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();