We shouldn't recurse into the parser when gathering metadata about various function...
authorsbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 29 Jun 2018 23:40:25 +0000 (23:40 +0000)
committersbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 29 Jun 2018 23:40:25 +0000 (23:40 +0000)
https://bugs.webkit.org/show_bug.cgi?id=184074
<rdar://problem/37165897>

Reviewed by Mark Lam.

JSTests:

* microbenchmarks/try-get-by-id-basic.js:
(const.bench.f.const.fooPlusBar.createBuiltin):
* microbenchmarks/try-get-by-id-polymorphic.js:
(fooPlusBar.createBuiltin):
* stress/array-push-with-force-exit.js:
* stress/dont-crash-on-stack-overflow-when-parsing-builtin.js: Added.
(f):
* stress/dont-crash-on-stack-overflow-when-parsing-default-constructor.js: Added.
(foo):
(prototype.runNearStackLimit):
* stress/is-constructor.js:
* stress/tailCallForwardArguments.js:
(putFuncToPrivateName.createBuiltin):

Source/JavaScriptCore:

Prior to this patch, when we made a builtin, we had to make an UnlinkedFunctionExecutable
for that builtin. This required calling into the parser. However, the parser
may throw a stack overflow. We were not able to recover from that. The only
reason we called into the parser here is that we were gathering text offsets
and various metadata for things in the builtin function. This patch writes a
mini parser that figures this information out without calling into the full
parser. (I've also added a debug assert that verifies the mini parser stays in
sync with the full parser.) The result of this is that BuiltinExecutbles::createExecutable
always succeeds.

* builtins/AsyncFromSyncIteratorPrototype.js:
(globalPrivate.createAsyncFromSyncIterator):
(globalPrivate.AsyncFromSyncIteratorConstructor):
* builtins/BuiltinExecutables.cpp:
(JSC::BuiltinExecutables::createExecutable):
* builtins/GlobalOperations.js:
(globalPrivate.getter.overriddenName.string_appeared_here.speciesGetter):
(globalPrivate.speciesConstructor):
(globalPrivate.copyDataProperties):
(globalPrivate.copyDataPropertiesNoExclusions):
* builtins/PromiseOperations.js:
(globalPrivate.newHandledRejectedPromise):
* builtins/RegExpPrototype.js:
(globalPrivate.hasObservableSideEffectsForRegExpMatch):
(globalPrivate.hasObservableSideEffectsForRegExpSplit):
* builtins/StringPrototype.js:
(globalPrivate.hasObservableSideEffectsForStringReplace):
(globalPrivate.getDefaultCollator):
* parser/Nodes.cpp:
(JSC::FunctionMetadataNode::FunctionMetadataNode):
(JSC::FunctionMetadataNode::operator== const):
(JSC::FunctionMetadataNode::dump const):
* parser/Nodes.h:
* parser/Parser.h:
(JSC::parse):
* parser/ParserError.h:
(JSC::ParserError::type const):
* parser/ParserTokens.h:
(JSC::JSTextPosition::operator== const):
(JSC::JSTextPosition::operator!= const):
* parser/SourceCode.h:
(JSC::SourceCode::operator== const):
(JSC::SourceCode::operator!= const):
(JSC::SourceCode::subExpression const):
(JSC::SourceCode::subExpression): Deleted.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@233377 268f45cc-cd09-0410-ab3c-d52691b4dbfc

21 files changed:
JSTests/ChangeLog
JSTests/microbenchmarks/try-get-by-id-basic.js
JSTests/microbenchmarks/try-get-by-id-polymorphic.js
JSTests/stress/array-push-with-force-exit.js
JSTests/stress/dont-crash-on-stack-overflow-when-parsing-builtin.js [new file with mode: 0644]
JSTests/stress/dont-crash-on-stack-overflow-when-parsing-default-constructor.js [new file with mode: 0644]
JSTests/stress/is-constructor.js
JSTests/stress/tailCallForwardArguments.js
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/builtins/AsyncFromSyncIteratorPrototype.js
Source/JavaScriptCore/builtins/BuiltinExecutables.cpp
Source/JavaScriptCore/builtins/GlobalOperations.js
Source/JavaScriptCore/builtins/PromiseOperations.js
Source/JavaScriptCore/builtins/RegExpPrototype.js
Source/JavaScriptCore/builtins/StringPrototype.js
Source/JavaScriptCore/parser/Nodes.cpp
Source/JavaScriptCore/parser/Nodes.h
Source/JavaScriptCore/parser/Parser.h
Source/JavaScriptCore/parser/ParserError.h
Source/JavaScriptCore/parser/ParserTokens.h
Source/JavaScriptCore/parser/SourceCode.h

