Ignore:
Timestamp:
Aug 12, 2015, 1:38:45 PM (10 years ago)
Author:
Yusuke Suzuki
Message:

[ES6] Add ES6 Modules preparsing phase to collect the dependencies
https://bugs.webkit.org/show_bug.cgi?id=147353

Reviewed by Geoffrey Garen.

This patch implements ModuleRecord and ModuleAnalyzer.
ModuleAnalyzer analyzes the produced AST from the parser.
By collaborating with the parser, ModuleAnalyzer collects the information
that is necessary to request the loading for the dependent modules and
construct module's environment and namespace object before executing the actual
module body.

In the parser, we annotate which variable is imported binding and which variable
is exported from the current module. This information is leveraged in the ModuleAnalyzer
to categorize the export entries.

To preparse the modules in the parser, we just add the new flag ModuleParseMode
instead of introducing a new TreeContext type. This is because only 2 users use the
parseModuleSourceElements; preparser and actual compiler. Adding the flag is simple
enough to switch the context to the SyntaxChecker when parsing the non-module related
statement in the preparsing phase.

To demonstrate the module analyzer, we added the new option dumpModuleRecord option
into the JSC shell. By specifying this, the result of analysis is dumped when the module
is parsed and analyzed.

(JSC::ASTBuilder::createExportDefaultDeclaration):

  • parser/ModuleAnalyzer.cpp: Added.

(JSC::ModuleAnalyzer::ModuleAnalyzer):
(JSC::ModuleAnalyzer::exportedBinding):
(JSC::ModuleAnalyzer::declareExportAlias):
(JSC::ModuleAnalyzer::exportVariable):
(JSC::ModuleAnalyzer::analyze):

  • parser/ModuleAnalyzer.h: Added.

(JSC::ModuleAnalyzer::vm):
(JSC::ModuleAnalyzer::moduleRecord):

  • parser/ModuleRecord.cpp: Added.

(JSC::printableName):
(JSC::ModuleRecord::dump):

  • parser/ModuleRecord.h: Added.

(JSC::ModuleRecord::ImportEntry::isNamespace):
(JSC::ModuleRecord::create):
(JSC::ModuleRecord::appendRequestedModule):
(JSC::ModuleRecord::addImportEntry):
(JSC::ModuleRecord::addExportEntry):
(JSC::ModuleRecord::addStarExportEntry):

  • parser/NodeConstructors.h:

(JSC::ModuleDeclarationNode::ModuleDeclarationNode):
(JSC::ImportDeclarationNode::ImportDeclarationNode):
(JSC::ExportAllDeclarationNode::ExportAllDeclarationNode):
(JSC::ExportDefaultDeclarationNode::ExportDefaultDeclarationNode):
(JSC::ExportLocalDeclarationNode::ExportLocalDeclarationNode):
(JSC::ExportNamedDeclarationNode::ExportNamedDeclarationNode):

  • parser/Nodes.h:

(JSC::ExportDefaultDeclarationNode::localName):

  • parser/NodesAnalyzeModule.cpp: Added.

(JSC::ScopeNode::analyzeModule):
(JSC::SourceElements::analyzeModule):
(JSC::ImportDeclarationNode::analyzeModule):
(JSC::ExportAllDeclarationNode::analyzeModule):
(JSC::ExportDefaultDeclarationNode::analyzeModule):
(JSC::ExportLocalDeclarationNode::analyzeModule):
(JSC::ExportNamedDeclarationNode::analyzeModule):

  • parser/Parser.cpp:

(JSC::Parser<LexerType>::parseInner):
(JSC::Parser<LexerType>::parseModuleSourceElements):
(JSC::Parser<LexerType>::parseVariableDeclarationList):
(JSC::Parser<LexerType>::createBindingPattern):
(JSC::Parser<LexerType>::parseFunctionDeclaration):
(JSC::Parser<LexerType>::parseClassDeclaration):
(JSC::Parser<LexerType>::parseImportClauseItem):
(JSC::Parser<LexerType>::parseExportSpecifier):
(JSC::Parser<LexerType>::parseExportDeclaration):

  • parser/Parser.h:

