Title: [215453] trunk
Revision
215453
Author
[email protected]
Date
2017-04-17 23:43:54 -0700 (Mon, 17 Apr 2017)

Log Message

BytecodeGenerator ".call" and ".apply" is exponential in nesting depth
https://bugs.webkit.org/show_bug.cgi?id=139847
<rdar://problem/19321122>

Reviewed by Oliver Hunt.

JSTests:

* stress/call-apply-exponential-bytecode-size.js: Added.
(assert):
(const.inc):
(const.inc2):
(bar):
(randomApplyOrCall):
(baz):
(jaz):
(haz):
(foo):

Source/_javascript_Core:

The BytecodeGenerator's .apply(...) and .call(...) code would
emit bytecode for the evaluation of its arguments twice. This
is exponential, specifically, 2^n, where n is the nesting depth of
.call(...) or .apply(...) inside other .call(...) or .apply(...).

The reason we emit code for the arguments twice is that we try
to emit efficient code for when .call or .apply is Function.prototype.call
or Function.prototype.apply. Because of this, we compare .call/.apply to
Function.prototype.call/.apply, and if they're the same, we emit a specialized
function call in bytecode. Otherwise, we emit the generalized version.

This patch makes it so that each .call(...) and .apply(...) records
its max inner nesting depth. Then, we only perform the optimization
for the bottom k (where k = 6) layers of the nesting tree. The reason we
apply the optimization to the bottom k layers instead of top k layers
is that we'll produce less code this way.

* bytecompiler/NodesCodegen.cpp:
(JSC::CallFunctionCallDotNode::emitBytecode):
(JSC::ApplyFunctionCallDotNode::emitBytecode):
* parser/ASTBuilder.h:
(JSC::ASTBuilder::makeFunctionCallNode):
* parser/NodeConstructors.h:
(JSC::CallFunctionCallDotNode::CallFunctionCallDotNode):
(JSC::ApplyFunctionCallDotNode::ApplyFunctionCallDotNode):
* parser/Nodes.h:
* parser/Parser.cpp:
(JSC::recordCallOrApplyDepth):
(JSC::Parser<LexerType>::parseMemberExpression):
* parser/Parser.h:
(JSC::Parser::CallOrApplyDepth::CallOrApplyDepth):
(JSC::Parser::CallOrApplyDepth::maxChildDepth):
(JSC::Parser::CallOrApplyDepth::~CallOrApplyDepth):
* parser/SyntaxChecker.h:
(JSC::SyntaxChecker::makeFunctionCallNode):

Modified Paths

Added Paths

Diff

Modified: trunk/JSTests/ChangeLog (215452 => 215453)


--- trunk/JSTests/ChangeLog	2017-04-18 06:05:25 UTC (rev 215452)
+++ trunk/JSTests/ChangeLog	2017-04-18 06:43:54 UTC (rev 215453)
@@ -1,3 +1,22 @@
+2017-04-17  Saam Barati  <[email protected]>
+
+        BytecodeGenerator ".call" and ".apply" is exponential in nesting depth
+        https://bugs.webkit.org/show_bug.cgi?id=139847
+        <rdar://problem/19321122>
+
+        Reviewed by Oliver Hunt.
+
+        * stress/call-apply-exponential-bytecode-size.js: Added.
+        (assert):
+        (const.inc):
+        (const.inc2):
+        (bar):
+        (randomApplyOrCall):
+        (baz):
+        (jaz):
+        (haz):
+        (foo):
+
 2017-04-17  Mark Lam  <[email protected]>
 
         JSArray::appendMemcpy() needs to handle copying from Undecided indexing type too.

Added: trunk/JSTests/stress/call-apply-exponential-bytecode-size.js (0 => 215453)