index f213286..ddd10e9 100644 (file)
@@ -1,3 +1,25 @@
+2018-06-29  Saam Barati  <sbarati@apple.com>
+
+        We shouldn't recurse into the parser when gathering metadata about various function offsets
+        https://bugs.webkit.org/show_bug.cgi?id=184074
+        <rdar://problem/37165897>
+
+        Reviewed by Mark Lam.
+
+        * microbenchmarks/try-get-by-id-basic.js:
+        (const.bench.f.const.fooPlusBar.createBuiltin):
+        * microbenchmarks/try-get-by-id-polymorphic.js:
+        (fooPlusBar.createBuiltin):
+        * stress/array-push-with-force-exit.js:
+        * stress/dont-crash-on-stack-overflow-when-parsing-builtin.js: Added.
+        (f):
+        * stress/dont-crash-on-stack-overflow-when-parsing-default-constructor.js: Added.
+        (foo):
+        (prototype.runNearStackLimit):
+        * stress/is-constructor.js:
+        * stress/tailCallForwardArguments.js:
+        (putFuncToPrivateName.createBuiltin):
+
 2018-06-27  Mark Lam  <mark.lam@apple.com>
 
         DFG's compileReallocatePropertyStorage() and compileAllocatePropertyStorage() slow paths should also clear unused properties.
