Title: [237054] trunk
Revision
237054
Author
[email protected]
Date
2018-10-11 16:43:58 -0700 (Thu, 11 Oct 2018)

Log 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/_javascript_Core:

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:

Modified Paths

Added Paths

Diff

Modified: trunk/JSTests/ChakraCore/test/Function/FuncBodyES5.baseline-jsc (237053 => 237054)


--- trunk/JSTests/ChakraCore/test/Function/FuncBodyES5.baseline-jsc	2018-10-11 23:41:18 UTC (rev 237053)
+++ trunk/JSTests/ChakraCore/test/Function/FuncBodyES5.baseline-jsc	2018-10-11 23:43:58 UTC (rev 237054)
@@ -9,13 +9,13 @@
 PASS: 8: new Function succeeded as expected
 PASS: 9: new Function succeeded as expected
 PASS: 10: new Function succeeded as expected
-PASS: 100: new Function failed as expected. SyntaxError: Unexpected token '{'. Expected ')' to end a compound _expression_.
-PASS: 100: new Function failed as expected. SyntaxError: Unexpected token '{'. Expected ')' to end a compound _expression_.
-PASS: 101: new Function failed as expected. SyntaxError: Unexpected keyword 'function'. Expected ')' to end a compound _expression_.
-PASS: 102: new Function failed as expected. SyntaxError: Unexpected keyword 'function'. Expected ')' to end a compound _expression_.
-PASS: 103: new Function failed as expected. SyntaxError: Unexpected keyword 'function'. Expected ')' to end a compound _expression_.
-PASS: 104: new Function failed as expected. SyntaxError: Unexpected token ';'. Expected ')' to end a compound _expression_.
-PASS: 105: new Function failed as expected. SyntaxError: Unexpected token ';'. Expected ')' to end a compound _expression_.
+PASS: 100: new Function failed as expected. SyntaxError: Parser error
+PASS: 100: new Function failed as expected. SyntaxError: Parser error
+PASS: 101: new Function failed as expected. SyntaxError: Parser error
+PASS: 102: new Function failed as expected. SyntaxError: Parameters should match arguments offered as parameters in Function constructor.
+PASS: 103: new Function failed as expected. SyntaxError: Parser error
+PASS: 104: new Function failed as expected. SyntaxError: Parameters should match arguments offered as parameters in Function constructor.
+PASS: 105: new Function failed as expected. SyntaxError: Parser error
 PASS: 200: new Function failed as expected. SyntaxError: Unexpected token ','. Expected a parameter pattern or a ')' in parameter list.
 PASS: 200: new Function failed as expected. SyntaxError: Unexpected token ','. Expected a parameter pattern or a ')' in parameter list.
 PASS: 201: new Function failed as expected. SyntaxError: Unexpected token ','. Expected a parameter pattern or a ')' in parameter list.

Modified: trunk/JSTests/ChangeLog (237053 => 237054)


--- trunk/JSTests/ChangeLog	2018-10-11 23:41:18 UTC (rev 237053)
+++ trunk/JSTests/ChangeLog	2018-10-11 23:43:58 UTC (rev 237054)
@@ -1,3 +1,28 @@
+2018-10-08  Yusuke Suzuki  <[email protected]>
+
+        [JSC] JSC should have "parseFunction" to optimize Function constructor
+        https://bugs.webkit.org/show_bug.cgi?id=190340
+
+        Reviewed by Mark Lam.
+
+        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:
+
 2018-10-10  Guillaume Emont  <[email protected]>
 
         Skip JSC test stress/sampling-profiler-richards.js on armv7/linux

Added: trunk/JSTests/stress/function-cache-with-parameters-end-position.js (0 => 237054)


--- trunk/JSTests/stress/function-cache-with-parameters-end-position.js	                        (rev 0)
+++ trunk/JSTests/stress/function-cache-with-parameters-end-position.js	2018-10-11 23:43:58 UTC (rev 237054)
@@ -0,0 +1,40 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function shouldThrow(func, errorMessage) {
+    var errorThrown = false;
+    var error = null;
+    try {
+        func();
+    } catch (e) {
+        errorThrown = true;
+        error = e;
+    }
+    if (!errorThrown)
+        throw new Error('not thrown');
+    if (String(error) !== errorMessage)
+        throw new Error(`bad error: ${String(error)}`);
+}
+
+for (var i = 0; i < 10; ++i) {
+    var f = Function('/*) {\n*/', 'return 42');
+    shouldBe(f.toString(),
+`function anonymous(/*) {
+*/) {
+return 42
+}`);
+}
+shouldThrow(() => Function('/*', '*/){\nreturn 42'), `SyntaxError: Parameters should match arguments offered as parameters in Function constructor.`);
+
+shouldThrow(() => Function('/*', '*/){\nreturn 43'), `SyntaxError: Parameters should match arguments offered as parameters in Function constructor.`);
+for (var i = 0; i < 10; ++i) {
+    var f = Function('/*) {\n*/', 'return 43');
+    shouldBe(f.toString(),
+`function anonymous(/*) {
+*/) {
+return 43
+}`);
+}
+

Added: trunk/JSTests/stress/function-constructor-name.js (0 => 237054)


--- trunk/JSTests/stress/function-constructor-name.js	                        (rev 0)
+++ trunk/JSTests/stress/function-constructor-name.js	2018-10-11 23:43:58 UTC (rev 237054)
@@ -0,0 +1,36 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+var GeneratorFunction = function*(){}.constructor;
+var AsyncFunction = async function(){}.constructor;
+var AsyncGeneratorFunction = async function*(){}.constructor;
+
+var f = Function(`return 42`);
+shouldBe(typeof anonymous, `undefined`);
+shouldBe(f.toString(),
+`function anonymous() {
+return 42
+}`);
+
+var gf = GeneratorFunction(`return 42`);
+shouldBe(typeof anonymous, `undefined`);
+shouldBe(gf.toString(),
+`function* anonymous() {
+return 42
+}`);
+
+var af = AsyncFunction(`return 42`);
+shouldBe(typeof anonymous, `undefined`);
+shouldBe(af.toString(),
+`async function anonymous() {
+return 42
+}`);
+
+var agf = AsyncGeneratorFunction(`return 42`);
+shouldBe(typeof anonymous, `undefined`);
+shouldBe(agf.toString(),
+`async function* anonymous() {
+return 42
+}`);

Modified: trunk/JSTests/test262/expectations.yaml (237053 => 237054)


--- trunk/JSTests/test262/expectations.yaml	2018-10-11 23:41:18 UTC (rev 237053)
+++ trunk/JSTests/test262/expectations.yaml	2018-10-11 23:43:58 UTC (rev 237054)
@@ -900,14 +900,14 @@
   default: "ReferenceError: Can't find variable: verifyNotEnumerable"
   strict mode: "ReferenceError: Can't find variable: verifyNotEnumerable"
 test/built-ins/Function/prototype/toString/AsyncFunction.js:
-  default: "SyntaxError: Unexpected token '}'. Expected a ')' or a ',' after a parameter declaration."
-  strict mode: "SyntaxError: Unexpected token '}'. Expected a ')' or a ',' after a parameter declaration."
+  default: "SyntaxError: Unexpected token ';'. Expected a ')' or a ',' after a parameter declaration."
+  strict mode: "SyntaxError: Unexpected token ';'. Expected a ')' or a ',' after a parameter declaration."
 test/built-ins/Function/prototype/toString/Function.js:
-  default: "SyntaxError: Unexpected token '}'. Expected a ')' or a ',' after a parameter declaration."
-  strict mode: "SyntaxError: Unexpected token '}'. Expected a ')' or a ',' after a parameter declaration."
+  default: "SyntaxError: Unexpected token ';'. Expected a ')' or a ',' after a parameter declaration."
+  strict mode: "SyntaxError: Unexpected token ';'. Expected a ')' or a ',' after a parameter declaration."
 test/built-ins/Function/prototype/toString/GeneratorFunction.js:
-  default: "SyntaxError: Unexpected token '}'. Expected a ')' or a ',' after a parameter declaration."
-  strict mode: "SyntaxError: Unexpected token '}'. Expected a ')' or a ',' after a parameter declaration."
+  default: "SyntaxError: Unexpected keyword 'yield'. Expected a ')' or a ',' after a parameter declaration."
+  strict mode: "SyntaxError: Unexpected keyword 'yield'. Expected a ')' or a ',' after a parameter declaration."
 test/built-ins/Function/prototype/toString/async-arrow-function.js:
   default: "Test262Error: Conforms to NativeFunction Syntax: 'async ( /* b */ a /* c */ , /* d */ b /* e */ ) /* f */ => /* g */ { /* h */ ; /* i */ }'.(async /* a */ ( /* b */ a /* c */ , /* d */ b /* e */ ) /* f */ => /* g */ { /* h */ ; /* i */ })"
   strict mode: "Test262Error: Conforms to NativeFunction Syntax: 'async ( /* b */ a /* c */ , /* d */ b /* e */ ) /* f */ => /* g */ { /* h */ ; /* i */ }'.(async /* a */ ( /* b */ a /* c */ , /* d */ b /* e */ ) /* f */ => /* g */ { /* h */ ; /* i */ })"

Modified: trunk/LayoutTests/ChangeLog (237053 => 237054)


--- trunk/LayoutTests/ChangeLog	2018-10-11 23:41:18 UTC (rev 237053)
+++ trunk/LayoutTests/ChangeLog	2018-10-11 23:43:58 UTC (rev 237054)
@@ -1,3 +1,16 @@
+2018-10-08  Yusuke Suzuki  <[email protected]>
+
+        [JSC] JSC should have "parseFunction" to optimize Function constructor
+        https://bugs.webkit.org/show_bug.cgi?id=190340
+
+        Reviewed by Mark Lam.
+
+        * 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:
+
 2018-10-11  Thibault Saunier  <[email protected]>
 
         [GStreamer] Support arbitrary video resolution in getUserMedia API

Modified: trunk/LayoutTests/fast/dom/attribute-event-listener-errors-expected.txt (237053 => 237054)


--- trunk/LayoutTests/fast/dom/attribute-event-listener-errors-expected.txt	2018-10-11 23:41:18 UTC (rev 237053)
+++ trunk/LayoutTests/fast/dom/attribute-event-listener-errors-expected.txt	2018-10-11 23:43:58 UTC (rev 237054)
@@ -1,4 +1,4 @@
 CONSOLE MESSAGE: line 4: ReferenceError: Can't find variable: error
-CONSOLE MESSAGE: line 9: SyntaxError: Invalid character: '@'
+CONSOLE MESSAGE: line 5: SyntaxError: Invalid character: '@'
 This test verifies that an attribute event listener error shows the right line number even if the attribute contains newlines.
   

Modified: trunk/LayoutTests/fast/events/attribute-listener-deletion-crash-expected.txt (237053 => 237054)


--- trunk/LayoutTests/fast/events/attribute-listener-deletion-crash-expected.txt	2018-10-11 23:41:18 UTC (rev 237053)
+++ trunk/LayoutTests/fast/events/attribute-listener-deletion-crash-expected.txt	2018-10-11 23:43:58 UTC (rev 237054)
@@ -1,21 +1,21 @@
-CONSOLE MESSAGE: line 2: SyntaxError: Unexpected token '|'
-CONSOLE MESSAGE: line 2: SyntaxError: Unexpected token '|'
-CONSOLE MESSAGE: line 2: SyntaxError: Unexpected token '|'
-CONSOLE MESSAGE: line 2: SyntaxError: Unexpected token '|'
-CONSOLE MESSAGE: line 2: SyntaxError: Unexpected token '|'
-CONSOLE MESSAGE: line 2: SyntaxError: Unexpected token '|'
-CONSOLE MESSAGE: line 2: SyntaxError: Unexpected token '|'
-CONSOLE MESSAGE: line 2: SyntaxError: Unexpected token '|'
-CONSOLE MESSAGE: line 2: SyntaxError: Unexpected token '|'
-CONSOLE MESSAGE: line 2: SyntaxError: Unexpected token '|'
-CONSOLE MESSAGE: line 2: SyntaxError: Unexpected token '|'
-CONSOLE MESSAGE: line 2: SyntaxError: Unexpected token '|'
-CONSOLE MESSAGE: line 2: SyntaxError: Unexpected token '|'
-CONSOLE MESSAGE: line 2: SyntaxError: Unexpected token '|'
-CONSOLE MESSAGE: line 2: SyntaxError: Unexpected token '|'
-CONSOLE MESSAGE: line 2: SyntaxError: Unexpected token '|'
-CONSOLE MESSAGE: line 2: SyntaxError: Unexpected token '|'
-CONSOLE MESSAGE: line 2: SyntaxError: Unexpected token '|'
-CONSOLE MESSAGE: line 2: SyntaxError: Unexpected token '|'
-CONSOLE MESSAGE: line 2: SyntaxError: Unexpected token '|'
+CONSOLE MESSAGE: line 1: SyntaxError: Unexpected token '|'
+CONSOLE MESSAGE: line 1: SyntaxError: Unexpected token '|'
+CONSOLE MESSAGE: line 1: SyntaxError: Unexpected token '|'
+CONSOLE MESSAGE: line 1: SyntaxError: Unexpected token '|'
+CONSOLE MESSAGE: line 1: SyntaxError: Unexpected token '|'
+CONSOLE MESSAGE: line 1: SyntaxError: Unexpected token '|'
+CONSOLE MESSAGE: line 1: SyntaxError: Unexpected token '|'
+CONSOLE MESSAGE: line 1: SyntaxError: Unexpected token '|'
+CONSOLE MESSAGE: line 1: SyntaxError: Unexpected token '|'
+CONSOLE MESSAGE: line 1: SyntaxError: Unexpected token '|'
+CONSOLE MESSAGE: line 1: SyntaxError: Unexpected token '|'
+CONSOLE MESSAGE: line 1: SyntaxError: Unexpected token '|'
+CONSOLE MESSAGE: line 1: SyntaxError: Unexpected token '|'
+CONSOLE MESSAGE: line 1: SyntaxError: Unexpected token '|'
+CONSOLE MESSAGE: line 1: SyntaxError: Unexpected token '|'
+CONSOLE MESSAGE: line 1: SyntaxError: Unexpected token '|'
+CONSOLE MESSAGE: line 1: SyntaxError: Unexpected token '|'
+CONSOLE MESSAGE: line 1: SyntaxError: Unexpected token '|'
+CONSOLE MESSAGE: line 1: SyntaxError: Unexpected token '|'
+CONSOLE MESSAGE: line 1: SyntaxError: Unexpected token '|'
 PASS

Modified: trunk/LayoutTests/fast/events/window-onerror-syntax-error-in-attr-expected.txt (237053 => 237054)


--- trunk/LayoutTests/fast/events/window-onerror-syntax-error-in-attr-expected.txt	2018-10-11 23:41:18 UTC (rev 237053)
+++ trunk/LayoutTests/fast/events/window-onerror-syntax-error-in-attr-expected.txt	2018-10-11 23:43:58 UTC (rev 237054)
@@ -1,6 +1,6 @@
 Test that window.onerror is called on window object when there is a syntax error in attribute handler. Bug 70991.
 