--- trunk/JSTests/stress/call-apply-exponential-bytecode-size.js	                        (rev 0)
+++ trunk/JSTests/stress/call-apply-exponential-bytecode-size.js	2017-04-18 06:43:54 UTC (rev 215453)
@@ -0,0 +1,104 @@
+"use strict";
+
+function assert(b) {
+    if (!b)
+        throw new Error();
+}
+
+const inc = (x, y) => { return x + 1; }
+const inc2 = (x, y) => { return y ? y + 1 : x + 1; }
+function bar() {
+    return inc.call(null, inc.call(null, inc.call(null, inc.call(null,
+      inc.call(null, inc.call(null, inc.call(null, inc.call(null,
+        inc.call(null, inc.call(null, inc.call(null, inc.call(null,
+          inc.call(null, inc.call(null, inc.call(null, inc.call(null,
+            inc.call(null, inc.call(null, inc.call(null, inc.call(null,
+              inc.call(null, 1)))))))))))))))))))));
+}
+assert(bar() === 22);
+
+function randomApplyOrCall(bias, size, dontSpreadBias = 0) {
+    let cur = `1`;
+    for (let i = 0; i < size; ++i) {
+        if (Math.random() >= bias) {
+            if (Math.random() >= dontSpreadBias)
+                cur = `inc.call(null, ${cur})`;
+            else
+                cur = `inc.call(...[null, ${cur}])`;
+        } else {
+            if (Math.random() >= dontSpreadBias)
+                cur = `inc.apply(null, [${cur}])`;
+            else
+                cur = `inc.apply(...[null, [${cur}]])`;
+        }
+    }
+
+    if (bias > 0.85) {
+        cur = `let random = ${Math.random()}; ${cur}`;
+    }
+
+    return eval(cur);
+}
+
+assert(randomApplyOrCall(0, 250) === 251);
+assert(randomApplyOrCall(1, 250) === 251);
+assert(randomApplyOrCall(0, 250, 0) === 251);
+assert(randomApplyOrCall(1, 250, 1) === 251);
+for (let i = 0; i < 1000; ++i) {
+    assert(randomApplyOrCall(Math.random(), 250) === 251);
+    assert(randomApplyOrCall(Math.random(), 252, Math.random()) === 253);
+}
+
+function baz() {
+    return inc.call(null, inc.call(null, inc.call(null, inc.call(null,
+      inc.call(null, inc.call(null, inc.call(null, inc.call(null,
+        inc.call(null, inc.call(null, inc.call(null, inc.call(null,
+          inc.call(null, inc.call(null, inc.call(null, inc.call(null,
+            inc.call(null, inc.call(null, inc.call(null, inc.call(null,
+              inc.call(null, 1)))))))))))))))))))), 
+       inc.call(null, inc.call(null, 1)));
+}
+assert(baz() === 22);
+
+function jaz() {
+    return inc.call(null, inc.call(null, inc.call(null, inc.call(null,
+      inc.call(null, inc.call(null, inc.call(null, inc.call(null,
+        inc.call(null, inc.call(null, inc.call(null, inc.call(null,
+          inc.call(null, inc.call(null, inc.call(null, inc.call(null,
+            inc.call(null, inc.apply(null, [inc.call(null, inc.call(null,
+              inc.call(null, 1)))]))))))))))))))))),
+       inc.call(null, inc.call(null, inc.apply(null, [1]))));
+}
+assert(jaz() === 22);
+
+function haz() {
+    return inc.call(null, inc.call(null, inc.call(null, inc.call(null,
+      inc.call(null, inc.call(null, inc.call(null, inc.call(null,
+        inc.call(null, inc.call(null, inc.call(null, inc.call(null,
+          inc.call(null, inc.call(null, inc.call(null, inc.call(null,
+            inc.call(null, inc.apply(null, [inc.call(null, inc.call(null,
+              inc.call(null, 1)))]))))))))))))))))),
+        inc.call(null, inc.call(null, inc.call(null, inc.call(null,
+          inc.call(null, inc.call(null, inc.call(null, inc.call(null,
+            inc.call(null, inc.call(null, inc.call(null, inc.call(null,
+              inc.call(null, inc.call(null, inc.call(null, inc.call(null,
+                inc.call(null, inc.apply(null, [inc.call(null, inc.call(null,
+                  inc.call(null, 1)))])))))))))))))))))));
+}
+assert(haz() === 22);
+
+function foo() {
+    return inc2.call(null, inc2.call(null, inc2.call(null, inc2.call(null,
+      inc2.call(null, inc2.call(null, inc2.call(null, inc2.call(null,
+        inc2.call(null, inc2.call(null, inc2.call(null, inc2.call(null,
+          inc2.call(null, inc2.call(null, inc2.call(null, inc2.call(null,
+            inc2.call(null, inc2.apply(null, [inc2.call(null, inc2.call(null,
+              inc2.call(null, 1)))]))))))))))))))))),
+        inc2.call(null, inc2.call(null, inc2.call(null, inc2.call(null,
+          inc2.call(null, inc2.call(null, inc2.call(null, inc2.call(null,
+            inc2.call(null, inc2.call(null, inc2.call(null, inc2.call(null,
+              inc2.call(null, inc2.call(null, inc2.call(null, inc2.call(null,
+                inc2.call(null, inc2.apply(null, [inc2.call(null, inc2.call(null,
+                  inc2.call(null, inc2.call(null, inc.call(null, 1)))))])))))))))))))))))));
+}
+assert(foo() === 25);