index 1bdbcaf..b17ac6a 100644 (file)
@@ -8,7 +8,7 @@ const check = (got, expect) => { if (got != expect) throw "Error: bad result got
 
 const bench = f => {
     // Re-create the builtin each time, so each benchmark gets its own value prediction.
-    const fooPlusBar = createBuiltin(`(function(o) { return @tryGetById(o, "foo") + @tryGetById(o, "bar"); })`);
+    const fooPlusBar = createBuiltin(`(function (o) { return @tryGetById(o, "foo") + @tryGetById(o, "bar"); })`);
     noInline(fooPlusBar);
     f(fooPlusBar);
 }
index 7bb7874..4d1c7f6 100644 (file)
@@ -6,7 +6,7 @@ var it = 1e5;
 
 const check = (got, expect) => { if (got != expect) throw "Error: bad result got " + got + " expected " + expect; };
 
-fooPlusBar = createBuiltin(`(function(o) { return @tryGetById(o, "foo") + @tryGetById(o, "bar"); })`);
+fooPlusBar = createBuiltin(`(function (o) { return @tryGetById(o, "foo") + @tryGetById(o, "bar"); })`);
 noInline(fooPlusBar);
 
 const bench = f => { f(); }
index e4363ac..3cec3fc 100644 (file)
@@ -1,6 +1,6 @@
 var createBuiltin = $vm.createBuiltin;
 
-var target = createBuiltin(`(function(array)
+var target = createBuiltin(`(function (array)
 {
     if (array) {
         @idWithProfile(array, "SpecOther").push(42);
diff --git a/JSTests/stress/dont-crash-on-stack-overflow-when-parsing-builtin.js b/JSTests/stress/dont-crash-on-stack-overflow-when-parsing-builtin.js
new file mode 100644 (file)
index 0000000..606ef4b
--- /dev/null
@@ -0,0 +1,13 @@
+//@ runDefault("--softReservedZoneSize=16384", "--reservedZoneSize=0", "--useJIT=0", "--validateBytecode=1", "--maxPerThreadStackUsage=500000")
+
+function f() {
+    try {
+        f();
+    } catch (e) {
+        try {
+            Map.prototype.forEach.call('', {});
+        } catch (e) {}
+    }
+}
+
+f()
diff --git a/JSTests/stress/dont-crash-on-stack-overflow-when-parsing-default-constructor.js b/JSTests/stress/dont-crash-on-stack-overflow-when-parsing-default-constructor.js
new file mode 100644 (file)
index 0000000..4f91cbe
--- /dev/null
@@ -0,0 +1,17 @@
+//@ runDefault("--softReservedZoneSize=16384", "--reservedZoneSize=0", "--useJIT=0", "--validateBytecode=1", "--maxPerThreadStackUsage=500000")
+
+function runNearStackLimit(f) {
+    function t() {
+        try {
+            return t();
+        } catch (e) {
+            new class extends (class {}) {}();
+            return f();
+        }
+    }
+    return t();
+}
+function foo() {
+    new class extends (class {}) {}();
+}
+runNearStackLimit(() => { return foo(); });
index a75d3d5..b6947a4 100644 (file)
@@ -5,7 +5,7 @@ function assert(x) {
         throw Error("Bad");
 }
 
-let isConstructor = createBuiltin("(function(c) { return @isConstructor(c); })");
+let isConstructor = createBuiltin("(function (c) { return @isConstructor(c); })");
 
 // Functions.
 assert(isConstructor(assert));
index 3b60f7c..f654f72 100644 (file)
@@ -1,7 +1,7 @@
 var createBuiltin = $vm.createBuiltin;
 
 // This is pretty bad but I need a private name.
-var putFuncToPrivateName = createBuiltin(`(function(func) { @arrayIteratorIsDone = func })`)
+var putFuncToPrivateName = createBuiltin(`(function (func) { @arrayIteratorIsDone = func })`)
 putFuncToPrivateName(function (a,b) { return b; })
 
 function createTailCallForwardingFuncWith(body, thisValue) {
index cb0aab2..9ae2191 100644 (file)
@@ -1,3 +1,57 @@
+2018-06-29  Saam Barati  <sbarati@apple.com>
+
+        We shouldn't recurse into the parser when gathering metadata about various function offsets
+        https://bugs.webkit.org/show_bug.cgi?id=184074
+        <rdar://problem/37165897>
+
+        Reviewed by Mark Lam.
+
+        Prior to this patch, when we made a builtin, we had to make an UnlinkedFunctionExecutable
+        for that builtin. This required calling into the parser. However, the parser
+        may throw a stack overflow. We were not able to recover from that. The only
+        reason we called into the parser here is that we were gathering text offsets
+        and various metadata for things in the builtin function. This patch writes a
+        mini parser that figures this information out without calling into the full
+        parser. (I've also added a debug assert that verifies the mini parser stays in
+        sync with the full parser.) The result of this is that BuiltinExecutbles::createExecutable
+        always succeeds.
+
+        * builtins/AsyncFromSyncIteratorPrototype.js:
+        (globalPrivate.createAsyncFromSyncIterator):
+        (globalPrivate.AsyncFromSyncIteratorConstructor):
+        * builtins/BuiltinExecutables.cpp:
+        (JSC::BuiltinExecutables::createExecutable):
+        * builtins/GlobalOperations.js:
+        (globalPrivate.getter.overriddenName.string_appeared_here.speciesGetter):
+        (globalPrivate.speciesConstructor):
+        (globalPrivate.copyDataProperties):
+        (globalPrivate.copyDataPropertiesNoExclusions):
+        * builtins/PromiseOperations.js:
+        (globalPrivate.newHandledRejectedPromise):
+        * builtins/RegExpPrototype.js:
+        (globalPrivate.hasObservableSideEffectsForRegExpMatch):
+        (globalPrivate.hasObservableSideEffectsForRegExpSplit):
+        * builtins/StringPrototype.js:
+        (globalPrivate.hasObservableSideEffectsForStringReplace):
+        (globalPrivate.getDefaultCollator):
+        * parser/Nodes.cpp:
+        (JSC::FunctionMetadataNode::FunctionMetadataNode):
+        (JSC::FunctionMetadataNode::operator== const):
+        (JSC::FunctionMetadataNode::dump const):
+        * parser/Nodes.h:
+        * parser/Parser.h:
+        (JSC::parse):
+        * parser/ParserError.h:
+        (JSC::ParserError::type const):
+        * parser/ParserTokens.h:
+        (JSC::JSTextPosition::operator== const):
+        (JSC::JSTextPosition::operator!= const):
+        * parser/SourceCode.h:
+        (JSC::SourceCode::operator== const):
+        (JSC::SourceCode::operator!= const):
+        (JSC::SourceCode::subExpression const):
+        (JSC::SourceCode::subExpression): Deleted.
+
 2018-06-28  Michael Saboff  <msaboff@apple.com>
   
         IsoCellSet::sweepToFreeList() not safe when Full GC in process
index c84bf27..94133fc 100644 (file)
@@ -151,6 +151,8 @@ function throw(exception)
 @globalPrivate
 function createAsyncFromSyncIterator(syncIterator, nextMethod)
 {
+    "use strict";
+
     if (!@isObject(syncIterator))
         @throwTypeError('Only objects can be wrapped by async-from-sync wrapper');
 
@@ -161,6 +163,8 @@ function createAsyncFromSyncIterator(syncIterator, nextMethod)
 @constructor
 function AsyncFromSyncIteratorConstructor(syncIterator, nextMethod)
 {
+    "use strict";
+
     @putByIdDirectPrivate(this, "syncIterator", syncIterator);
     @putByIdDirectPrivate(this, "nextMethod", nextMethod);
 }
index 1ff33e0..c70c181 100644 (file)
@@ -85,40 +85,174 @@ UnlinkedFunctionExecutable* createBuiltinExecutable(VM& vm, const SourceCode& co
 
 UnlinkedFunctionExecutable* BuiltinExecutables::createExecutable(VM& vm, const SourceCode& source, const Identifier& name, ConstructorKind constructorKind, ConstructAbility constructAbility)
 {
+    // Someone should get mad at me for writing this code. But, it prevents us from recursing into
+    // the parser, and hence, from throwing stack overflow when parsing a builtin.
+    StringView view = source.view();
+    RELEASE_ASSERT(!view.isNull());
+    RELEASE_ASSERT(view.is8Bit());
+    auto* characters = view.characters8();
+    RELEASE_ASSERT(view.length() >= 15); // strlen("(function (){})") == 15
+    RELEASE_ASSERT(characters[0] ==  '(');
+    RELEASE_ASSERT(characters[1] ==  'f');
+    RELEASE_ASSERT(characters[2] ==  'u');
+    RELEASE_ASSERT(characters[3] ==  'n');
+    RELEASE_ASSERT(characters[4] ==  'c');
+    RELEASE_ASSERT(characters[5] ==  't');
+    RELEASE_ASSERT(characters[6] ==  'i');
+    RELEASE_ASSERT(characters[7] ==  'o');
+    RELEASE_ASSERT(characters[8] ==  'n');
+    RELEASE_ASSERT(characters[9] ==  ' ');
+    RELEASE_ASSERT(characters[10] == '(');
+
+    JSTokenLocation start;
+    start.line = -1;
+    start.lineStartOffset = -1;
+    start.startOffset = 10;
+    start.endOffset = -1;
+
+    JSTokenLocation end;
+    end.line = 1;
+    end.lineStartOffset = 0;
+    end.startOffset = 1;
+    end.endOffset = -1;
+
+    unsigned startColumn = 10; // strlen("function (") == 10
+    int functionKeywordStart = 1; // (f
+    int functionNameStart = 10;
+    int parametersStart = 10;
+    bool isInStrictContext = false;
+    bool isArrowFunctionBodyExpression = false;
+
+    unsigned parameterCount;
+    {
+        unsigned i = 11;
+        unsigned commas = 0;
+        bool sawOneParam = false;
+        bool hasRestParam = false;
+        while (true) {
+            ASSERT(i < view.length());
+            if (characters[i] == ')')
+                break;
+
+            if (characters[i] == ',')
+                ++commas;
+            else if (!Lexer<LChar>::isWhiteSpace(characters[i]))
+                sawOneParam = true;
+
+            if (i + 2 < view.length() && characters[i] == '.' && characters[i + 1] == '.' && characters[i + 2] == '.') {
+                hasRestParam = true;
+                i += 2;
+            }
+
+            ++i;
+        }
+
+        if (commas)
+            parameterCount = commas + 1;
+        else if (sawOneParam)
+            parameterCount = 1;
+        else
+            parameterCount = 0;
+
+        if (hasRestParam) {
+            RELEASE_ASSERT(parameterCount);
+            --parameterCount;
+        }
+    }
+
+    unsigned lineCount = 0;
+    unsigned endColumn = 0;
+    unsigned offsetOfLastNewline = 0;
+    std::optional<unsigned> offsetOfSecondToLastNewline;
+    for (unsigned i = 0; i < view.length(); ++i) {
+        if (characters[i] == '\n') {
+            if (lineCount)
+                offsetOfSecondToLastNewline = offsetOfLastNewline;
+            ++lineCount;
+            endColumn = 0;
+            offsetOfLastNewline = i;
+        } else
+            ++endColumn;
+
+        if (!isInStrictContext && (characters[i] == '"' || characters[i] == '\'')) {
+            const unsigned useStrictLength = strlen("use strict");
+            if (i + 1 + useStrictLength < view.length()) {
+                if (!memcmp(characters + i + 1, "use strict", useStrictLength)) {
+                    isInStrictContext = true;
+                    i += 1 + useStrictLength;
+                }
+            }
+        }
+    }
+
+    unsigned positionBeforeLastNewlineLineStartOffset = offsetOfSecondToLastNewline ? *offsetOfSecondToLastNewline + 1 : 0;
+
+    int closeBraceOffsetFromEnd = 1;
+    while (true) {
+        if (characters[view.length() - closeBraceOffsetFromEnd] == '}')
+            break;
+        ++closeBraceOffsetFromEnd;
+    }
+
     JSTextPosition positionBeforeLastNewline;
-    ParserError error;
+    positionBeforeLastNewline.line = lineCount;
+    positionBeforeLastNewline.offset = offsetOfLastNewline;
+    positionBeforeLastNewline.lineStartOffset = positionBeforeLastNewlineLineStartOffset;
+
+    SourceCode newSource = source.subExpression(10, view.length() - closeBraceOffsetFromEnd, 0, 10);
     bool isBuiltinDefaultClassConstructor = constructorKind != ConstructorKind::None;
-    JSParserBuiltinMode builtinMode = isBuiltinDefaultClassConstructor ? JSParserBuiltinMode::NotBuiltin : JSParserBuiltinMode::Builtin;
     UnlinkedFunctionKind kind = isBuiltinDefaultClassConstructor ? UnlinkedNormalFunction : UnlinkedBuiltinFunction;
-    std::unique_ptr<ProgramNode> program = parse<ProgramNode>(
-        &vm, source, Identifier(), builtinMode,
-        JSParserStrictMode::NotStrict, JSParserScriptMode::Classic, SourceParseMode::ProgramMode, SuperBinding::NotNeeded, error,
-        &positionBeforeLastNewline, constructorKind);
-
-    if (!program) {
-        dataLog("Fatal error compiling builtin function '", name.string(), "': ", error.message());
-        CRASH();
+
+    FunctionMetadataNode metadata(
+        start, end, startColumn, endColumn, functionKeywordStart, functionNameStart, parametersStart,
+        isInStrictContext, constructorKind, constructorKind == ConstructorKind::Extends ? SuperBinding::Needed : SuperBinding::NotNeeded,
+        parameterCount, SourceParseMode::NormalFunctionMode, isArrowFunctionBodyExpression);
+
+    metadata.finishParsing(newSource, Identifier(), FunctionMode::FunctionExpression);
+    metadata.overrideName(name);
+    metadata.setEndPosition(positionBeforeLastNewline);
+
+    if (!ASSERT_DISABLED || Options::validateBytecode()) {
+        JSTextPosition positionBeforeLastNewlineFromParser;
+        ParserError error;
+        JSParserBuiltinMode builtinMode = isBuiltinDefaultClassConstructor ? JSParserBuiltinMode::NotBuiltin : JSParserBuiltinMode::Builtin;
+        std::unique_ptr<ProgramNode> program = parse<ProgramNode>(
+            &vm, source, Identifier(), builtinMode,
+            JSParserStrictMode::NotStrict, JSParserScriptMode::Classic, SourceParseMode::ProgramMode, SuperBinding::NotNeeded, error,
+            &positionBeforeLastNewlineFromParser, constructorKind);
+
+        if (program) {
+            StatementNode* exprStatement = program->singleStatement();
+            RELEASE_ASSERT(exprStatement);
+            RELEASE_ASSERT(exprStatement->isExprStatement());
+            ExpressionNode* funcExpr = static_cast<ExprStatementNode*>(exprStatement)->expr();
+            RELEASE_ASSERT(funcExpr);
+            RELEASE_ASSERT(funcExpr->isFuncExprNode());
+            FunctionMetadataNode* metadataFromParser = static_cast<FuncExprNode*>(funcExpr)->metadata();
+            RELEASE_ASSERT(!program->hasCapturedVariables());
+            
+            metadataFromParser->setEndPosition(positionBeforeLastNewlineFromParser);
+            RELEASE_ASSERT(metadataFromParser);
+            RELEASE_ASSERT(metadataFromParser->ident().isNull());
+            
+            // This function assumes an input string that would result in a single anonymous function expression.
+            metadataFromParser->setEndPosition(positionBeforeLastNewlineFromParser);
+            RELEASE_ASSERT(metadataFromParser);
+            metadataFromParser->overrideName(name);
+            metadataFromParser->setEndPosition(positionBeforeLastNewlineFromParser);
+
+            if (metadata != *metadataFromParser || positionBeforeLastNewlineFromParser != positionBeforeLastNewline) {
+                WTFLogAlways("Metadata of parser and hand rolled parser don't match\n");
+                CRASH();
+            }
+        } else {
+            RELEASE_ASSERT(error.isValid());
+            RELEASE_ASSERT(error.type() == ParserError::StackOverflow);
+        }
     }
 
-    StatementNode* exprStatement = program->singleStatement();
-    RELEASE_ASSERT(exprStatement);
-    RELEASE_ASSERT(exprStatement->isExprStatement());
-    ExpressionNode* funcExpr = static_cast<ExprStatementNode*>(exprStatement)->expr();
-    RELEASE_ASSERT(funcExpr);
-    RELEASE_ASSERT(funcExpr->isFuncExprNode());
-    FunctionMetadataNode* metadata = static_cast<FuncExprNode*>(funcExpr)->metadata();
-    RELEASE_ASSERT(!program->hasCapturedVariables());
-    
-    metadata->setEndPosition(positionBeforeLastNewline);
-    RELEASE_ASSERT(metadata);
-    RELEASE_ASSERT(metadata->ident().isNull());
-    
-    // This function assumes an input string that would result in a single anonymous function expression.
-    metadata->setEndPosition(positionBeforeLastNewline);
-    RELEASE_ASSERT(metadata);
-    metadata->overrideName(name);
     VariableEnvironment dummyTDZVariables;
-    UnlinkedFunctionExecutable* functionExecutable = UnlinkedFunctionExecutable::create(&vm, source, metadata, kind, constructAbility, JSParserScriptMode::Classic, dummyTDZVariables, DerivedContextType::None, isBuiltinDefaultClassConstructor);
+    UnlinkedFunctionExecutable* functionExecutable = UnlinkedFunctionExecutable::create(&vm, source, &metadata, kind, constructAbility, JSParserScriptMode::Classic, dummyTDZVariables, DerivedContextType::None, isBuiltinDefaultClassConstructor);
     return functionExecutable;
 }
 
index d7e033e..62c29d5 100644 (file)
@@ -62,12 +62,15 @@ function isDictionary(object)
 @overriddenName="get [Symbol.species]"
 function speciesGetter()
 {
+    "use strict";
     return this;
 }
 
 @globalPrivate
 function speciesConstructor(obj, defaultConstructor)
 {
+    "use strict";
+
     var constructor = obj.constructor;
     if (constructor === @undefined)
         return defaultConstructor;
@@ -84,6 +87,8 @@ function speciesConstructor(obj, defaultConstructor)
 @globalPrivate
 function copyDataProperties(target, source, excludedSet)
 {
+    "use strict";
+
     if (!@isObject(target))
         @throwTypeError("target needs to be an object");
 
@@ -109,6 +114,8 @@ function copyDataProperties(target, source, excludedSet)
 @globalPrivate
 function copyDataPropertiesNoExclusions(target, source)
 {
+    "use strict";
+
     if (!@isObject(target))
         @throwTypeError("target needs to be an object");
 
index 5d566ec..be85499 100644 (file)
@@ -87,6 +87,7 @@ function newPromiseCapability(constructor)
 @globalPrivate
 function newHandledRejectedPromise(error)
 {
+    "use strict";
     let promise = @Promise.@reject(error);
     @putByIdDirectPrivate(promise, "promiseIsHandled", true);
     return promise;
index 4d17ac0..2860ee1 100644 (file)
@@ -63,7 +63,10 @@ function regExpExec(regexp, str)
 }
 
 @globalPrivate
-function hasObservableSideEffectsForRegExpMatch(regexp) {
+function hasObservableSideEffectsForRegExpMatch(regexp)
+{
+    "use strict";
+
     // This is accessed by the RegExpExec internal function.
     let regexpExec = @tryGetById(regexp, "exec");
     if (regexpExec !== @regExpBuiltinExec)
@@ -351,7 +354,10 @@ function search(strArg)
 }
 
 @globalPrivate
-function hasObservableSideEffectsForRegExpSplit(regexp) {
+function hasObservableSideEffectsForRegExpSplit(regexp)
+{
+    "use strict";
+
     // This is accessed by the RegExpExec internal function.
     let regexpExec = @tryGetById(regexp, "exec");
     if (regexpExec !== @regExpBuiltinExec)
index 313e025..db0c401 100644 (file)
@@ -191,7 +191,10 @@ function padEnd(maxLength/*, fillString*/)
 }
 
 @globalPrivate
-function hasObservableSideEffectsForStringReplace(regexp, replacer) {
+function hasObservableSideEffectsForStringReplace(regexp, replacer)
+{
+    "use strict";
+
     if (replacer !== @regExpPrototypeSymbolReplace)
         return true;
     
@@ -235,6 +238,8 @@ function replace(search, replace)
 @globalPrivate
 function getDefaultCollator()
 {
+    "use strict";
+
     return @getDefaultCollator.collator || (@getDefaultCollator.collator = new @Collator());
 }
     
index 4bb05f3..214995c 100644 (file)
@@ -214,6 +214,29 @@ FunctionMetadataNode::FunctionMetadataNode(
     ASSERT(m_constructorKind == static_cast<unsigned>(constructorKind));
 }
 
+FunctionMetadataNode::FunctionMetadataNode(
+    const JSTokenLocation& startLocation, 
+    const JSTokenLocation& endLocation, unsigned startColumn, unsigned endColumn, 
+    int functionKeywordStart, int functionNameStart, int parametersStart, bool isInStrictContext, 
+    ConstructorKind constructorKind, SuperBinding superBinding, unsigned parameterCount, SourceParseMode mode, bool isArrowFunctionBodyExpression)
+        : Node(endLocation)
+        , m_startColumn(startColumn)
+        , m_endColumn(endColumn)
+        , m_functionKeywordStart(functionKeywordStart)
+        , m_functionNameStart(functionNameStart)
+        , m_parametersStart(parametersStart)
+        , m_startStartOffset(startLocation.startOffset)
+        , m_parameterCount(parameterCount)
+        , m_parseMode(mode)
+        , m_isInStrictContext(isInStrictContext)
+        , m_superBinding(static_cast<unsigned>(superBinding))
+        , m_constructorKind(static_cast<unsigned>(constructorKind))
+        , m_isArrowFunctionBodyExpression(isArrowFunctionBodyExpression)
+{
+    ASSERT(m_superBinding == static_cast<unsigned>(superBinding));
+    ASSERT(m_constructorKind == static_cast<unsigned>(constructorKind));
+}
+
 void FunctionMetadataNode::finishParsing(const SourceCode& source, const Identifier& ident, FunctionMode functionMode)
 {
     m_source = source;
@@ -227,6 +250,55 @@ void FunctionMetadataNode::setEndPosition(JSTextPosition position)
     m_endColumn = position.offset - position.lineStartOffset;
 }
 
+bool FunctionMetadataNode::operator==(const FunctionMetadataNode& other) const
+{
+    return m_ident == other.m_ident
+        && m_ecmaName == other.m_ecmaName
+        && m_inferredName == other.m_inferredName
+        && m_functionMode== other.m_functionMode
+        && m_startColumn== other.m_startColumn
+        && m_endColumn== other.m_endColumn
+        && m_functionKeywordStart== other.m_functionKeywordStart
+        && m_functionNameStart== other.m_functionNameStart
+        && m_parametersStart== other.m_parametersStart
+        && m_source== other.m_source
+        && m_classSource== other.m_classSource
+        && m_startStartOffset== other.m_startStartOffset
+        && m_parameterCount== other.m_parameterCount
+        && m_lastLine== other.m_lastLine
+        && m_parseMode== other.m_parseMode
+        && m_isInStrictContext == other.m_isInStrictContext 
+        && m_superBinding == other.m_superBinding 
+        && m_constructorKind == other.m_constructorKind 
+        && m_isArrowFunctionBodyExpression == other.m_isArrowFunctionBodyExpression
+        && m_position == other.m_position;
+}
+
+void FunctionMetadataNode::dump(PrintStream& stream) const
+{
+    stream.println("m_ident ", m_ident);
+    stream.println("m_ecmaName ", m_ecmaName);
+    stream.println("m_inferredName ", m_inferredName);
+    stream.println("m_functionMode ", static_cast<uint32_t>(m_functionMode));
+    stream.println("m_startColumn ", m_startColumn);
+    stream.println("m_endColumn ", m_endColumn);
+    stream.println("m_functionKeywordStart ", m_functionKeywordStart);
+    stream.println("m_functionNameStart ", m_functionNameStart);
+    stream.println("m_parametersStart ", m_parametersStart);
+    stream.println("m_classSource.isNull() ", m_classSource.isNull());
+    stream.println("m_startStartOffset ", m_startStartOffset);
+    stream.println("m_parameterCount ", m_parameterCount);
+    stream.println("m_lastLine ", m_lastLine);
+    stream.println("m_parseMode ", static_cast<uint32_t>(m_parseMode));
+    stream.println("m_isInStrictContext ", m_isInStrictContext);
+    stream.println("m_superBinding ", m_superBinding);
+    stream.println("m_constructorKind ", m_constructorKind);
+    stream.println("m_isArrowFunctionBodyExpression ", m_isArrowFunctionBodyExpression);
+    stream.println("position().line ", position().line);
+    stream.println("position().offset ", position().offset);
+    stream.println("position().lineStartOffset ", position().lineStartOffset);
+}
+
 // ------------------------------ FunctionNode -----------------------------
 
 FunctionNode::FunctionNode(ParserArena& parserArena, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned startColumn, unsigned endColumn, SourceElements* children, VariableEnvironment& varEnvironment, FunctionStack&& funcStack, VariableEnvironment& lexicalVariables, UniquedStringImplPtrSet&& sloppyModeHoistedFunctions, FunctionParameters* parameters, const SourceCode& sourceCode, CodeFeatures features, InnerArrowFunctionCodeFeatures innerArrowFunctionCodeFeatures, int numConstants, RefPtr<ModuleScopeData>&&)
index 4365637..34f4417 100644 (file)
@@ -1988,6 +1988,14 @@ namespace JSC {
             int functionNameStart, int parametersStart, bool isInStrictContext, 
             ConstructorKind, SuperBinding, unsigned parameterCount,
             SourceParseMode, bool isArrowFunctionBodyExpression);
+        FunctionMetadataNode(
+            const JSTokenLocation& start, const JSTokenLocation& end, 
+            unsigned startColumn, unsigned endColumn, int functionKeywordStart, 
+            int functionNameStart, int parametersStart, bool isInStrictContext, 
+            ConstructorKind, SuperBinding, unsigned parameterCount,
+            SourceParseMode, bool isArrowFunctionBodyExpression);
+
+        void dump(PrintStream&) const;
 
         void finishParsing(const SourceCode&, const Identifier&, FunctionMode);
         
@@ -2028,7 +2036,13 @@ namespace JSC {
         }
         unsigned lastLine() const { return m_lastLine; }
 
-    protected:
+        bool operator==(const FunctionMetadataNode&) const;
+        bool operator!=(const FunctionMetadataNode& other) const
+        {
+            return !(*this == other);
+        }
+
+    public:
         Identifier m_ident;
         Identifier m_ecmaName;
         Identifier m_inferredName;
index 32a1c1e..3666059 100644 (file)
@@ -1964,8 +1964,11 @@ std::unique_ptr<ParsedNode> parse(
         if (positionBeforeLastNewline)
             *positionBeforeLastNewline = parser.positionBeforeLastNewline();
         if (builtinMode == JSParserBuiltinMode::Builtin) {
-            if (!result)
-                dataLogLn("Error compiling builtin: ", error.message());
+            if (!result) {
+                ASSERT(error.isValid());
+                if (error.type() != ParserError::StackOverflow)
+                    dataLogLn("Unexpected error compiling builtin: ", error.message());
+            }
         }
     } else {
         ASSERT_WITH_MESSAGE(defaultConstructorKind == ConstructorKind::None, "BuiltinExecutables::createDefaultConstructor should always use a 8-bit string");
index 7883efe..b177912 100644 (file)
@@ -83,6 +83,7 @@ public:
     const JSToken& token() const { return m_token; }
     const String& message() const { return m_message; }
     int line() const { return m_line; }
+    ErrorType type() const { return m_type; }
 
     JSObject* toErrorObject(
         JSGlobalObject* globalObject, const SourceCode& source, 
index c2058ae..86b7cfb 100644 (file)
@@ -202,6 +202,17 @@ struct JSTextPosition {
 
     operator int() const { return offset; }
 
+    bool operator==(const JSTextPosition& other) const
+    {
+        return line == other.line
+            && offset == other.offset
+            && lineStartOffset == other.lineStartOffset;
+    }
+    bool operator!=(const JSTextPosition& other) const
+    {
+        return !(*this == other);
+    }
+
     int line;
     int offset;
     int lineStartOffset;
index 90f547a..7807ce4 100644 (file)
@@ -79,7 +79,21 @@ namespace JSC {
 
         SourceProvider* provider() const { return m_provider.get(); }
 
-        SourceCode subExpression(unsigned openBrace, unsigned closeBrace, int firstLine, int startColumn);
+        SourceCode subExpression(unsigned openBrace, unsigned closeBrace, int firstLine, int startColumn) const;
+
+        bool operator==(const SourceCode& other) const
+        {
+            return m_firstLine == other.m_firstLine
+                && m_startColumn == other.m_startColumn
+                && m_provider == other.m_provider
+                && m_startOffset == other.m_startOffset
+                && m_endOffset == other.m_endOffset;
+        }
+
+        bool operator!=(const SourceCode& other) const
+        {
+            return !(*this == other);
+        }
 
     private:
         OrdinalNumber m_firstLine;
@@ -91,7 +105,7 @@ namespace JSC {
         return SourceCode(StringSourceProvider::create(source, sourceOrigin, url, startPosition, sourceType), startPosition.m_line.oneBasedInt(), startPosition.m_column.oneBasedInt());
     }
     
-    inline SourceCode SourceCode::subExpression(unsigned openBrace, unsigned closeBrace, int firstLine, int startColumn)
+    inline SourceCode SourceCode::subExpression(unsigned openBrace, unsigned closeBrace, int firstLine, int startColumn) const
     {
         startColumn += 1; // Convert to base 1.
         return SourceCode(RefPtr<SourceProvider> { provider() }, openBrace, closeBrace + 1, firstLine, startColumn);