-Main frame window.onerror: SyntaxError: Unexpected token '%' at window-onerror-syntax-error-in-attr.html:11:38 SyntaxError: Unexpected token '%'
-Main frame window.onerror: SyntaxError: Unexpected token '%' at window-onerror-syntax-error-in-attr.html:37:38 SyntaxError: Unexpected token '%'
-Main frame window.onerror: SyntaxError: Unexpected token '%' at window-onerror-syntax-error-in-attr.html:37:14 SyntaxError: Unexpected token '%'
+Main frame window.onerror: SyntaxError: Unexpected token '%' at window-onerror-syntax-error-in-attr.html:10:38 SyntaxError: Unexpected token '%'
+Main frame window.onerror: SyntaxError: Unexpected token '%' at window-onerror-syntax-error-in-attr.html:36:38 SyntaxError: Unexpected token '%'
+Main frame window.onerror: SyntaxError: Unexpected token '%' at window-onerror-syntax-error-in-attr.html:36:14 SyntaxError: Unexpected token '%'
 Button 1 Button 2 Button 3

Modified: trunk/LayoutTests/imported/w3c/ChangeLog (237053 => 237054)


--- trunk/LayoutTests/imported/w3c/ChangeLog	2018-10-11 23:41:18 UTC (rev 237053)
+++ trunk/LayoutTests/imported/w3c/ChangeLog	2018-10-11 23:43:58 UTC (rev 237054)
@@ -1,3 +1,15 @@
+2018-10-08  Yusuke Suzuki  <[email protected]>
+
+        [JSC] JSC should have "parseFunction" to optimize Function constructor
+        https://bugs.webkit.org/show_bug.cgi?id=190340
+
+        Reviewed by Mark Lam.
+
+        * 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:
+
 2018-10-10  Chris Dumez  <[email protected]>
 
         Unreviewed, rolling out r236802.

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/html/webappapis/scripting/events/inline-event-handler-ordering-expected.txt (237053 => 237054)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/html/webappapis/scripting/events/inline-event-handler-ordering-expected.txt	2018-10-11 23:41:18 UTC (rev 237053)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/html/webappapis/scripting/events/inline-event-handler-ordering-expected.txt	2018-10-11 23:43:58 UTC (rev 237054)
@@ -1,5 +1,5 @@
 CONSOLE MESSAGE: line 19: SyntaxError: Unexpected token '}'
-CONSOLE MESSAGE: line 54: SyntaxError: Unexpected token '}'
+CONSOLE MESSAGE: line 52: SyntaxError: Unexpected token '}'
 
 PASS Inline event handlers retain their ordering when invalid and force-compiled 
 PASS Inline event handlers retain their ordering when invalid and force-compiled via dispatch 

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/html/webappapis/scripting/events/invalid-uncompiled-raw-handler-compiled-late-expected.txt (237053 => 237054)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/html/webappapis/scripting/events/invalid-uncompiled-raw-handler-compiled-late-expected.txt	2018-10-11 23:41:18 UTC (rev 237053)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/html/webappapis/scripting/events/invalid-uncompiled-raw-handler-compiled-late-expected.txt	2018-10-11 23:43:58 UTC (rev 237054)
@@ -1,5 +1,5 @@
-CONSOLE MESSAGE: line 26: SyntaxError: Unexpected token '}'. Expected ')' to end a compound _expression_.
-CONSOLE MESSAGE: line 21: SyntaxError: Unexpected token '}'. Expected ')' to end a compound _expression_.
+CONSOLE MESSAGE: line 24: SyntaxError: Parser error
+CONSOLE MESSAGE: line 21: SyntaxError: Parser error
 
 FAIL Invalid uncompiled raw handlers should only be compiled when about to call them. assert_array_equals: lengths differ, expected 3 got 4
 

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/html/webappapis/scripting/processing-model-2/compile-error-in-attribute-expected.txt (237053 => 237054)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/html/webappapis/scripting/processing-model-2/compile-error-in-attribute-expected.txt	2018-10-11 23:41:18 UTC (rev 237053)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/html/webappapis/scripting/processing-model-2/compile-error-in-attribute-expected.txt	2018-10-11 23:43:58 UTC (rev 237054)
@@ -1,4 +1,4 @@
-CONSOLE MESSAGE: line 26: SyntaxError: Unexpected token ')'
+CONSOLE MESSAGE: line 24: SyntaxError: Unexpected end of script
 
 PASS window.onerror - compile error in attribute 
 PASS window.onerror - compile error in attribute (column) 

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/html/webappapis/scripting/processing-model-2/compile-error-in-body-onerror-expected.txt (237053 => 237054)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/html/webappapis/scripting/processing-model-2/compile-error-in-body-onerror-expected.txt	2018-10-11 23:41:18 UTC (rev 237053)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/html/webappapis/scripting/processing-model-2/compile-error-in-body-onerror-expected.txt	2018-10-11 23:43:58 UTC (rev 237054)
@@ -1,5 +1,5 @@
 CONSOLE MESSAGE: line 19: SyntaxError: Unexpected token ')'
-CONSOLE MESSAGE: line 18: SyntaxError: Unexpected token ')'
+CONSOLE MESSAGE: line 16: SyntaxError: Unexpected end of script
 
 PASS window.onerror - compile error in <body onerror> 
 

Modified: trunk/LayoutTests/js/dom/invalid-syntax-for-function-expected.txt (237053 => 237054)


--- trunk/LayoutTests/js/dom/invalid-syntax-for-function-expected.txt	2018-10-11 23:41:18 UTC (rev 237053)
+++ trunk/LayoutTests/js/dom/invalid-syntax-for-function-expected.txt	2018-10-11 23:43:58 UTC (rev 237054)
@@ -1,3 +1,3 @@
-CONSOLE MESSAGE: line 2: SyntaxError: Invalid character: '#'
+CONSOLE MESSAGE: line 1: SyntaxError: Invalid character: '#'
 This test ensures we don't crash when we are given garbage for an attribute expecting a function.
 https://bugs.webkit.org/show_bug.cgi?id=19025

Modified: trunk/LayoutTests/js/dom/script-start-end-locations-expected.txt (237053 => 237054)


--- trunk/LayoutTests/js/dom/script-start-end-locations-expected.txt	2018-10-11 23:41:18 UTC (rev 237053)
+++ trunk/LayoutTests/js/dom/script-start-end-locations-expected.txt	2018-10-11 23:43:58 UTC (rev 237054)
@@ -243,32 +243,32 @@
 eval { 1:1 - 1:56 }
 
   new Function Object:
-function "anonymous" { 1:20 - 2:228 }
+function "anonymous" { 1:19 - 2:228 }
 function "nf1a" { 2:57 - 2:219 }
 function "nf1b" { 2:87 - 2:209 }
 function "nf1c" { 2:117 - 2:199 }
 eval { 1:1 - 1:56 }
-function "anonymous" { 1:20 - 18:8 }
+function "anonymous" { 1:19 - 18:8 }
 function "nf2a" { 5:18 - 16:5 }
 function "nf2b" { 7:22 - 14:9 }
 function "nf2c" { 9:26 - 12:13 }
 eval { 1:1 - 1:56 }
-function "anonymous" { 1:20 - 2:228 }
+function "anonymous" { 1:19 - 2:228 }
 function "nf1a" { 2:57 - 2:219 }
 function "nf1b" { 2:87 - 2:209 }
 function "nf1c" { 2:117 - 2:199 }
 eval { 1:1 - 1:56 }