Modified: trunk/Source/_javascript_Core/ChangeLog (215452 => 215453)


--- trunk/Source/_javascript_Core/ChangeLog	2017-04-18 06:05:25 UTC (rev 215452)
+++ trunk/Source/_javascript_Core/ChangeLog	2017-04-18 06:43:54 UTC (rev 215453)
@@ -1,3 +1,47 @@
+2017-04-17  Saam Barati  <[email protected]>
+
+        BytecodeGenerator ".call" and ".apply" is exponential in nesting depth
+        https://bugs.webkit.org/show_bug.cgi?id=139847
+        <rdar://problem/19321122>
+
+        Reviewed by Oliver Hunt.
+
+        The BytecodeGenerator's .apply(...) and .call(...) code would
+        emit bytecode for the evaluation of its arguments twice. This
+        is exponential, specifically, 2^n, where n is the nesting depth of
+        .call(...) or .apply(...) inside other .call(...) or .apply(...).
+        
+        The reason we emit code for the arguments twice is that we try
+        to emit efficient code for when .call or .apply is Function.prototype.call
+        or Function.prototype.apply. Because of this, we compare .call/.apply to
+        Function.prototype.call/.apply, and if they're the same, we emit a specialized
+        function call in bytecode. Otherwise, we emit the generalized version.
+        
+        This patch makes it so that each .call(...) and .apply(...) records
+        its max inner nesting depth. Then, we only perform the optimization
+        for the bottom k (where k = 6) layers of the nesting tree. The reason we
+        apply the optimization to the bottom k layers instead of top k layers
+        is that we'll produce less code this way.
+
+        * bytecompiler/NodesCodegen.cpp:
+        (JSC::CallFunctionCallDotNode::emitBytecode):
+        (JSC::ApplyFunctionCallDotNode::emitBytecode):
+        * parser/ASTBuilder.h:
+        (JSC::ASTBuilder::makeFunctionCallNode):
+        * parser/NodeConstructors.h:
+        (JSC::CallFunctionCallDotNode::CallFunctionCallDotNode):
+        (JSC::ApplyFunctionCallDotNode::ApplyFunctionCallDotNode):
+        * parser/Nodes.h:
+        * parser/Parser.cpp:
+        (JSC::recordCallOrApplyDepth):
+        (JSC::Parser<LexerType>::parseMemberExpression):
+        * parser/Parser.h:
+        (JSC::Parser::CallOrApplyDepth::CallOrApplyDepth):
+        (JSC::Parser::CallOrApplyDepth::maxChildDepth):
+        (JSC::Parser::CallOrApplyDepth::~CallOrApplyDepth):
+        * parser/SyntaxChecker.h:
+        (JSC::SyntaxChecker::makeFunctionCallNode):
+
 2017-04-17  Mark Lam  <[email protected]>
 
         JSArray::appendMemcpy() needs to handle copying from Undecided indexing type too.

Modified: trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp (215452 => 215453)


--- trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp	2017-04-18 06:05:25 UTC (rev 215452)
+++ trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp	2017-04-18 06:43:54 UTC (rev 215453)
@@ -1189,21 +1189,36 @@
 
 RegisterID* CallFunctionCallDotNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
 {
-    Ref<Label> realCall = generator.newLabel();
-    Ref<Label> end = generator.newLabel();
     RefPtr<RegisterID> base = generator.emitNode(m_base);
     generator.emitExpressionInfo(subexpressionDivot(), subexpressionStart(), subexpressionEnd());
     RefPtr<RegisterID> function;
-    bool emitCallCheck = !generator.isBuiltinFunction();
-    if (emitCallCheck) {
+    RefPtr<RegisterID> returnValue = generator.finalDestination(dst);
+
+    auto makeFunction = [&] {
         if (m_base->isSuperNode()) {
             RefPtr<RegisterID> thisValue = generator.ensureThis();
             function = generator.emitGetById(generator.tempDestination(dst), base.get(), thisValue.get(), generator.propertyNames().builtinNames().callPublicName());
         } else
             function = generator.emitGetById(generator.tempDestination(dst), base.get(), generator.propertyNames().builtinNames().callPublicName());
+    };
+
+    bool emitCallCheck = !generator.isBuiltinFunction();
+    if (m_callOrApplyChildDepth > 4 && emitCallCheck) {
+        makeFunction();
+        CallArguments callArguments(generator, m_args);
+        generator.emitMove(callArguments.thisRegister(), base.get());
+        generator.emitCallInTailPosition(returnValue.get(), function.get(), NoExpectedFunction, callArguments, divot(), divotStart(), divotEnd(), DebuggableCall::Yes);
+        generator.moveToDestinationIfNeeded(dst, returnValue.get());
+        return returnValue.get();
+    }
+
+    Ref<Label> realCall = generator.newLabel();
+    Ref<Label> end = generator.newLabel();
+
+    if (emitCallCheck) {
+        makeFunction();
         generator.emitJumpIfNotFunctionCall(function.get(), realCall.get());
     }
-    RefPtr<RegisterID> returnValue = generator.finalDestination(dst);
     {
         if (m_args->m_listNode && m_args->m_listNode->m_expr && m_args->m_listNode->m_expr->isSpreadExpression()) {
             SpreadExpressionNode* spread = static_cast<SpreadExpressionNode*>(m_args->m_listNode->m_expr);
@@ -1256,19 +1271,32 @@
     // function.apply(thisArg, [arg0, arg1, ...]) -> can be trivially coerced into function.call(thisArg, arg0, arg1, ...) and saves object allocation
     bool mayBeCall = areTrivialApplyArguments(m_args);
 
-    Ref<Label> realCall = generator.newLabel();
-    Ref<Label> end = generator.newLabel();
+    RefPtr<RegisterID> function;
     RefPtr<RegisterID> base = generator.emitNode(m_base);
-    generator.emitExpressionInfo(subexpressionDivot(), subexpressionStart(), subexpressionEnd());
-    RefPtr<RegisterID> function;
-    RefPtr<RegisterID> returnValue = generator.finalDestination(dst, function.get());
-    bool emitCallCheck = !generator.isBuiltinFunction();
-    if (emitCallCheck) {
+    RefPtr<RegisterID> returnValue = generator.finalDestination(dst);
+    auto makeFunction = [&] {
         if (m_base->isSuperNode()) {
             RefPtr<RegisterID> thisValue = generator.ensureThis();
             function = generator.emitGetById(generator.tempDestination(dst), base.get(), thisValue.get(), generator.propertyNames().builtinNames().applyPublicName());
         } else
             function = generator.emitGetById(generator.tempDestination(dst), base.get(), generator.propertyNames().builtinNames().applyPublicName());
+    };
+
+    bool emitCallCheck = !generator.isBuiltinFunction();
+    if (m_callOrApplyChildDepth > 4 && emitCallCheck) {
+        makeFunction();
+        CallArguments callArguments(generator, m_args);
+        generator.emitMove(callArguments.thisRegister(), base.get());
+        generator.emitCallInTailPosition(returnValue.get(), function.get(), NoExpectedFunction, callArguments, divot(), divotStart(), divotEnd(), DebuggableCall::Yes);
+        generator.moveToDestinationIfNeeded(dst, returnValue.get());
+        return returnValue.get();
+    }
+
+    Ref<Label> realCall = generator.newLabel();
+    Ref<Label> end = generator.newLabel();
+    generator.emitExpressionInfo(subexpressionDivot(), subexpressionStart(), subexpressionEnd());
+    if (emitCallCheck) {
+        makeFunction();
         generator.emitJumpIfNotFunctionApply(function.get(), realCall.get());
     }
     if (mayBeCall) {

Modified: trunk/Source/_javascript_Core/parser/ASTBuilder.h (215452 => 215453)


--- trunk/Source/_javascript_Core/parser/ASTBuilder.h	2017-04-18 06:05:25 UTC (rev 215452)
+++ trunk/Source/_javascript_Core/parser/ASTBuilder.h	2017-04-18 06:43:54 UTC (rev 215453)
@@ -129,7 +129,7 @@
     static const int  DontBuildStrings = 0;
 
     ExpressionNode* makeBinaryNode(const JSTokenLocation&, int token, std::pair<ExpressionNode*, BinaryOpInfo>, std::pair<ExpressionNode*, BinaryOpInfo>);
-    ExpressionNode* makeFunctionCallNode(const JSTokenLocation&, ExpressionNode* func, ArgumentsNode* args, const JSTextPosition& divotStart, const JSTextPosition& divot, const JSTextPosition& divotEnd);
+    ExpressionNode* makeFunctionCallNode(const JSTokenLocation&, ExpressionNode* func, ArgumentsNode* args, const JSTextPosition& divotStart, const JSTextPosition& divot, const JSTextPosition& divotEnd, size_t callOrApplyChildDepth);
 
     JSC::SourceElements* createSourceElements() { return new (m_parserArena) JSC::SourceElements(); }
 
@@ -1301,7 +1301,7 @@
     return new (m_parserArena) BitXOrNode(location, expr1, expr2, rightHasAssignments);
 }
 
-ExpressionNode* ASTBuilder::makeFunctionCallNode(const JSTokenLocation& location, ExpressionNode* func, ArgumentsNode* args, const JSTextPosition& divotStart, const JSTextPosition& divot, const JSTextPosition& divotEnd)
+ExpressionNode* ASTBuilder::makeFunctionCallNode(const JSTokenLocation& location, ExpressionNode* func, ArgumentsNode* args, const JSTextPosition& divotStart, const JSTextPosition& divot, const JSTextPosition& divotEnd, size_t callOrApplyChildDepth)
 {
     ASSERT(divot.offset >= divot.lineStartOffset);
     if (func->isSuperNode())
@@ -1333,9 +1333,9 @@
     DotAccessorNode* dot = static_cast<DotAccessorNode*>(func);
     FunctionCallDotNode* node;
     if (dot->identifier() == m_vm->propertyNames->builtinNames().callPublicName() || dot->identifier() == m_vm->propertyNames->builtinNames().callPrivateName())
-        node = new (m_parserArena) CallFunctionCallDotNode(location, dot->base(), dot->identifier(), args, divot, divotStart, divotEnd);
+        node = new (m_parserArena) CallFunctionCallDotNode(location, dot->base(), dot->identifier(), args, divot, divotStart, divotEnd, callOrApplyChildDepth);
     else if (dot->identifier() == m_vm->propertyNames->builtinNames().applyPublicName() || dot->identifier() == m_vm->propertyNames->builtinNames().applyPrivateName())
-        node = new (m_parserArena) ApplyFunctionCallDotNode(location, dot->base(), dot->identifier(), args, divot, divotStart, divotEnd);
+        node = new (m_parserArena) ApplyFunctionCallDotNode(location, dot->base(), dot->identifier(), args, divot, divotStart, divotEnd, callOrApplyChildDepth);
     else
         node = new (m_parserArena) FunctionCallDotNode(location, dot->base(), dot->identifier(), args, divot, divotStart, divotEnd);
     node->setSubexpressionInfo(dot->divot(), dot->divotEnd().offset);

Modified: trunk/Source/_javascript_Core/parser/NodeConstructors.h (215452 => 215453)


--- trunk/Source/_javascript_Core/parser/NodeConstructors.h	2017-04-18 06:05:25 UTC (rev 215452)
+++ trunk/Source/_javascript_Core/parser/NodeConstructors.h	2017-04-18 06:43:54 UTC (rev 215453)
@@ -403,13 +403,15 @@
     {
     }
 
-    inline CallFunctionCallDotNode::CallFunctionCallDotNode(const JSTokenLocation& location, ExpressionNode* base, const Identifier& ident, ArgumentsNode* args, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd)
+    inline CallFunctionCallDotNode::CallFunctionCallDotNode(const JSTokenLocation& location, ExpressionNode* base, const Identifier& ident, ArgumentsNode* args, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd, size_t callOrApplyChildDepth)
         : FunctionCallDotNode(location, base, ident, args, divot, divotStart, divotEnd)
+        , m_callOrApplyChildDepth(callOrApplyChildDepth)
     {
     }
 
-    inline ApplyFunctionCallDotNode::ApplyFunctionCallDotNode(const JSTokenLocation& location, ExpressionNode* base, const Identifier& ident, ArgumentsNode* args, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd)
+    inline ApplyFunctionCallDotNode::ApplyFunctionCallDotNode(const JSTokenLocation& location, ExpressionNode* base, const Identifier& ident, ArgumentsNode* args, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd, size_t callOrApplyChildDepth)
         : FunctionCallDotNode(location, base, ident, args, divot, divotStart, divotEnd)
+        , m_callOrApplyChildDepth(callOrApplyChildDepth)
     {
     }
 

Modified: trunk/Source/_javascript_Core/parser/Nodes.h (215452 => 215453)


--- trunk/Source/_javascript_Core/parser/Nodes.h	2017-04-18 06:05:25 UTC (rev 215452)
+++ trunk/Source/_javascript_Core/parser/Nodes.h	2017-04-18 06:43:54 UTC (rev 215453)
@@ -884,18 +884,20 @@
 
     class CallFunctionCallDotNode : public FunctionCallDotNode {
     public:
-        CallFunctionCallDotNode(const JSTokenLocation&, ExpressionNode* base, const Identifier&, ArgumentsNode*, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd);
+        CallFunctionCallDotNode(const JSTokenLocation&, ExpressionNode* base, const Identifier&, ArgumentsNode*, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd, size_t callOrApplyChildDepth);
 
     private:
         RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override;
+        size_t m_callOrApplyChildDepth;
     };
     
     class ApplyFunctionCallDotNode : public FunctionCallDotNode {
     public:
-        ApplyFunctionCallDotNode(const JSTokenLocation&, ExpressionNode* base, const Identifier&, ArgumentsNode*, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd);
+        ApplyFunctionCallDotNode(const JSTokenLocation&, ExpressionNode* base, const Identifier&, ArgumentsNode*, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd, size_t callOrApplyChildDepth);
 
     private:
         RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override;
+        size_t m_callOrApplyChildDepth;
     };
 
     class DeleteResolveNode : public ExpressionNode, public ThrowableExpressionData {

Modified: trunk/Source/_javascript_Core/parser/Parser.cpp (215452 => 215453)


--- trunk/Source/_javascript_Core/parser/Parser.cpp	2017-04-18 06:05:25 UTC (rev 215452)
+++ trunk/Source/_javascript_Core/parser/Parser.cpp	2017-04-18 06:43:54 UTC (rev 215453)
@@ -4384,6 +4384,22 @@
     return parseAssignmentExpression(context);
 }
 
+template <typename TreeBuilder, typename ParserType, typename = typename std::enable_if<std::is_same<TreeBuilder, ASTBuilder>::value>::type>
+static inline void recordCallOrApplyDepth(ParserType* parser, VM& vm, std::optional<typename ParserType::CallOrApplyDepth>& callOrApplyDepth, ExpressionNode* _expression_)
+{
+    if (_expression_->isDotAccessorNode()) {
+        DotAccessorNode* dot = static_cast<DotAccessorNode*>(_expression_);
+        bool isCallOrApply = dot->identifier() == vm.propertyNames->builtinNames().callPublicName() || dot->identifier() == vm.propertyNames->builtinNames().applyPublicName();
+        if (isCallOrApply)
+            callOrApplyDepth.emplace(parser);
+    }
+}
+
+template <typename TreeBuilder, typename ParserType, typename = typename std::enable_if<std::is_same<TreeBuilder, SyntaxChecker>::value>::type>
+static inline void recordCallOrApplyDepth(ParserType*, VM&, std::optional<typename ParserType::CallOrApplyDepth>&, SyntaxChecker::_expression_)
+{
+}
+
 template <typename LexerType>
 template <class TreeBuilder> TreeExpression Parser<LexerType>::parseMemberExpression(TreeBuilder& context)
 {
@@ -4498,6 +4514,9 @@
             } else {
                 size_t usedVariablesSize = currentScope()->currentUsedVariablesSize();
                 JSTextPosition expressionEnd = lastTokenEndPosition();
+                std::optional<CallOrApplyDepth> callOrApplyDepth;
+                recordCallOrApplyDepth<TreeBuilder>(this, *m_vm, callOrApplyDepth, base);
+
                 TreeArguments arguments = parseArguments(context);
 
                 if (baseIsAsyncKeyword && (!arguments || match(ARROWFUNCTION))) {
@@ -4525,7 +4544,8 @@
                     if (currentScope()->isArrowFunction())
                         functionScope->setInnerArrowFunctionUsesSuperCall();
                 }
-                base = context.makeFunctionCallNode(startLocation, base, arguments, expressionStart, expressionEnd, lastTokenEndPosition());
+                base = context.makeFunctionCallNode(startLocation, base, arguments, expressionStart,
+                    expressionEnd, lastTokenEndPosition(), callOrApplyDepth ? callOrApplyDepth->maxChildDepth() : 0);
             }
             m_parserState.nonLHSCount = nonLHSCount;
             break;

Modified: trunk/Source/_javascript_Core/parser/Parser.h (215452 => 215453)


--- trunk/Source/_javascript_Core/parser/Parser.h	2017-04-18 06:05:25 UTC (rev 215452)
+++ trunk/Source/_javascript_Core/parser/Parser.h	2017-04-18 06:43:54 UTC (rev 215453)
@@ -887,6 +887,37 @@
     JSTextPosition positionBeforeLastNewline() const { return m_lexer->positionBeforeLastNewline(); }
     JSTokenLocation locationBeforeLastToken() const { return m_lexer->lastTokenLocation(); }
 
+    struct CallOrApplyDepth {
+        CallOrApplyDepth(Parser* parser)
+            : m_parser(parser)
+            , m_parent(parser->m_callOrApplyDepth)
+            , m_depth(m_parent ? m_parent->m_depth + 1 : 0)
+            , m_childDepth(m_depth)
+        {
+            parser->m_callOrApplyDepth = this;
+        }
+
+        size_t maxChildDepth() const
+        {
+            ASSERT(m_childDepth >= m_depth);
+            return m_childDepth - m_depth;
+        }
+
+        ~CallOrApplyDepth()
+        {
+            if (m_parent)
+                m_parent->m_childDepth = std::max(m_childDepth, m_parent->m_childDepth);
+            m_parser->m_callOrApplyDepth = m_parent;
+        }
+
+    private:
+
+        Parser* m_parser;
+        CallOrApplyDepth* m_parent;
+        size_t m_depth;
+        size_t m_childDepth;
+    };
+
 private:
     struct AllowInOverride {
         AllowInOverride(Parser* parser)
@@ -1780,6 +1811,7 @@
     bool m_immediateParentAllowsFunctionDeclarationInStatement;
     RefPtr<ModuleScopeData> m_moduleScopeData;
     DebuggerParseData* m_debuggerParseData;
+    CallOrApplyDepth* m_callOrApplyDepth { nullptr };
 
     struct DepthManager {
         DepthManager(int* depth)

Modified: trunk/Source/_javascript_Core/parser/SyntaxChecker.h (215452 => 215453)


--- trunk/Source/_javascript_Core/parser/SyntaxChecker.h	2017-04-18 06:05:25 UTC (rev 215452)
+++ trunk/Source/_javascript_Core/parser/SyntaxChecker.h	2017-04-18 06:43:54 UTC (rev 215453)
@@ -143,7 +143,7 @@
     static const unsigned DontBuildStrings = LexerFlagsDontBuildStrings;
 
     int createSourceElements() { return SourceElementsResult; }
-    ExpressionType makeFunctionCallNode(const JSTokenLocation&, int, int, int, int, int) { return CallExpr; }
+    ExpressionType makeFunctionCallNode(const JSTokenLocation&, int, int, int, int, int, size_t) { return CallExpr; }
     ExpressionType createCommaExpr(const JSTokenLocation&, ExpressionType expr) { return expr; }
     ExpressionType appendToCommaExpr(const JSTokenLocation&, ExpressionType& head, ExpressionType, ExpressionType next) { head = next; return next; }
     ExpressionType makeAssignNode(const JSTokenLocation&, ExpressionType, Operator, ExpressionType, bool, bool, int, int, int) { return AssignmentExpr; }
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to