(JSC::Scope::lexicalVariables):
(JSC::Scope::declareLexicalVariable):
(JSC::Parser::declareVariable):
(JSC::Parser::exportName):
(JSC::Parser<LexerType>::parse):
(JSC::parse):

  • parser/ParserModes.h:
  • parser/SyntaxChecker.h:

(JSC::SyntaxChecker::createExportDefaultDeclaration):

  • parser/VariableEnvironment.cpp:

(JSC::VariableEnvironment::markVariableAsImported):
(JSC::VariableEnvironment::markVariableAsExported):

  • parser/VariableEnvironment.h:

(JSC::VariableEnvironmentEntry::isExported):
(JSC::VariableEnvironmentEntry::isImported):
(JSC::VariableEnvironmentEntry::setIsExported):
(JSC::VariableEnvironmentEntry::setIsImported):

  • runtime/CommonIdentifiers.h:
  • runtime/Completion.cpp:

(JSC::checkModuleSyntax):

  • runtime/Options.h:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/parser/Parser.cpp

    r188219 r188355  
    236236
    237237template <typename LexerType>
    238 String Parser<LexerType>::parseInner(const Identifier& calleeName, FunctionParseMode parseMode)
    239 {
     238String Parser<LexerType>::parseInner(const Identifier& calleeName, FunctionParseMode parseMode, ModuleParseMode moduleParseMode)
     239{
     240    UNUSED_PARAM(moduleParseMode);
     241
    240242    String parseError = String();
    241243   
     
    273275#if ENABLE(ES6_MODULES)
    274276        else if (m_codeType == JSParserCodeType::Module)
    275             sourceElements = parseModuleSourceElements(context);
     277            sourceElements = parseModuleSourceElements(context, moduleParseMode);
    276278#endif
    277279        else
     
    413415
    414416template <typename LexerType>
    415 template <class TreeBuilder> TreeSourceElements Parser<LexerType>::parseModuleSourceElements(TreeBuilder& context)
     417template <class TreeBuilder> TreeSourceElements Parser<LexerType>::parseModuleSourceElements(TreeBuilder& context, ModuleParseMode moduleParseMode)
    416418{
    417419    TreeSourceElements sourceElements = context.createSourceElements();
    418 
    419     // FIXME: When some sort of ModuleAnalyzer TreeBuilder is landed,
    420     // this SyntaxChecker will be replaced with typedef under the TreeBuilder,
    421     // like TreeBuilder::ModuleStatementItemBuilder.
    422     // https://bugs.webkit.org/show_bug.cgi?id=147353
    423     SyntaxChecker moduleStatementItemBuilder(const_cast<VM*>(m_vm), m_lexer.get());
     420    SyntaxChecker syntaxChecker(const_cast<VM*>(m_vm), m_lexer.get());
    424421
    425422    while (true) {
    426         if (match(IMPORT) || match(EXPORT)) {
    427             TreeStatement statement = 0;
    428             if (match(IMPORT))
    429                 statement = parseImportDeclaration(context);
    430             else
    431                 statement = parseExportDeclaration(context);
    432 
    433             if (!statement)
    434                 break;
    435             context.appendStatement(sourceElements, statement);
    436         } else {
     423        TreeStatement statement = 0;
     424        if (match(IMPORT))
     425            statement = parseImportDeclaration(context);
     426        else if (match(EXPORT))
     427            statement = parseExportDeclaration(context);
     428        else {
    437429            const Identifier* directive = 0;
    438430            unsigned directiveLiteralLength = 0;
    439             if (!parseStatementListItem(moduleStatementItemBuilder, directive, &directiveLiteralLength))
    440                 break;
    441         }
     431            if (moduleParseMode == ModuleParseMode::Analyze) {
     432                if (!parseStatementListItem(syntaxChecker, directive, &directiveLiteralLength))
     433                    break;
     434                continue;
     435            }
     436            statement = parseStatementListItem(context, directive, &directiveLiteralLength);
     437        }
     438
     439        if (!statement)
     440            break;
     441        context.appendStatement(sourceElements, statement);
    442442    }
    443443
    444444    propagateError();
    445445
    446     for (const auto& uid : currentScope()->moduleScopeData().exportedBindings())
    447         semanticFailIfFalse(currentScope()->hasDeclaredVariable(uid) || currentScope()->hasLexicallyDeclaredVariable(uid), "Exported binding '", uid.get(), "' needs to refer to a top-level declared variable");
     446    for (const auto& uid : currentScope()->moduleScopeData().exportedBindings()) {
     447        if (currentScope()->hasDeclaredVariable(uid)) {
     448            currentScope()->declaredVariables().markVariableAsExported(uid);
     449            continue;
     450        }
     451
     452        if (currentScope()->hasLexicallyDeclaredVariable(uid)) {
     453            currentScope()->lexicalVariables().markVariableAsExported(uid);
     454            continue;
     455        }
     456
     457        semanticFail("Exported binding '", uid.get(), "' needs to refer to a top-level declared variable");
     458    }
    448459
    449460    return sourceElements;
     
    602613                }
    603614            }
    604             semanticFailIfTrue(exportType == ExportType::Exported && !currentScope()->moduleScopeData().exportName(*name), "Cannot export a duplicate name '", name->impl(), "'");
     615            if (exportType == ExportType::Exported) {
     616                semanticFailIfFalse(exportName(*name), "Cannot export a duplicate name '", name->impl(), "'");
     617                currentScope()->moduleScopeData().exportBinding(*name);
     618            }
    605619
    606620            if (hasInitializer) {
     
    706720    }
    707721
    708     semanticFailIfTrue(exportType == ExportType::Exported && !currentScope()->moduleScopeData().exportName(name), "Cannot export a duplicate name '", name.impl(), "'");
     722    if (exportType == ExportType::Exported) {
     723        semanticFailIfFalse(exportName(name), "Cannot export a duplicate name '", name.impl(), "'");
     724        currentScope()->moduleScopeData().exportBinding(name);
     725    }
    709726    return context.createBindingLocation(token.m_location, name, token.m_startPosition, token.m_endPosition, bindingContext);
    710727}
     
    18571874    failIfFalse(functionInfo.name, "Function statements must have a name");
    18581875    failIfTrueIfStrict(declareVariable(functionInfo.name) & DeclarationResult::InvalidStrictMode, "Cannot declare a function named '", functionInfo.name->impl(), "' in strict mode");
    1859     semanticFailIfTrue(exportType == ExportType::Exported && !currentScope()->moduleScopeData().exportName(*functionInfo.name), "Cannot export a duplicate function name: '", functionInfo.name->impl(), "'");
     1876    if (exportType == ExportType::Exported) {
     1877        semanticFailIfFalse(exportName(*functionInfo.name), "Cannot export a duplicate function name: '", functionInfo.name->impl(), "'");
     1878        currentScope()->moduleScopeData().exportBinding(*functionInfo.name);
     1879    }
    18601880    return context.createFuncDeclStatement(location, functionInfo);
    18611881}
     
    18771897    if (declarationResult & DeclarationResult::InvalidDuplicateDeclaration)
    18781898        internalFailWithMessage(false, "Cannot declare a class twice: '", info.className->impl(), "'");
    1879     semanticFailIfTrue(exportType == ExportType::Exported && !currentScope()->moduleScopeData().exportName(*info.className), "Cannot export a duplicate class name: '", info.className->impl(), "'");
     1899    if (exportType == ExportType::Exported) {
     1900        semanticFailIfFalse(exportName(*info.className), "Cannot export a duplicate class name: '", info.className->impl(), "'");
     1901        currentScope()->moduleScopeData().exportBinding(*info.className);
     1902    }
    18801903
    18811904    JSTextPosition classEnd = lastTokenEndPosition();
     
    22832306
    22842307    semanticFailIfTrue(localNameToken.m_type & KeywordTokenFlag, "Cannot use keyword as imported binding name");
    2285     DeclarationResultMask declarationResult = declareVariable(localName, DeclarationType::ConstDeclaration);
     2308    DeclarationResultMask declarationResult = declareVariable(localName, DeclarationType::ConstDeclaration, DeclarationImportType::Imported);
    22862309    if (declarationResult != DeclarationResult::Valid) {
    22872310        failIfTrueIfStrict(declarationResult & DeclarationResult::InvalidStrictMode, "Cannot declare an imported binding named ", localName->impl(), " in strict mode");
     
    23852408    }
    23862409
    2387     semanticFailIfFalse(currentScope()->moduleScopeData().exportName(*exportedName), "Cannot export a duplicate name '", exportedName->impl(), "'");
     2410    semanticFailIfFalse(exportName(*exportedName), "Cannot export a duplicate name '", exportedName->impl(), "'");
    23882411    maybeLocalNames.append(localName);
    23892412    return context.createExportSpecifier(specifierLocation, *localName, *exportedName);
     
    24082431        failIfFalse(moduleSpecifier, "Cannot parse the 'from' clause");
    24092432        failIfFalse(autoSemiColon(), "Expected a ';' following a targeted export declaration");
     2433
    24102434        return context.createExportAllDeclaration(exportLocation, moduleSpecifier);
    24112435    }
     
    24192443
    24202444        TreeStatement result = 0;
    2421         bool hasName = false;
    24222445        bool isFunctionOrClassDeclaration = false;
     2446        const Identifier* localName = nullptr;
    24232447        SavePoint savePoint = createSavePoint();
    24242448        if (match(FUNCTION)
     
    24302454            next();
    24312455            // FIXME: When landing ES6 generators, we need to take care of that '*' comes.
    2432             hasName = match(IDENT);
     2456            if (match(IDENT))
     2457                localName = m_token.m_data.ident;
    24332458            restoreSavePoint(savePoint);
    24342459        }
    24352460
    2436         if (hasName) {
     2461        if (localName) {
    24372462            if (match(FUNCTION))
    24382463                result = parseFunctionDeclaration(context);
     
    24442469#endif
    24452470        } else {
     2471            // export default expr;
     2472            //
     2473            // It should be treated as the same to the following.
     2474            //
     2475            // const *default* = expr;
     2476            // export { *default* as default }
    24462477            JSTokenLocation location(tokenLocation());
    24472478            JSTextPosition start = tokenStartPosition();
    24482479            TreeExpression expression = parseAssignmentExpression(context);
    24492480            failIfFalse(expression, "Cannot parse expression");
    2450             result = context.createExprStatement(location, expression, start, tokenEndPosition());
     2481
     2482            DeclarationResultMask declarationResult = declareVariable(&m_vm->propertyNames->starDefaultPrivateName, DeclarationType::ConstDeclaration);
     2483            if (declarationResult & DeclarationResult::InvalidDuplicateDeclaration)
     2484                internalFailWithMessage(false, "Cannot export 'default' name twice: '");
     2485
     2486            TreeExpression assignment = context.createAssignResolve(location, m_vm->propertyNames->starDefaultPrivateName, expression, start, start, tokenEndPosition(), AssignmentContext::ConstDeclarationStatement);
     2487            result = context.createExprStatement(location, assignment, start, tokenEndPosition());
    24512488            if (!isFunctionOrClassDeclaration)
    24522489                failIfFalse(autoSemiColon(), "Expected a ';' following a targeted export declaration");
     2490            localName = &m_vm->propertyNames->starDefaultPrivateName;
    24532491        }
    24542492        failIfFalse(result, "Cannot parse the declaration");
    2455         semanticFailIfFalse(currentScope()->moduleScopeData().exportName(m_vm->propertyNames->defaultKeyword), "Cannot export 'default' name twice.");
    2456 
    2457         return context.createExportDefaultDeclaration(exportLocation, result);
     2493
     2494        semanticFailIfFalse(exportName(m_vm->propertyNames->defaultKeyword), "Cannot export 'default' name twice.");
     2495        currentScope()->moduleScopeData().exportBinding(*localName);
     2496        return context.createExportDefaultDeclaration(exportLocation, result, *localName);
    24582497    }
    24592498
Note: See TracChangeset for help on using the changeset viewer.