-function "anonymous" { 1:20 - 2:237 }
+function "anonymous" { 1:19 - 2:237 }
 function "nfi1a" { 2:58 - 2:227 }
 function "nfi1b" { 2:90 - 2:216 }
 function "nfi1c" { 2:122 - 2:205 }
 eval { 1:1 - 1:56 }
-function "anonymous" { 1:20 - 18:8 }
+function "anonymous" { 1:19 - 18:8 }
 function "nf2a" { 5:18 - 16:5 }
 function "nf2b" { 7:22 - 14:9 }
 function "nf2c" { 9:26 - 12:13 }
 eval { 1:1 - 1:56 }
-function "anonymous" { 1:20 - 18:9 }
+function "anonymous" { 1:19 - 18:9 }
 function "nfi2a" { 5:19 - 16:5 }
 function "nfi2b" { 7:23 - 14:9 }
 function "nfi2c" { 9:27 - 12:13 }

Modified: trunk/Source/_javascript_Core/ChangeLog (237053 => 237054)


--- trunk/Source/_javascript_Core/ChangeLog	2018-10-11 23:41:18 UTC (rev 237053)
+++ trunk/Source/_javascript_Core/ChangeLog	2018-10-11 23:43:58 UTC (rev 237054)
@@ -1,3 +1,78 @@
+2018-10-08  Yusuke Suzuki  <[email protected]>
+
+        [JSC] JSC should have "parseFunction" to optimize Function constructor
+        https://bugs.webkit.org/show_bug.cgi?id=190340
+
+        Reviewed by Mark Lam.
+
+        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:
+
 2018-10-11  Ross Kirsling  <[email protected]>
 
         Fix non-existent define `CPU(JSVALUE64)`

Modified: trunk/Source/_javascript_Core/bytecode/UnlinkedFunctionExecutable.cpp (237053 => 237054)


--- trunk/Source/_javascript_Core/bytecode/UnlinkedFunctionExecutable.cpp	2018-10-11 23:41:18 UTC (rev 237053)
+++ trunk/Source/_javascript_Core/bytecode/UnlinkedFunctionExecutable.cpp	2018-10-11 23:43:58 UTC (rev 237054)
@@ -174,7 +174,7 @@
 
 UnlinkedFunctionExecutable* UnlinkedFunctionExecutable::fromGlobalCode(
     const Identifier& name, ExecState& exec, const SourceCode& source, 
-    JSObject*& exception, int overrideLineNumber)
+    JSObject*& exception, int overrideLineNumber, std::optional<int> functionConstructorParametersEndPosition)
 {
     ParserError error;
     VM& vm = exec.vm();
@@ -181,7 +181,7 @@
     auto& globalObject = *exec.lexicalGlobalObject();
     CodeCache* codeCache = vm.codeCache();
     DebuggerMode debuggerMode = globalObject.hasInteractiveDebugger() ? DebuggerOn : DebuggerOff;
-    UnlinkedFunctionExecutable* executable = codeCache->getUnlinkedGlobalFunctionExecutable(vm, name, source, debuggerMode, error);
+    UnlinkedFunctionExecutable* executable = codeCache->getUnlinkedGlobalFunctionExecutable(vm, name, source, debuggerMode, functionConstructorParametersEndPosition, error);
 
     if (globalObject.hasDebugger())
         globalObject.debugger()->sourceParsed(&exec, source.provider(), error.line(), error.message());

Modified: trunk/Source/_javascript_Core/bytecode/UnlinkedFunctionExecutable.h (237053 => 237054)


--- trunk/Source/_javascript_Core/bytecode/UnlinkedFunctionExecutable.h	2018-10-11 23:41:18 UTC (rev 237053)
+++ trunk/Source/_javascript_Core/bytecode/UnlinkedFunctionExecutable.h	2018-10-11 23:43:58 UTC (rev 237054)
@@ -107,7 +107,7 @@
 
     static UnlinkedFunctionExecutable* fromGlobalCode(
         const Identifier&, ExecState&, const SourceCode&, JSObject*& exception, 
-        int overrideLineNumber);
+        int overrideLineNumber, std::optional<int> functionConstructorParametersEndPosition);
 
     JS_EXPORT_PRIVATE FunctionExecutable* link(VM&, const SourceCode& parentSource, std::optional<int> overrideLineNumber = std::nullopt, Intrinsic = NoIntrinsic);
 

Modified: trunk/Source/_javascript_Core/parser/Parser.cpp (237053 => 237054)


--- trunk/Source/_javascript_Core/parser/Parser.cpp	2018-10-11 23:41:18 UTC (rev 237053)
+++ trunk/Source/_javascript_Core/parser/Parser.cpp	2018-10-11 23:43:58 UTC (rev 237054)
@@ -195,7 +195,7 @@
 }
 
 template <typename LexerType>
-String Parser<LexerType>::parseInner(const Identifier& calleeName, SourceParseMode parseMode)
+String Parser<LexerType>::parseInner(const Identifier& calleeName, SourceParseMode parseMode, ParsingContext parsingContext, std::optional<int> functionConstructorParametersEndPosition)
 {
     String parseError = String();
 
@@ -239,6 +239,8 @@
             sourceElements = parseGeneratorFunctionSourceElements(context, calleeName, CheckForStrictMode);
         else if (isAsyncGeneratorWrapperParseMode(parseMode))
             sourceElements = parseAsyncGeneratorFunctionSourceElements(context, parseMode, isArrowFunctionBodyExpression, CheckForStrictMode);
+        else if (parsingContext == ParsingContext::FunctionConstructor)
+            sourceElements = parseSingleFunction(context, functionConstructorParametersEndPosition);
         else
             sourceElements = parseSourceElements(context, CheckForStrictMode);
     }
@@ -611,6 +613,38 @@
         
     return sourceElements;
 }
+
+template <typename LexerType>
+template <class TreeBuilder> TreeSourceElements Parser<LexerType>::parseSingleFunction(TreeBuilder& context, std::optional<int> functionConstructorParametersEndPosition)
+{
+    TreeSourceElements sourceElements = context.createSourceElements();
+    TreeStatement statement = 0;
+    switch (m_token.m_type) {
+    case FUNCTION:
+        statement = parseFunctionDeclaration(context, ExportType::NotExported, DeclarationDefaultContext::Standard, functionConstructorParametersEndPosition);
+        break;
+    case IDENT:
+        if (*m_token.m_data.ident == m_vm->propertyNames->async && !m_token.m_data.escaped) {
+            next();
+            failIfFalse(match(FUNCTION) && !m_lexer->prevTerminator(), "Cannot parse the async function");
+            statement = parseAsyncFunctionDeclaration(context, ExportType::NotExported, DeclarationDefaultContext::Standard, functionConstructorParametersEndPosition);
+            break;
+        }
+        FALLTHROUGH;
+    default:
+        failDueToUnexpectedToken();
+        break;
+    }
+
+    if (statement) {
+        context.setEndOffset(statement, m_lastTokenEndPosition.offset);
+        context.appendStatement(sourceElements, statement);
+    }
+
+    propagateError();
+    return sourceElements;
+}
+
     
 template <typename LexerType>
 template <class TreeBuilder> TreeStatement Parser<LexerType>::parseStatementListItem(TreeBuilder& context, const Identifier*& directive, unsigned* directiveLiteralLength)
@@ -2264,7 +2298,7 @@
 }
 
 template <typename LexerType>
-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)
+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, std::optional<int> functionConstructorParametersEndPosition)
 {
     RELEASE_ASSERT(isFunctionParseMode(mode));
 
@@ -2460,6 +2494,10 @@
         }
         
         matchOrFail(OPENBRACE, "Expected an opening '{' at the start of a ", stringForFunctionMode(mode), " body");
