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.cpp

    r233855 r237054  
    196196
    197197template <typename LexerType>
    198 String Parser<LexerType>::parseInner(const Identifier& calleeName, SourceParseMode parseMode)
     198String Parser<LexerType>::parseInner(const Identifier& calleeName, SourceParseMode parseMode, ParsingContext parsingContext, std::optional<int> functionConstructorParametersEndPosition)
    199199{
    200200    String parseError = String();
     
    240240        else if (isAsyncGeneratorWrapperParseMode(parseMode))
    241241            sourceElements = parseAsyncGeneratorFunctionSourceElements(context, parseMode, isArrowFunctionBodyExpression, CheckForStrictMode);
     242        else if (parsingContext == ParsingContext::FunctionConstructor)
     243            sourceElements = parseSingleFunction(context, functionConstructorParametersEndPosition);
    242244        else
    243245            sourceElements = parseSourceElements(context, CheckForStrictMode);
     
    612614    return sourceElements;
    613615}
     616
     617template <typename LexerType>
     618template <class TreeBuilder> TreeSourceElements Parser<LexerType>::parseSingleFunction(TreeBuilder& context, std::optional<int> functionConstructorParametersEndPosition)
     619{
     620    TreeSourceElements sourceElements = context.createSourceElements();
     621    TreeStatement statement = 0;
     622    switch (m_token.m_type) {
     623    case FUNCTION:
     624        statement = parseFunctionDeclaration(context, ExportType::NotExported, DeclarationDefaultContext::Standard, functionConstructorParametersEndPosition);
     625        break;
     626    case IDENT:
     627        if (*m_token.m_data.ident == m_vm->propertyNames->async && !m_token.m_data.escaped) {
     628            next();
     629            failIfFalse(match(FUNCTION) && !m_lexer->prevTerminator(), "Cannot parse the async function");
     630            statement = parseAsyncFunctionDeclaration(context, ExportType::NotExported, DeclarationDefaultContext::Standard, functionConstructorParametersEndPosition);
     631            break;
     632        }
     633        FALLTHROUGH;
     634    default:
     635        failDueToUnexpectedToken();
     636        break;
     637    }
     638
     639    if (statement) {
     640        context.setEndOffset(statement, m_lastTokenEndPosition.offset);
     641        context.appendStatement(sourceElements, statement);
     642    }
     643
     644    propagateError();
     645    return sourceElements;
     646}
     647
    614648   
    615649template <typename LexerType>
     
    22652299
    22662300template <typename LexerType>
    2267 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)
     2301template <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, std::optional<int> functionConstructorParametersEndPosition)
    22682302{
    22692303    RELEASE_ASSERT(isFunctionParseMode(mode));
     
    24612495       
    24622496        matchOrFail(OPENBRACE, "Expected an opening '{' at the start of a ", stringForFunctionMode(mode), " body");
     2497
     2498        // If the code is invoked from function constructor, we need to ensure that parameters are only composed by the string offered as parameters.
     2499        if (functionConstructorParametersEndPosition)
     2500            semanticFailIfFalse(lastTokenEndPosition().offset == *functionConstructorParametersEndPosition, "Parameters should match arguments offered as parameters in Function constructor");
    24632501       
    24642502        // BytecodeGenerator emits code to throw TypeError when a class constructor is "call"ed.
     
    25952633
    25962634template <typename LexerType>
    2597 template <class TreeBuilder> TreeStatement Parser<LexerType>::parseFunctionDeclaration(TreeBuilder& context, ExportType exportType, DeclarationDefaultContext declarationDefaultContext)
     2635template <class TreeBuilder> TreeStatement Parser<LexerType>::parseFunctionDeclaration(TreeBuilder& context, ExportType exportType, DeclarationDefaultContext declarationDefaultContext, std::optional<int> functionConstructorParametersEndPosition)
    25982636{
    25992637    ASSERT(match(FUNCTION));
     
    26322670    }
    26332671
    2634     failIfFalse((parseFunctionInfo(context, requirements, parseMode, true, ConstructorKind::None, SuperBinding::NotNeeded, functionKeywordStart, functionInfo, FunctionDefinitionType::Declaration)), "Cannot parse this function");
     2672    failIfFalse((parseFunctionInfo(context, requirements, parseMode, true, ConstructorKind::None, SuperBinding::NotNeeded, functionKeywordStart, functionInfo, FunctionDefinitionType::Declaration, functionConstructorParametersEndPosition)), "Cannot parse this function");
    26352673    ASSERT(functionInfo.name);
    26362674
     
    26532691
    26542692template <typename LexerType>
    2655 template <class TreeBuilder> TreeStatement Parser<LexerType>::parseAsyncFunctionDeclaration(TreeBuilder& context, ExportType exportType, DeclarationDefaultContext declarationDefaultContext)
     2693template <class TreeBuilder> TreeStatement Parser<LexerType>::parseAsyncFunctionDeclaration(TreeBuilder& context, ExportType exportType, DeclarationDefaultContext declarationDefaultContext, std::optional<int> functionConstructorParametersEndPosition)
    26562694{
    26572695    ASSERT(match(FUNCTION));
     
    26902728    }
    26912729
    2692     failIfFalse((parseFunctionInfo(context, requirements, parseMode, true, ConstructorKind::None, SuperBinding::NotNeeded, functionKeywordStart, functionInfo, FunctionDefinitionType::Declaration)), "Cannot parse this async function");
     2730    failIfFalse((parseFunctionInfo(context, requirements, parseMode, true, ConstructorKind::None, SuperBinding::NotNeeded, functionKeywordStart, functionInfo, FunctionDefinitionType::Declaration, functionConstructorParametersEndPosition)), "Cannot parse this async function");
    26932731    failIfFalse(functionInfo.name, "Async function statements must have a name");
    26942732
Note: See TracChangeset for help on using the changeset viewer.