Ignore:
Timestamp:
Oct 11, 2018, 4:43:58 PM (7 years ago)
Author:
[email protected]
Message:

[JSC] JSC should have "parseFunction" to optimize Function constructor
https://bugs.webkit.org/show_bug.cgi?id=190340

Reviewed by Mark Lam.

JSTests:

This patch fixes the line number of syntax errors raised by the Function constructor,
since we now parse the final code only once. And we no longer use block statement
for Function constructor's parsing.

  • ChakraCore/test/Function/FuncBodyES5.baseline-jsc:
  • stress/function-cache-with-parameters-end-position.js: Added.

(shouldBe):
(shouldThrow):
(i.anonymous):

  • stress/function-constructor-name.js: Added.

(shouldBe):
(GeneratorFunction):
(AsyncFunction.async):
(AsyncGeneratorFunction.async):
(anonymous):
(async.anonymous):

  • test262/expectations.yaml:

LayoutTests/imported/w3c:

  • web-platform-tests/html/webappapis/scripting/events/inline-event-handler-ordering-expected.txt:
  • web-platform-tests/html/webappapis/scripting/events/invalid-uncompiled-raw-handler-compiled-late-expected.txt:
  • web-platform-tests/html/webappapis/scripting/processing-model-2/compile-error-in-attribute-expected.txt:
  • web-platform-tests/html/webappapis/scripting/processing-model-2/compile-error-in-body-onerror-expected.txt:

Source/JavaScriptCore:

The current Function constructor is suboptimal. We parse the piece of the same code three times to meet
the spec requirement. (1) check parameters syntax, (2) check body syntax, and (3) parse the entire function.
And to parse 1-3 correctly, we create two strings, the parameters and the entire function. This operation
is really costly and ideally we should meet the above requirement by the one time parsing.

To meet the above requirement, we add a special function for Parser, parseSingleFunction. This function
takes std::optional<int> functionConstructorParametersEndPosition and check this end position is correct in the parser.
For example, if we run the code,

Function('/*', '*/){')

According to the spec, this should produce '/*' parameter string and '*/){' body string. And parameter
string should be syntax-checked by the parser, and raise the error since it is incorrect. Instead of doing
that, in our implementation, we first create the entire string.

function anonymous(/*) {

*/){

}