+
+        // If the code is invoked from function constructor, we need to ensure that parameters are only composed by the string offered as parameters.
+        if (functionConstructorParametersEndPosition)
+            semanticFailIfFalse(lastTokenEndPosition().offset == *functionConstructorParametersEndPosition, "Parameters should match arguments offered as parameters in Function constructor");
         
         // BytecodeGenerator emits code to throw TypeError when a class constructor is "call"ed.
         // Set ConstructorKind to None for non-constructor methods of classes.
@@ -2594,7 +2632,7 @@
 static FunctionMetadataNode* getMetadata(ParserFunctionInfo<ASTBuilder>& info) { return info.body; }
 
 template <typename LexerType>
-template <class TreeBuilder> TreeStatement Parser<LexerType>::parseFunctionDeclaration(TreeBuilder& context, ExportType exportType, DeclarationDefaultContext declarationDefaultContext)
+template <class TreeBuilder> TreeStatement Parser<LexerType>::parseFunctionDeclaration(TreeBuilder& context, ExportType exportType, DeclarationDefaultContext declarationDefaultContext, std::optional<int> functionConstructorParametersEndPosition)
 {
     ASSERT(match(FUNCTION));
     JSTokenLocation location(tokenLocation());
@@ -2631,7 +2669,7 @@
         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");
+    failIfFalse((parseFunctionInfo(context, requirements, parseMode, true, ConstructorKind::None, SuperBinding::NotNeeded, functionKeywordStart, functionInfo, FunctionDefinitionType::Declaration, functionConstructorParametersEndPosition)), "Cannot parse this function");
     ASSERT(functionInfo.name);
 
     std::pair<DeclarationResultMask, ScopeRef> functionDeclaration = declareFunction(functionInfo.name);
@@ -2652,7 +2690,7 @@
 }
 
 template <typename LexerType>
-template <class TreeBuilder> TreeStatement Parser<LexerType>::parseAsyncFunctionDeclaration(TreeBuilder& context, ExportType exportType, DeclarationDefaultContext declarationDefaultContext)
+template <class TreeBuilder> TreeStatement Parser<LexerType>::parseAsyncFunctionDeclaration(TreeBuilder& context, ExportType exportType, DeclarationDefaultContext declarationDefaultContext, std::optional<int> functionConstructorParametersEndPosition)
 {
     ASSERT(match(FUNCTION));
     JSTokenLocation location(tokenLocation());
@@ -2689,7 +2727,7 @@
         functionInfo.name = &m_vm->propertyNames->builtinNames().starDefaultPrivateName();
     }
 
-    failIfFalse((parseFunctionInfo(context, requirements, parseMode, true, ConstructorKind::None, SuperBinding::NotNeeded, functionKeywordStart, functionInfo, FunctionDefinitionType::Declaration)), "Cannot parse this async function");
+    failIfFalse((parseFunctionInfo(context, requirements, parseMode, true, ConstructorKind::None, SuperBinding::NotNeeded, functionKeywordStart, functionInfo, FunctionDefinitionType::Declaration, functionConstructorParametersEndPosition)), "Cannot parse this async function");
     failIfFalse(functionInfo.name, "Async function statements must have a name");
 
     std::pair<DeclarationResultMask, ScopeRef> functionDeclaration = declareFunction(functionInfo.name);

Modified: trunk/Source/_javascript_Core/parser/Parser.h (237053 => 237054)


--- trunk/Source/_javascript_Core/parser/Parser.h	2018-10-11 23:41:18 UTC (rev 237053)
+++ trunk/Source/_javascript_Core/parser/Parser.h	2018-10-11 23:43:58 UTC (rev 237054)
@@ -859,10 +859,8 @@
     unsigned m_index;
 };
 
-enum class ArgumentType {
-    Normal,
-    Spread
-};
+enum class ArgumentType { Normal, Spread };
+enum class ParsingContext { Program, FunctionConstructor, Eval };
 
 template <typename LexerType>
 class Parser {
@@ -874,7 +872,7 @@
     ~Parser();
 
     template <class ParsedNode>
-    std::unique_ptr<ParsedNode> parse(ParserError&, const Identifier&, SourceParseMode);
+    std::unique_ptr<ParsedNode> parse(ParserError&, const Identifier&, SourceParseMode, ParsingContext, std::optional<int> functionConstructorParametersEndPosition = std::nullopt);
 
     JSTextPosition positionBeforeLastNewline() const { return m_lexer->positionBeforeLastNewline(); }
     JSTokenLocation locationBeforeLastToken() const { return m_lexer->lastTokenLocation(); }
@@ -1312,8 +1310,9 @@
     }
 
     Parser();
-    String parseInner(const Identifier&, SourceParseMode);
 
+    String parseInner(const Identifier&, SourceParseMode, ParsingContext, std::optional<int> functionConstructorParametersEndPosition = std::nullopt);
+
     void didFinishParsing(SourceElements*, DeclarationStacks::FunctionStack&&, VariableEnvironment&, UniquedStringImplPtrSet&&, CodeFeatures, int);
 
     // Used to determine type of error to report.
@@ -1524,13 +1523,14 @@
     template <class TreeBuilder> TreeSourceElements parseGeneratorFunctionSourceElements(TreeBuilder&, const Identifier& name, SourceElementsMode);
     template <class TreeBuilder> TreeSourceElements parseAsyncFunctionSourceElements(TreeBuilder&, SourceParseMode, bool isArrowFunctionBodyExpression, SourceElementsMode);
     template <class TreeBuilder> TreeSourceElements parseAsyncGeneratorFunctionSourceElements(TreeBuilder&, SourceParseMode, bool isArrowFunctionBodyExpression, SourceElementsMode);
+    template <class TreeBuilder> TreeSourceElements parseSingleFunction(TreeBuilder&, std::optional<int> functionConstructorParametersEndPosition);
     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, DeclarationDefaultContext = DeclarationDefaultContext::Standard);
-    template <class TreeBuilder> TreeStatement parseFunctionDeclaration(TreeBuilder&, ExportType = ExportType::NotExported, DeclarationDefaultContext = DeclarationDefaultContext::Standard);
+    template <class TreeBuilder> TreeStatement parseFunctionDeclaration(TreeBuilder&, ExportType = ExportType::NotExported, DeclarationDefaultContext = DeclarationDefaultContext::Standard, std::optional<int> functionConstructorParametersEndPosition = std::nullopt);
     template <class TreeBuilder> TreeStatement parseFunctionDeclarationStatement(TreeBuilder&, bool isAsync, bool parentAllowsFunctionDeclarationAsStatement);
-    template <class TreeBuilder> TreeStatement parseAsyncFunctionDeclaration(TreeBuilder&, ExportType = ExportType::NotExported, DeclarationDefaultContext = DeclarationDefaultContext::Standard);
+    template <class TreeBuilder> TreeStatement parseAsyncFunctionDeclaration(TreeBuilder&, ExportType = ExportType::NotExported, DeclarationDefaultContext = DeclarationDefaultContext::Standard, std::optional<int> functionConstructorParametersEndPosition = std::nullopt);
     template <class TreeBuilder> NEVER_INLINE bool maybeParseAsyncFunctionDeclarationStatement(TreeBuilder& context, TreeStatement& result, bool parentAllowsFunctionDeclarationAsStatement);
     template <class TreeBuilder> TreeStatement parseVariableDeclaration(TreeBuilder&, DeclarationType, ExportType = ExportType::NotExported);
     template <class TreeBuilder> TreeStatement parseDoWhileStatement(TreeBuilder&);
@@ -1599,7 +1599,7 @@
     template <class TreeBuilder> ALWAYS_INLINE TreeExpression createResolveAndUseVariable(TreeBuilder&, const Identifier*, bool isEval, const JSTextPosition&, const JSTokenLocation&);
 
     enum class FunctionDefinitionType { _expression_, Declaration, Method };
-    template <class TreeBuilder> NEVER_INLINE bool parseFunctionInfo(TreeBuilder&, FunctionNameRequirements, 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, std::optional<int> functionConstructorParametersEndPosition = std::nullopt);
     
     ALWAYS_INLINE bool isArrowFunctionParameters();
     
@@ -1848,7 +1848,7 @@
 
 template <typename LexerType>
 template <class ParsedNode>
-std::unique_ptr<ParsedNode> Parser<LexerType>::parse(ParserError& error, const Identifier& calleeName, SourceParseMode parseMode)
+std::unique_ptr<ParsedNode> Parser<LexerType>::parse(ParserError& error, const Identifier& calleeName, SourceParseMode parseMode, ParsingContext parsingContext, std::optional<int> functionConstructorParametersEndPosition)
 {
     int errLine;
     String errMsg;
@@ -1865,7 +1865,7 @@
     ASSERT(m_source->startColumn() > OrdinalNumber::beforeFirst());
     unsigned startColumn = m_source->startColumn().zeroBasedInt();
 
-    String parseError = parseInner(calleeName, parseMode);
+    String parseError = parseInner(calleeName, parseMode, parsingContext, functionConstructorParametersEndPosition);
 
     int lineNumber = m_lexer->lineNumber();
     bool lexError = m_lexer->sawError();
@@ -1960,7 +1960,7 @@
     std::unique_ptr<ParsedNode> result;
     if (source.provider()->source().is8Bit()) {
         Parser<Lexer<LChar>> parser(vm, source, builtinMode, strictMode, scriptMode, parseMode, superBinding, defaultConstructorKind, derivedContextType, isEvalNode<ParsedNode>(), evalContextType, debuggerParseData);
-        result = parser.parse<ParsedNode>(error, name, parseMode);
+        result = parser.parse<ParsedNode>(error, name, parseMode, isEvalNode<ParsedNode>() ? ParsingContext::Eval : ParsingContext::Program);
         if (positionBeforeLastNewline)
             *positionBeforeLastNewline = parser.positionBeforeLastNewline();
         if (builtinMode == JSParserBuiltinMode::Builtin) {
@@ -1973,7 +1973,7 @@
     } else {
         ASSERT_WITH_MESSAGE(defaultConstructorKind == ConstructorKind::None, "BuiltinExecutables::createDefaultConstructor should always use a 8-bit string");
         Parser<Lexer<UChar>> parser(vm, source, builtinMode, strictMode, scriptMode, parseMode, superBinding, defaultConstructorKind, derivedContextType, isEvalNode<ParsedNode>(), evalContextType, debuggerParseData);
-        result = parser.parse<ParsedNode>(error, name, parseMode);
+        result = parser.parse<ParsedNode>(error, name, parseMode, isEvalNode<ParsedNode>() ? ParsingContext::Eval : ParsingContext::Program);
         if (positionBeforeLastNewline)
             *positionBeforeLastNewline = parser.positionBeforeLastNewline();
     }
@@ -1987,4 +1987,37 @@
     return result;
 }
 
+inline std::unique_ptr<ProgramNode> parseFunctionForFunctionConstructor(VM& vm, const SourceCode& source, ParserError& error, JSTextPosition* positionBeforeLastNewline, std::optional<int> functionConstructorParametersEndPosition)
+{
+    ASSERT(!source.provider()->source().isNull());
+
+    MonotonicTime before;
+    if (UNLIKELY(Options::reportParseTimes()))
+        before = MonotonicTime::now();
+
+    Identifier name;
+    bool isEvalNode = false;
+    std::unique_ptr<ProgramNode> result;
+    if (source.provider()->source().is8Bit()) {
+        Parser<Lexer<LChar>> parser(&vm, source, JSParserBuiltinMode::NotBuiltin, JSParserStrictMode::NotStrict, JSParserScriptMode::Classic, SourceParseMode::ProgramMode, SuperBinding::NotNeeded, ConstructorKind::None, DerivedContextType::None, isEvalNode, EvalContextType::None, nullptr);
+        result = parser.parse<ProgramNode>(error, name, SourceParseMode::ProgramMode, ParsingContext::FunctionConstructor, functionConstructorParametersEndPosition);
+        if (positionBeforeLastNewline)
+            *positionBeforeLastNewline = parser.positionBeforeLastNewline();
+    } else {
+        Parser<Lexer<UChar>> parser(&vm, source, JSParserBuiltinMode::NotBuiltin, JSParserStrictMode::NotStrict, JSParserScriptMode::Classic, SourceParseMode::ProgramMode, SuperBinding::NotNeeded, ConstructorKind::None, DerivedContextType::None, isEvalNode, EvalContextType::None, nullptr);
+        result = parser.parse<ProgramNode>(error, name, SourceParseMode::ProgramMode, ParsingContext::FunctionConstructor, functionConstructorParametersEndPosition);
+        if (positionBeforeLastNewline)
+            *positionBeforeLastNewline = parser.positionBeforeLastNewline();
+    }
+
+    if (UNLIKELY(Options::reportParseTimes())) {
+        MonotonicTime after = MonotonicTime::now();
+        ParseHash hash(source);
+        dataLogLn(result ? "Parsed #" : "Failed to parse #", hash.hashForCall(), "/#", hash.hashForConstruct(), " in ", (after - before).milliseconds(), " ms.");
+    }
+
+    return result;
+}
+
+
 } // namespace

Modified: trunk/Source/_javascript_Core/parser/ParserModes.h (237053 => 237054)


--- trunk/Source/_javascript_Core/parser/ParserModes.h	2018-10-11 23:41:18 UTC (rev 237053)
+++ trunk/Source/_javascript_Core/parser/ParserModes.h	2018-10-11 23:43:58 UTC (rev 237054)
@@ -33,7 +33,6 @@
 enum class JSParserStrictMode { NotStrict, Strict };
 enum class JSParserBuiltinMode { NotBuiltin, Builtin };
 enum class JSParserScriptMode { Classic, Module };
-enum class JSParserCodeType { Program, Function, Module };
 
 enum class ConstructorKind { None, Base, Extends };
 enum class SuperBinding { Needed, NotNeeded };

Modified: trunk/Source/_javascript_Core/parser/ParserTokens.h (237053 => 237054)