And we parse it. At that time, we also pass the end position of the parameters to the parser. In the above case,
the position of the `function anonymous(/*)' <> is passed. And in the parser, we check that the last token
offset of the parameters is the given end position. This check allows us to raise the error correctly to the
above example while we parse the entire function only once. And we do not need to create two strings too.

This improves the performance of the Function constructor significantly. And web-tooling-benchmark/uglify-js is
significantly sped up (28.2%).

Before:

uglify-js: 2.94 runs/s

After:

uglify-js: 3.77 runs/s

  • bytecode/UnlinkedFunctionExecutable.cpp:

(JSC::UnlinkedFunctionExecutable::fromGlobalCode):

  • bytecode/UnlinkedFunctionExecutable.h:
  • parser/Parser.cpp:

(JSC::Parser<LexerType>::parseInner):
(JSC::Parser<LexerType>::parseSingleFunction):
(JSC::Parser<LexerType>::parseFunctionInfo):
(JSC::Parser<LexerType>::parseFunctionDeclaration):
(JSC::Parser<LexerType>::parseAsyncFunctionDeclaration):
(JSC::Parser<LexerType>::parseClass):
(JSC::Parser<LexerType>::parsePropertyMethod):
(JSC::Parser<LexerType>::parseGetterSetter):
(JSC::Parser<LexerType>::parseFunctionExpression):
(JSC::Parser<LexerType>::parseAsyncFunctionExpression):
(JSC::Parser<LexerType>::parseArrowFunctionExpression):

  • parser/Parser.h:

(JSC::Parser<LexerType>::parse):
(JSC::parse):
(JSC::parseFunctionForFunctionConstructor):

  • parser/ParserModes.h:
  • parser/ParserTokens.h:

(JSC::JSTextPosition::JSTextPosition):
(JSC::JSTokenLocation::JSTokenLocation): Deleted.

  • parser/SourceCodeKey.h:

(JSC::SourceCodeKey::SourceCodeKey):
(JSC::SourceCodeKey::operator== const):

  • runtime/CodeCache.cpp:

(JSC::CodeCache::getUnlinkedGlobalCodeBlock):
(JSC::CodeCache::getUnlinkedGlobalFunctionExecutable):

  • runtime/CodeCache.h:
  • runtime/FunctionConstructor.cpp:

(JSC::constructFunctionSkippingEvalEnabledCheck):

  • runtime/FunctionExecutable.cpp:

(JSC::FunctionExecutable::fromGlobalCode):

  • runtime/FunctionExecutable.h:

LayoutTests:

  • fast/dom/attribute-event-listener-errors-expected.txt:
  • fast/events/attribute-listener-deletion-crash-expected.txt:
  • fast/events/window-onerror-syntax-error-in-attr-expected.txt:
  • js/dom/invalid-syntax-for-function-expected.txt:
  • js/dom/script-start-end-locations-expected.txt:
File:
1 edited

Legend:

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

    r233377 r237054  
    860860};
    861861
    862 enum class ArgumentType {
    863     Normal,
    864     Spread
    865 };
     862enum class ArgumentType { Normal, Spread };
     863enum class ParsingContext { Program, FunctionConstructor, Eval };
    866864
    867865template <typename LexerType>
     
    875873
    876874    template <class ParsedNode>
    877     std::unique_ptr<ParsedNode> parse(ParserError&, const Identifier&, SourceParseMode);
     875    std::unique_ptr<ParsedNode> parse(ParserError&, const Identifier&, SourceParseMode, ParsingContext, std::optional<int> functionConstructorParametersEndPosition = std::nullopt);
    878876
    879877    JSTextPosition positionBeforeLastNewline() const { return m_lexer->positionBeforeLastNewline(); }
     
    13131311
    13141312    Parser();
    1315     String parseInner(const Identifier&, SourceParseMode);
     1313
     1314    String parseInner(const Identifier&, SourceParseMode, ParsingContext, std::optional<int> functionConstructorParametersEndPosition = std::nullopt);
    13161315
    13171316    void didFinishParsing(SourceElements*, DeclarationStacks::FunctionStack&&, VariableEnvironment&, UniquedStringImplPtrSet&&, CodeFeatures, int);
     
    15251524    template <class TreeBuilder> TreeSourceElements parseAsyncFunctionSourceElements(TreeBuilder&, SourceParseMode, bool isArrowFunctionBodyExpression, SourceElementsMode);
    15261525    template <class TreeBuilder> TreeSourceElements parseAsyncGeneratorFunctionSourceElements(TreeBuilder&, SourceParseMode, bool isArrowFunctionBodyExpression, SourceElementsMode);
     1526    template <class TreeBuilder> TreeSourceElements parseSingleFunction(TreeBuilder&, std::optional<int> functionConstructorParametersEndPosition);
    15271527    template <class TreeBuilder> TreeStatement parseStatementListItem(TreeBuilder&, const Identifier*& directive, unsigned* directiveLiteralLength);
    15281528    template <class TreeBuilder> TreeStatement parseStatement(TreeBuilder&, const Identifier*& directive, unsigned* directiveLiteralLength = 0);
    15291529    enum class ExportType { Exported, NotExported };
    15301530    template <class TreeBuilder> TreeStatement parseClassDeclaration(TreeBuilder&, ExportType = ExportType::NotExported, DeclarationDefaultContext = DeclarationDefaultContext::Standard);
    1531     template <class TreeBuilder> TreeStatement parseFunctionDeclaration(TreeBuilder&, ExportType = ExportType::NotExported, DeclarationDefaultContext = DeclarationDefaultContext::Standard);
     1531    template <class TreeBuilder> TreeStatement parseFunctionDeclaration(TreeBuilder&, ExportType = ExportType::NotExported, DeclarationDefaultContext = DeclarationDefaultContext::Standard, std::optional<int> functionConstructorParametersEndPosition = std::nullopt);
    15321532    template <class TreeBuilder> TreeStatement parseFunctionDeclarationStatement(TreeBuilder&, bool isAsync, bool parentAllowsFunctionDeclarationAsStatement);
    1533     template <class TreeBuilder> TreeStatement parseAsyncFunctionDeclaration(TreeBuilder&, ExportType = ExportType::NotExported, DeclarationDefaultContext = DeclarationDefaultContext::Standard);
     1533    template <class TreeBuilder> TreeStatement parseAsyncFunctionDeclaration(TreeBuilder&, ExportType = ExportType::NotExported, DeclarationDefaultContext = DeclarationDefaultContext::Standard, std::optional<int> functionConstructorParametersEndPosition = std::nullopt);
    15341534    template <class TreeBuilder> NEVER_INLINE bool maybeParseAsyncFunctionDeclarationStatement(TreeBuilder& context, TreeStatement& result, bool parentAllowsFunctionDeclarationAsStatement);
    15351535    template <class TreeBuilder> TreeStatement parseVariableDeclaration(TreeBuilder&, DeclarationType, ExportType = ExportType::NotExported);
     
    16001600
    16011601    enum class FunctionDefinitionType { Expression, Declaration, Method };
    1602     template <class TreeBuilder> NEVER_INLINE bool parseFunctionInfo(TreeBuilder&, FunctionNameRequirements, SourceParseMode, bool nameIsInContainingScope, ConstructorKind, SuperBinding, int functionKeywordStart, ParserFunctionInfo<TreeBuilder>&, FunctionDefinitionType);
     1602    template <class TreeBuilder> NEVER_INLINE bool parseFunctionInfo(TreeBuilder&, FunctionNameRequirements, SourceParseMode, bool nameIsInContainingScope, ConstructorKind, SuperBinding, int functionKeywordStart, ParserFunctionInfo<TreeBuilder>&, FunctionDefinitionType, std::optional<int> functionConstructorParametersEndPosition = std::nullopt);
    16031603   
    16041604    ALWAYS_INLINE bool isArrowFunctionParameters();
     
    18491849template <typename LexerType>
    18501850template <class ParsedNode>
    1851 std::unique_ptr<ParsedNode> Parser<LexerType>::parse(ParserError& error, const Identifier& calleeName, SourceParseMode parseMode)
     1851std::unique_ptr<ParsedNode> Parser<LexerType>::parse(ParserError& error, const Identifier& calleeName, SourceParseMode parseMode, ParsingContext parsingContext, std::optional<int> functionConstructorParametersEndPosition)
    18521852{
    18531853    int errLine;
     
    18661866    unsigned startColumn = m_source->startColumn().zeroBasedInt();
    18671867
    1868     String parseError = parseInner(calleeName, parseMode);
     1868    String parseError = parseInner(calleeName, parseMode, parsingContext, functionConstructorParametersEndPosition);
    18691869
    18701870    int lineNumber = m_lexer->lineNumber();
     
    19611961    if (source.provider()->source().is8Bit()) {
    19621962        Parser<Lexer<LChar>> parser(vm, source, builtinMode, strictMode, scriptMode, parseMode, superBinding, defaultConstructorKind, derivedContextType, isEvalNode<ParsedNode>(), evalContextType, debuggerParseData);
    1963         result = parser.parse<ParsedNode>(error, name, parseMode);
     1963        result = parser.parse<ParsedNode>(error, name, parseMode, isEvalNode<ParsedNode>() ? ParsingContext::Eval : ParsingContext::Program);
    19641964        if (positionBeforeLastNewline)
    19651965            *positionBeforeLastNewline = parser.positionBeforeLastNewline();
     
    19741974        ASSERT_WITH_MESSAGE(defaultConstructorKind == ConstructorKind::None, "BuiltinExecutables::createDefaultConstructor should always use a 8-bit string");
    19751975        Parser<Lexer<UChar>> parser(vm, source, builtinMode, strictMode, scriptMode, parseMode, superBinding, defaultConstructorKind, derivedContextType, isEvalNode<ParsedNode>(), evalContextType, debuggerParseData);
    1976         result = parser.parse<ParsedNode>(error, name, parseMode);
     1976        result = parser.parse<ParsedNode>(error, name, parseMode, isEvalNode<ParsedNode>() ? ParsingContext::Eval : ParsingContext::Program);
    19771977        if (positionBeforeLastNewline)
    19781978            *positionBeforeLastNewline = parser.positionBeforeLastNewline();
     
    19881988}
    19891989
     1990inline std::unique_ptr<ProgramNode> parseFunctionForFunctionConstructor(VM& vm, const SourceCode& source, ParserError& error, JSTextPosition* positionBeforeLastNewline, std::optional<int> functionConstructorParametersEndPosition)
     1991{
     1992    ASSERT(!source.provider()->source().isNull());
     1993
     1994    MonotonicTime before;
     1995    if (UNLIKELY(Options::reportParseTimes()))
     1996        before = MonotonicTime::now();
     1997
     1998    Identifier name;
     1999    bool isEvalNode = false;
     2000    std::unique_ptr<ProgramNode> result;
     2001    if (source.provider()->source().is8Bit()) {
     2002        Parser<Lexer<LChar>> parser(&vm, source, JSParserBuiltinMode::NotBuiltin, JSParserStrictMode::NotStrict, JSParserScriptMode::Classic, SourceParseMode::ProgramMode, SuperBinding::NotNeeded, ConstructorKind::None, DerivedContextType::None, isEvalNode, EvalContextType::None, nullptr);
     2003        result = parser.parse<ProgramNode>(error, name, SourceParseMode::ProgramMode, ParsingContext::FunctionConstructor, functionConstructorParametersEndPosition);
     2004        if (positionBeforeLastNewline)
     2005            *positionBeforeLastNewline = parser.positionBeforeLastNewline();
     2006    } else {
     2007        Parser<Lexer<UChar>> parser(&vm, source, JSParserBuiltinMode::NotBuiltin, JSParserStrictMode::NotStrict, JSParserScriptMode::Classic, SourceParseMode::ProgramMode, SuperBinding::NotNeeded, ConstructorKind::None, DerivedContextType::None, isEvalNode, EvalContextType::None, nullptr);
     2008        result = parser.parse<ProgramNode>(error, name, SourceParseMode::ProgramMode, ParsingContext::FunctionConstructor, functionConstructorParametersEndPosition);
     2009        if (positionBeforeLastNewline)
     2010            *positionBeforeLastNewline = parser.positionBeforeLastNewline();
     2011    }
     2012
     2013    if (UNLIKELY(Options::reportParseTimes())) {
     2014        MonotonicTime after = MonotonicTime::now();
     2015        ParseHash hash(source);
     2016        dataLogLn(result ? "Parsed #" : "Failed to parse #", hash.hashForCall(), "/#", hash.hashForConstruct(), " in ", (after - before).milliseconds(), " ms.");
     2017    }
     2018
     2019    return result;
     2020}
     2021
     2022
    19902023} // namespace
Note: See TracChangeset for help on using the changeset viewer.