--- trunk/Source/_javascript_Core/parser/ParserTokens.h	2018-10-11 23:41:18 UTC (rev 237053)
+++ trunk/Source/_javascript_Core/parser/ParserTokens.h	2018-10-11 23:43:58 UTC (rev 237054)
@@ -193,7 +193,6 @@
 struct JSTextPosition {
     JSTextPosition() = default;
     JSTextPosition(int _line, int _offset, int _lineStartOffset) : line(_line), offset(_offset), lineStartOffset(_lineStartOffset) { }
-    JSTextPosition(const JSTextPosition& other) : line(other.line), offset(other.offset), lineStartOffset(other.lineStartOffset) { }
 
     JSTextPosition operator+(int adjustment) const { return JSTextPosition(line, offset + adjustment, lineStartOffset); }
     JSTextPosition operator+(unsigned adjustment) const { return *this + static_cast<int>(adjustment); }
@@ -246,13 +245,6 @@
 
 struct JSTokenLocation {
     JSTokenLocation() = default;
-    JSTokenLocation(const JSTokenLocation& location)
-    {
-        line = location.line;
-        lineStartOffset = location.lineStartOffset;
-        startOffset = location.startOffset;
-        endOffset = location.endOffset;
-    }
 
     int line { 0 };
     unsigned lineStartOffset { 0 };

Modified: trunk/Source/_javascript_Core/parser/SourceCodeKey.h (237053 => 237054)


--- trunk/Source/_javascript_Core/parser/SourceCodeKey.h	2018-10-11 23:41:18 UTC (rev 237053)
+++ trunk/Source/_javascript_Core/parser/SourceCodeKey.h	2018-10-11 23:43:58 UTC (rev 237054)
@@ -29,6 +29,7 @@
 #include "ParserModes.h"
 #include "UnlinkedSourceCode.h"
 #include <wtf/HashTraits.h>
+#include <wtf/Hasher.h>
 
 namespace JSC {
 
@@ -71,18 +72,17 @@
 
 class SourceCodeKey {
 public:
-    SourceCodeKey()
-    {
-    }
+    SourceCodeKey() = default;
 
     SourceCodeKey(
         const UnlinkedSourceCode& sourceCode, const String& name, SourceCodeType codeType, JSParserStrictMode strictMode, 
         JSParserScriptMode scriptMode, DerivedContextType derivedContextType, EvalContextType evalContextType, bool isArrowFunctionContext,
-        DebuggerMode debuggerMode, TypeProfilerEnabled typeProfilerEnabled, ControlFlowProfilerEnabled controlFlowProfilerEnabled)
+        DebuggerMode debuggerMode, TypeProfilerEnabled typeProfilerEnabled, ControlFlowProfilerEnabled controlFlowProfilerEnabled, std::optional<int> functionConstructorParametersEndPosition)
             : m_sourceCode(sourceCode)
             , m_name(name)
             , m_flags(codeType, strictMode, scriptMode, derivedContextType, evalContextType, isArrowFunctionContext, debuggerMode, typeProfilerEnabled, controlFlowProfilerEnabled)
-            , m_hash(sourceCode.hash() ^ m_flags.bits())
+            , m_functionConstructorParametersEndPosition(functionConstructorParametersEndPosition.value_or(-1))
+            , m_hash(sourceCode.hash() ^ computeHash(m_flags.bits(), m_functionConstructorParametersEndPosition))
     {
     }
 
@@ -108,6 +108,7 @@
         return m_hash == other.m_hash
             && length() == other.length()
             && m_flags == other.m_flags
+            && m_functionConstructorParametersEndPosition == other.m_functionConstructorParametersEndPosition
             && m_name == other.m_name
             && string() == other.string();
     }
@@ -127,7 +128,8 @@
     UnlinkedSourceCode m_sourceCode;
     String m_name;
     SourceCodeFlags m_flags;
-    unsigned m_hash;
+    int m_functionConstructorParametersEndPosition { -1 };
+    unsigned m_hash { 0 };
 };
 
 } // namespace JSC

Modified: trunk/Source/_javascript_Core/runtime/CodeCache.cpp (237053 => 237054)


--- trunk/Source/_javascript_Core/runtime/CodeCache.cpp	2018-10-11 23:41:18 UTC (rev 237053)
+++ trunk/Source/_javascript_Core/runtime/CodeCache.cpp	2018-10-11 23:43:58 UTC (rev 237054)
@@ -57,7 +57,8 @@
         source, String(), CacheTypes<UnlinkedCodeBlockType>::codeType, strictMode, scriptMode, 
         derivedContextType, evalContextType, isArrowFunctionContext, debuggerMode, 
         vm.typeProfiler() ? TypeProfilerEnabled::Yes : TypeProfilerEnabled::No, 
-        vm.controlFlowProfiler() ? ControlFlowProfilerEnabled::Yes : ControlFlowProfilerEnabled::No);
+        vm.controlFlowProfiler() ? ControlFlowProfilerEnabled::Yes : ControlFlowProfilerEnabled::No,
+        std::nullopt);
     SourceCodeValue* cache = m_sourceCode.findCacheAndUpdateAge(key);
     if (cache && Options::useCodeCache()) {
         UnlinkedCodeBlockType* unlinkedCodeBlock = jsCast<UnlinkedCodeBlockType*>(cache->cell.get());
@@ -95,7 +96,7 @@
     return getUnlinkedGlobalCodeBlock<UnlinkedModuleProgramCodeBlock>(vm, executable, source, JSParserStrictMode::Strict, JSParserScriptMode::Module, debuggerMode, error, EvalContextType::None);
 }
 
-UnlinkedFunctionExecutable* CodeCache::getUnlinkedGlobalFunctionExecutable(VM& vm, const Identifier& name, const SourceCode& source, DebuggerMode debuggerMode, ParserError& error)
+UnlinkedFunctionExecutable* CodeCache::getUnlinkedGlobalFunctionExecutable(VM& vm, const Identifier& name, const SourceCode& source, DebuggerMode debuggerMode, std::optional<int> functionConstructorParametersEndPosition, ParserError& error)
 {
     bool isArrowFunctionContext = false;
     SourceCodeKey key(
@@ -107,7 +108,8 @@
         isArrowFunctionContext,
         debuggerMode, 
         vm.typeProfiler() ? TypeProfilerEnabled::Yes : TypeProfilerEnabled::No, 
-        vm.controlFlowProfiler() ? ControlFlowProfilerEnabled::Yes : ControlFlowProfilerEnabled::No);
+        vm.controlFlowProfiler() ? ControlFlowProfilerEnabled::Yes : ControlFlowProfilerEnabled::No,
+        functionConstructorParametersEndPosition);
     SourceCodeValue* cache = m_sourceCode.findCacheAndUpdateAge(key);
     if (cache && Options::useCodeCache()) {
         UnlinkedFunctionExecutable* executable = jsCast<UnlinkedFunctionExecutable*>(cache->cell.get());
@@ -117,10 +119,7 @@
     }
 
     JSTextPosition positionBeforeLastNewline;
-    std::unique_ptr<ProgramNode> program = parse<ProgramNode>(
-        &vm, source, Identifier(), JSParserBuiltinMode::NotBuiltin,
-        JSParserStrictMode::NotStrict, JSParserScriptMode::Classic, SourceParseMode::ProgramMode, SuperBinding::NotNeeded,
-        error, &positionBeforeLastNewline);
+    std::unique_ptr<ProgramNode> program = parseFunctionForFunctionConstructor(vm, source, error, &positionBeforeLastNewline, functionConstructorParametersEndPosition);
     if (!program) {
         RELEASE_ASSERT(error.isValid());
         return nullptr;
@@ -127,15 +126,7 @@
     }
 
     // This function assumes an input string that would result in a single function declaration.
-    StatementNode* statement = program->singleStatement();
-    if (UNLIKELY(!statement)) {
-        JSToken token;
-        error = ParserError(ParserError::SyntaxError, ParserError::SyntaxErrorIrrecoverable, token, "Parser error", -1);
-        return nullptr;
-    }
-    ASSERT(statement->isBlock());
-
-    StatementNode* funcDecl = static_cast<BlockNode*>(statement)->singleStatement();
+    StatementNode* funcDecl = program->singleStatement();
     if (UNLIKELY(!funcDecl)) {
         JSToken token;
         error = ParserError(ParserError::SyntaxError, ParserError::SyntaxErrorIrrecoverable, token, "Parser error", -1);

Modified: trunk/Source/_javascript_Core/runtime/CodeCache.h (237053 => 237054)


--- trunk/Source/_javascript_Core/runtime/CodeCache.h	2018-10-11 23:41:18 UTC (rev 237053)
+++ trunk/Source/_javascript_Core/runtime/CodeCache.h	2018-10-11 23:43:58 UTC (rev 237054)
@@ -194,7 +194,7 @@
     UnlinkedProgramCodeBlock* getUnlinkedProgramCodeBlock(VM&, ProgramExecutable*, const SourceCode&, JSParserStrictMode, DebuggerMode, ParserError&);
     UnlinkedEvalCodeBlock* getUnlinkedEvalCodeBlock(VM&, IndirectEvalExecutable*, const SourceCode&, JSParserStrictMode, DebuggerMode, ParserError&, EvalContextType);
     UnlinkedModuleProgramCodeBlock* getUnlinkedModuleProgramCodeBlock(VM&, ModuleProgramExecutable*, const SourceCode&, DebuggerMode, ParserError&);
-    UnlinkedFunctionExecutable* getUnlinkedGlobalFunctionExecutable(VM&, const Identifier&, const SourceCode&, DebuggerMode, ParserError&);
+    UnlinkedFunctionExecutable* getUnlinkedGlobalFunctionExecutable(VM&, const Identifier&, const SourceCode&, DebuggerMode, std::optional<int> functionConstructorParametersEndPosition, ParserError&);
 
     void clear() { m_sourceCode.clear(); }
 

Modified: trunk/Source/_javascript_Core/runtime/FunctionConstructor.cpp (237053 => 237054)


--- trunk/Source/_javascript_Core/runtime/FunctionConstructor.cpp	2018-10-11 23:41:18 UTC (rev 237053)
+++ trunk/Source/_javascript_Core/runtime/FunctionConstructor.cpp	2018-10-11 23:43:58 UTC (rev 237054)
@@ -94,82 +94,48 @@
         prefix = "async function ";
         break;
     case FunctionConstructionMode::AsyncGenerator:
-        prefix = "{async function*";
+        prefix = "async function*";
         break;
     }
 
-    auto checkBody = [&] (const String& body) {
-        // The spec mandates that the body parses a valid function body independent
-        // of the parameters.
-        String program = makeString("(", prefix, "(){\n", body, "\n})");
-        SourceCode source = makeSource(program, sourceOrigin, sourceURL, position);
-        JSValue exception;
-        checkSyntax(exec, source, &exception);
-        if (exception) {
-            scope.throwException(exec, exception);
-            return;
-        }
-    };
-
     // How we stringify functions is sometimes important for web compatibility.
     // See https://bugs.webkit.org/show_bug.cgi?id=24350.
     String program;
+    std::optional<int> functionConstructorParametersEndPosition = std::nullopt;
     if (args.isEmpty())
-        program = makeString("{", prefix, functionName.string(), "() {\n\n}}");
+        program = makeString(prefix, functionName.string(), "() {\n\n}");
     else if (args.size() == 1) {
         auto body = args.at(0).toWTFString(exec);
         RETURN_IF_EXCEPTION(scope, nullptr);
-        checkBody(body);
-        RETURN_IF_EXCEPTION(scope, nullptr);
-        program = makeString("{", prefix, functionName.string(), "() {\n", body, "\n}}");
+        program = makeString(prefix, functionName.string(), "() {\n", body, "\n}");
     } else {
         StringBuilder builder;
-        builder.append('{');
         builder.append(prefix);
         builder.append(functionName.string());
+
         builder.append('(');
-        StringBuilder parameterBuilder;
         auto viewWithString = args.at(0).toString(exec)->viewWithUnderlyingString(exec);
         RETURN_IF_EXCEPTION(scope, nullptr);
-        parameterBuilder.append(viewWithString.view);
+        builder.append(viewWithString.view);
         for (size_t i = 1; i < args.size() - 1; i++) {
-            parameterBuilder.appendLiteral(", ");
+            builder.appendLiteral(", ");
             auto viewWithString = args.at(i).toString(exec)->viewWithUnderlyingString(exec);
             RETURN_IF_EXCEPTION(scope, nullptr);
-            parameterBuilder.append(viewWithString.view);
+            builder.append(viewWithString.view);
         }
-        auto body = args.at(args.size() - 1).toWTFString(exec);
-        RETURN_IF_EXCEPTION(scope, nullptr);
+        functionConstructorParametersEndPosition = builder.length() + 1;
+        builder.appendLiteral(") {\n");
 
-        {
-            // The spec mandates that the parameters parse as a valid parameter list
-            // independent of the function body.
-            String program = tryMakeString("(", prefix, "(", parameterBuilder.toString(), "){\n\n})");
-            if (UNLIKELY(!program)) {
-                throwOutOfMemoryError(exec, scope);
-                return nullptr;
-            }
-            SourceCode source = makeSource(program, sourceOrigin, sourceURL, position);
-            JSValue exception;
-            checkSyntax(exec, source, &exception);
-            if (exception) {
-                scope.throwException(exec, exception);
-                return nullptr;
-            }
-        }
-
-        builder.append(parameterBuilder);
-        builder.appendLiteral(") {\n");
-        checkBody(body);
+        auto body = args.at(args.size() - 1).toString(exec)->viewWithUnderlyingString(exec);
         RETURN_IF_EXCEPTION(scope, nullptr);
-        builder.append(body);
-        builder.appendLiteral("\n}}");
+        builder.append(body.view);
+        builder.appendLiteral("\n}");
         program = builder.toString();
     }
 
     SourceCode source = makeSource(program, sourceOrigin, sourceURL, position);
     JSObject* exception = nullptr;
-    FunctionExecutable* function = FunctionExecutable::fromGlobalCode(functionName, *exec, source, exception, overrideLineNumber);
+    FunctionExecutable* function = FunctionExecutable::fromGlobalCode(functionName, *exec, source, exception, overrideLineNumber, functionConstructorParametersEndPosition);
     if (!function) {
         ASSERT(exception);
         return throwException(exec, scope, exception);

Modified: trunk/Source/_javascript_Core/runtime/FunctionExecutable.cpp (237053 => 237054)


--- trunk/Source/_javascript_Core/runtime/FunctionExecutable.cpp	2018-10-11 23:41:18 UTC (rev 237053)
+++ trunk/Source/_javascript_Core/runtime/FunctionExecutable.cpp	2018-10-11 23:43:58 UTC (rev 237054)
@@ -94,11 +94,11 @@
 
 FunctionExecutable* FunctionExecutable::fromGlobalCode(
     const Identifier& name, ExecState& exec, const SourceCode& source, 
-    JSObject*& exception, int overrideLineNumber)
+    JSObject*& exception, int overrideLineNumber, std::optional<int> functionConstructorParametersEndPosition)
 {
     UnlinkedFunctionExecutable* unlinkedExecutable = 
         UnlinkedFunctionExecutable::fromGlobalCode(
-            name, exec, source, exception, overrideLineNumber);
+            name, exec, source, exception, overrideLineNumber, functionConstructorParametersEndPosition);
     if (!unlinkedExecutable)
         return nullptr;
 

Modified: trunk/Source/_javascript_Core/runtime/FunctionExecutable.h (237053 => 237054)


--- trunk/Source/_javascript_Core/runtime/FunctionExecutable.h	2018-10-11 23:41:18 UTC (rev 237053)
+++ trunk/Source/_javascript_Core/runtime/FunctionExecutable.h	2018-10-11 23:43:58 UTC (rev 237054)
@@ -55,7 +55,7 @@
     }
     static FunctionExecutable* fromGlobalCode(
         const Identifier& name, ExecState&, const SourceCode&, 
-        JSObject*& exception, int overrideLineNumber);
+        JSObject*& exception, int overrideLineNumber, std::optional<int> functionConstructorParametersEndPosition);
 
     static void destroy(JSCell*);
         
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to