[fullcodegen] Implement control flow across do-expressions.
This implements proper handling of local control flow (i.e. break and continue) that spans the boundary of a do-expression. We can no longer determine the number of operands to be dropped from the nesting of statements alone, instead we use the new precise operand stack depth tracking. R=jarin@chromium.org TEST=mjsunit/harmony/do-expressions-control BUG=v8:4488 LOG=n Review URL: https://codereview.chromium.org/1724753002 Cr-Commit-Position: refs/heads/master@{#34246}
This commit is contained in:
parent
1c1b70c98d
commit
ee8108b71c
@ -994,14 +994,14 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
|
|||||||
|
|
||||||
FeedbackVectorSlot slot = stmt->ForInFeedbackSlot();
|
FeedbackVectorSlot slot = stmt->ForInFeedbackSlot();
|
||||||
|
|
||||||
Label loop, exit;
|
|
||||||
ForIn loop_statement(this, stmt);
|
|
||||||
increment_loop_depth();
|
|
||||||
|
|
||||||
// Get the object to enumerate over.
|
// Get the object to enumerate over.
|
||||||
SetExpressionAsStatementPosition(stmt->enumerable());
|
SetExpressionAsStatementPosition(stmt->enumerable());
|
||||||
VisitForAccumulatorValue(stmt->enumerable());
|
VisitForAccumulatorValue(stmt->enumerable());
|
||||||
OperandStackDepthIncrement(ForIn::kElementCount);
|
OperandStackDepthIncrement(5);
|
||||||
|
|
||||||
|
Label loop, exit;
|
||||||
|
Iteration loop_statement(this, stmt);
|
||||||
|
increment_loop_depth();
|
||||||
|
|
||||||
// If the object is null or undefined, skip over the loop, otherwise convert
|
// If the object is null or undefined, skip over the loop, otherwise convert
|
||||||
// it to a JS receiver. See ECMA-262 version 5, section 12.6.4.
|
// it to a JS receiver. See ECMA-262 version 5, section 12.6.4.
|
||||||
@ -1888,7 +1888,7 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
|
|||||||
case Yield::kSuspend:
|
case Yield::kSuspend:
|
||||||
// Pop value from top-of-stack slot; box result into result register.
|
// Pop value from top-of-stack slot; box result into result register.
|
||||||
EmitCreateIteratorResult(false);
|
EmitCreateIteratorResult(false);
|
||||||
__ push(result_register());
|
PushOperand(result_register());
|
||||||
// Fall through.
|
// Fall through.
|
||||||
case Yield::kInitial: {
|
case Yield::kInitial: {
|
||||||
Label suspend, continuation, post_runtime, resume;
|
Label suspend, continuation, post_runtime, resume;
|
||||||
@ -1907,6 +1907,7 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
|
|||||||
EmitUnwindAndReturn();
|
EmitUnwindAndReturn();
|
||||||
|
|
||||||
__ bind(&suspend);
|
__ bind(&suspend);
|
||||||
|
OperandStackDepthIncrement(1); // Not popped on this path.
|
||||||
VisitForAccumulatorValue(expr->generator_object());
|
VisitForAccumulatorValue(expr->generator_object());
|
||||||
DCHECK(continuation.pos() > 0 && Smi::IsValid(continuation.pos()));
|
DCHECK(continuation.pos() > 0 && Smi::IsValid(continuation.pos()));
|
||||||
__ mov(r1, Operand(Smi::FromInt(continuation.pos())));
|
__ mov(r1, Operand(Smi::FromInt(continuation.pos())));
|
||||||
@ -1932,7 +1933,6 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
|
|||||||
|
|
||||||
case Yield::kFinal: {
|
case Yield::kFinal: {
|
||||||
// Pop value from top-of-stack slot, box result into result register.
|
// Pop value from top-of-stack slot, box result into result register.
|
||||||
OperandStackDepthDecrement(1);
|
|
||||||
EmitCreateIteratorResult(true);
|
EmitCreateIteratorResult(true);
|
||||||
EmitUnwindAndReturn();
|
EmitUnwindAndReturn();
|
||||||
break;
|
break;
|
||||||
@ -2081,7 +2081,7 @@ void FullCodeGenerator::EmitCreateIteratorResult(bool done) {
|
|||||||
|
|
||||||
__ bind(&done_allocate);
|
__ bind(&done_allocate);
|
||||||
__ LoadNativeContextSlot(Context::ITERATOR_RESULT_MAP_INDEX, r1);
|
__ LoadNativeContextSlot(Context::ITERATOR_RESULT_MAP_INDEX, r1);
|
||||||
__ pop(r2);
|
PopOperand(r2);
|
||||||
__ LoadRoot(r3,
|
__ LoadRoot(r3,
|
||||||
done ? Heap::kTrueValueRootIndex : Heap::kFalseValueRootIndex);
|
done ? Heap::kTrueValueRootIndex : Heap::kFalseValueRootIndex);
|
||||||
__ LoadRoot(r4, Heap::kEmptyFixedArrayRootIndex);
|
__ LoadRoot(r4, Heap::kEmptyFixedArrayRootIndex);
|
||||||
|
@ -997,14 +997,14 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
|
|||||||
|
|
||||||
// TODO(all): This visitor probably needs better comments and a revisit.
|
// TODO(all): This visitor probably needs better comments and a revisit.
|
||||||
|
|
||||||
Label loop, exit;
|
|
||||||
ForIn loop_statement(this, stmt);
|
|
||||||
increment_loop_depth();
|
|
||||||
|
|
||||||
// Get the object to enumerate over.
|
// Get the object to enumerate over.
|
||||||
SetExpressionAsStatementPosition(stmt->enumerable());
|
SetExpressionAsStatementPosition(stmt->enumerable());
|
||||||
VisitForAccumulatorValue(stmt->enumerable());
|
VisitForAccumulatorValue(stmt->enumerable());
|
||||||
OperandStackDepthIncrement(ForIn::kElementCount);
|
OperandStackDepthIncrement(5);
|
||||||
|
|
||||||
|
Label loop, exit;
|
||||||
|
Iteration loop_statement(this, stmt);
|
||||||
|
increment_loop_depth();
|
||||||
|
|
||||||
// If the object is null or undefined, skip over the loop, otherwise convert
|
// If the object is null or undefined, skip over the loop, otherwise convert
|
||||||
// it to a JS receiver. See ECMA-262 version 5, section 12.6.4.
|
// it to a JS receiver. See ECMA-262 version 5, section 12.6.4.
|
||||||
@ -3874,7 +3874,7 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
|
|||||||
case Yield::kSuspend:
|
case Yield::kSuspend:
|
||||||
// Pop value from top-of-stack slot; box result into result register.
|
// Pop value from top-of-stack slot; box result into result register.
|
||||||
EmitCreateIteratorResult(false);
|
EmitCreateIteratorResult(false);
|
||||||
__ Push(result_register());
|
PushOperand(result_register());
|
||||||
// Fall through.
|
// Fall through.
|
||||||
case Yield::kInitial: {
|
case Yield::kInitial: {
|
||||||
Label suspend, continuation, post_runtime, resume;
|
Label suspend, continuation, post_runtime, resume;
|
||||||
@ -3896,6 +3896,7 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
|
|||||||
EmitUnwindAndReturn();
|
EmitUnwindAndReturn();
|
||||||
|
|
||||||
__ Bind(&suspend);
|
__ Bind(&suspend);
|
||||||
|
OperandStackDepthIncrement(1); // Not popped on this path.
|
||||||
VisitForAccumulatorValue(expr->generator_object());
|
VisitForAccumulatorValue(expr->generator_object());
|
||||||
DCHECK((continuation.pos() > 0) && Smi::IsValid(continuation.pos()));
|
DCHECK((continuation.pos() > 0) && Smi::IsValid(continuation.pos()));
|
||||||
__ Mov(x1, Smi::FromInt(continuation.pos()));
|
__ Mov(x1, Smi::FromInt(continuation.pos()));
|
||||||
@ -3921,7 +3922,6 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
|
|||||||
|
|
||||||
case Yield::kFinal: {
|
case Yield::kFinal: {
|
||||||
// Pop value from top-of-stack slot, box result into result register.
|
// Pop value from top-of-stack slot, box result into result register.
|
||||||
OperandStackDepthDecrement(1);
|
|
||||||
EmitCreateIteratorResult(true);
|
EmitCreateIteratorResult(true);
|
||||||
EmitUnwindAndReturn();
|
EmitUnwindAndReturn();
|
||||||
break;
|
break;
|
||||||
@ -4079,7 +4079,7 @@ void FullCodeGenerator::EmitCreateIteratorResult(bool done) {
|
|||||||
Register empty_fixed_array = x4;
|
Register empty_fixed_array = x4;
|
||||||
Register untagged_result = x5;
|
Register untagged_result = x5;
|
||||||
__ LoadNativeContextSlot(Context::ITERATOR_RESULT_MAP_INDEX, map_reg);
|
__ LoadNativeContextSlot(Context::ITERATOR_RESULT_MAP_INDEX, map_reg);
|
||||||
__ Pop(result_value);
|
PopOperand(result_value);
|
||||||
__ LoadRoot(boolean_done,
|
__ LoadRoot(boolean_done,
|
||||||
done ? Heap::kTrueValueRootIndex : Heap::kFalseValueRootIndex);
|
done ? Heap::kTrueValueRootIndex : Heap::kFalseValueRootIndex);
|
||||||
__ LoadRoot(empty_fixed_array, Heap::kEmptyFixedArrayRootIndex);
|
__ LoadRoot(empty_fixed_array, Heap::kEmptyFixedArrayRootIndex);
|
||||||
|
@ -913,7 +913,6 @@ void FullCodeGenerator::VisitIfStatement(IfStatement* stmt) {
|
|||||||
|
|
||||||
void FullCodeGenerator::EmitContinue(Statement* target) {
|
void FullCodeGenerator::EmitContinue(Statement* target) {
|
||||||
NestedStatement* current = nesting_stack_;
|
NestedStatement* current = nesting_stack_;
|
||||||
int stack_depth = 0;
|
|
||||||
int context_length = 0;
|
int context_length = 0;
|
||||||
// When continuing, we clobber the unpredictable value in the accumulator
|
// When continuing, we clobber the unpredictable value in the accumulator
|
||||||
// with one that's safe for GC. If we hit an exit from the try block of
|
// with one that's safe for GC. If we hit an exit from the try block of
|
||||||
@ -923,15 +922,17 @@ void FullCodeGenerator::EmitContinue(Statement* target) {
|
|||||||
while (!current->IsContinueTarget(target)) {
|
while (!current->IsContinueTarget(target)) {
|
||||||
if (current->IsTryFinally()) {
|
if (current->IsTryFinally()) {
|
||||||
Comment cmnt(masm(), "[ Deferred continue through finally");
|
Comment cmnt(masm(), "[ Deferred continue through finally");
|
||||||
current->Exit(&stack_depth, &context_length);
|
current->Exit(&context_length);
|
||||||
DCHECK_EQ(0, stack_depth);
|
DCHECK_EQ(-1, context_length);
|
||||||
DCHECK_EQ(0, context_length);
|
|
||||||
current->AsTryFinally()->deferred_commands()->RecordContinue(target);
|
current->AsTryFinally()->deferred_commands()->RecordContinue(target);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
current = current->Exit(&stack_depth, &context_length);
|
current = current->Exit(&context_length);
|
||||||
}
|
}
|
||||||
__ Drop(stack_depth);
|
int stack_depth = current->GetStackDepthAtTarget();
|
||||||
|
int stack_drop = operand_stack_depth_ - stack_depth;
|
||||||
|
DCHECK_GE(stack_drop, 0);
|
||||||
|
__ Drop(stack_drop);
|
||||||
if (context_length > 0) {
|
if (context_length > 0) {
|
||||||
while (context_length > 0) {
|
while (context_length > 0) {
|
||||||
LoadContextField(context_register(), Context::PREVIOUS_INDEX);
|
LoadContextField(context_register(), Context::PREVIOUS_INDEX);
|
||||||
@ -952,7 +953,6 @@ void FullCodeGenerator::VisitContinueStatement(ContinueStatement* stmt) {
|
|||||||
|
|
||||||
void FullCodeGenerator::EmitBreak(Statement* target) {
|
void FullCodeGenerator::EmitBreak(Statement* target) {
|
||||||
NestedStatement* current = nesting_stack_;
|
NestedStatement* current = nesting_stack_;
|
||||||
int stack_depth = 0;
|
|
||||||
int context_length = 0;
|
int context_length = 0;
|
||||||
// When breaking, we clobber the unpredictable value in the accumulator
|
// When breaking, we clobber the unpredictable value in the accumulator
|
||||||
// with one that's safe for GC. If we hit an exit from the try block of
|
// with one that's safe for GC. If we hit an exit from the try block of
|
||||||
@ -962,15 +962,17 @@ void FullCodeGenerator::EmitBreak(Statement* target) {
|
|||||||
while (!current->IsBreakTarget(target)) {
|
while (!current->IsBreakTarget(target)) {
|
||||||
if (current->IsTryFinally()) {
|
if (current->IsTryFinally()) {
|
||||||
Comment cmnt(masm(), "[ Deferred break through finally");
|
Comment cmnt(masm(), "[ Deferred break through finally");
|
||||||
current->Exit(&stack_depth, &context_length);
|
current->Exit(&context_length);
|
||||||
DCHECK_EQ(0, stack_depth);
|
DCHECK_EQ(-1, context_length);
|
||||||
DCHECK_EQ(0, context_length);
|
|
||||||
current->AsTryFinally()->deferred_commands()->RecordBreak(target);
|
current->AsTryFinally()->deferred_commands()->RecordBreak(target);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
current = current->Exit(&stack_depth, &context_length);
|
current = current->Exit(&context_length);
|
||||||
}
|
}
|
||||||
__ Drop(stack_depth);
|
int stack_depth = current->GetStackDepthAtTarget();
|
||||||
|
int stack_drop = operand_stack_depth_ - stack_depth;
|
||||||
|
DCHECK_GE(stack_drop, 0);
|
||||||
|
__ Drop(stack_drop);
|
||||||
if (context_length > 0) {
|
if (context_length > 0) {
|
||||||
while (context_length > 0) {
|
while (context_length > 0) {
|
||||||
LoadContextField(context_register(), Context::PREVIOUS_INDEX);
|
LoadContextField(context_register(), Context::PREVIOUS_INDEX);
|
||||||
@ -991,20 +993,17 @@ void FullCodeGenerator::VisitBreakStatement(BreakStatement* stmt) {
|
|||||||
|
|
||||||
void FullCodeGenerator::EmitUnwindAndReturn() {
|
void FullCodeGenerator::EmitUnwindAndReturn() {
|
||||||
NestedStatement* current = nesting_stack_;
|
NestedStatement* current = nesting_stack_;
|
||||||
int stack_depth = 0;
|
|
||||||
int context_length = 0;
|
int context_length = 0;
|
||||||
while (current != NULL) {
|
while (current != NULL) {
|
||||||
if (current->IsTryFinally()) {
|
if (current->IsTryFinally()) {
|
||||||
Comment cmnt(masm(), "[ Deferred return through finally");
|
Comment cmnt(masm(), "[ Deferred return through finally");
|
||||||
current->Exit(&stack_depth, &context_length);
|
current->Exit(&context_length);
|
||||||
DCHECK_EQ(0, stack_depth);
|
DCHECK_EQ(-1, context_length);
|
||||||
DCHECK_EQ(0, context_length);
|
|
||||||
current->AsTryFinally()->deferred_commands()->RecordReturn();
|
current->AsTryFinally()->deferred_commands()->RecordReturn();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
current = current->Exit(&stack_depth, &context_length);
|
current = current->Exit(&context_length);
|
||||||
}
|
}
|
||||||
__ Drop(stack_depth);
|
|
||||||
EmitReturnSequence();
|
EmitReturnSequence();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1281,7 +1280,8 @@ void FullCodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) {
|
|||||||
try_catch_depth_++;
|
try_catch_depth_++;
|
||||||
int handler_index = NewHandlerTableEntry();
|
int handler_index = NewHandlerTableEntry();
|
||||||
EnterTryBlock(handler_index, &handler_entry);
|
EnterTryBlock(handler_index, &handler_entry);
|
||||||
{ TryCatch try_body(this);
|
{
|
||||||
|
Comment cmnt_try(masm(), "[ Try block");
|
||||||
Visit(stmt->try_block());
|
Visit(stmt->try_block());
|
||||||
}
|
}
|
||||||
ExitTryBlock(handler_index);
|
ExitTryBlock(handler_index);
|
||||||
@ -1322,7 +1322,7 @@ void FullCodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
|
|||||||
// Exception handler code. This code is only executed when an exception
|
// Exception handler code. This code is only executed when an exception
|
||||||
// is thrown. Record the continuation and jump to the finally block.
|
// is thrown. Record the continuation and jump to the finally block.
|
||||||
{
|
{
|
||||||
Comment cmt_handler(masm(), "[ Finally handler");
|
Comment cmnt_handler(masm(), "[ Finally handler");
|
||||||
deferred.RecordThrow();
|
deferred.RecordThrow();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1331,6 +1331,7 @@ void FullCodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
|
|||||||
int handler_index = NewHandlerTableEntry();
|
int handler_index = NewHandlerTableEntry();
|
||||||
EnterTryBlock(handler_index, &handler_entry);
|
EnterTryBlock(handler_index, &handler_entry);
|
||||||
{
|
{
|
||||||
|
Comment cmnt_try(masm(), "[ Try block");
|
||||||
TryFinally try_body(this, &deferred);
|
TryFinally try_body(this, &deferred);
|
||||||
Visit(stmt->try_block());
|
Visit(stmt->try_block());
|
||||||
}
|
}
|
||||||
@ -1345,15 +1346,14 @@ void FullCodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
|
|||||||
|
|
||||||
// Finally block implementation.
|
// Finally block implementation.
|
||||||
__ bind(&finally_entry);
|
__ bind(&finally_entry);
|
||||||
Comment cmnt_finally(masm(), "[ Finally block");
|
|
||||||
OperandStackDepthIncrement(2); // Token and accumulator are on stack.
|
|
||||||
EnterFinallyBlock();
|
|
||||||
{
|
{
|
||||||
Finally finally_body(this);
|
Comment cmnt_finally(masm(), "[ Finally block");
|
||||||
|
OperandStackDepthIncrement(2); // Token and accumulator are on stack.
|
||||||
|
EnterFinallyBlock();
|
||||||
Visit(stmt->finally_block());
|
Visit(stmt->finally_block());
|
||||||
|
ExitFinallyBlock();
|
||||||
|
OperandStackDepthDecrement(2); // Token and accumulator were on stack.
|
||||||
}
|
}
|
||||||
ExitFinallyBlock();
|
|
||||||
OperandStackDepthDecrement(2); // Token and accumulator were on stack.
|
|
||||||
|
|
||||||
{
|
{
|
||||||
Comment cmnt_deferred(masm(), "[ Post-finally dispatch");
|
Comment cmnt_deferred(masm(), "[ Post-finally dispatch");
|
||||||
@ -1599,28 +1599,32 @@ void FullCodeGenerator::VisitRewritableExpression(RewritableExpression* expr) {
|
|||||||
Visit(expr->expression());
|
Visit(expr->expression());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
FullCodeGenerator::NestedStatement* FullCodeGenerator::TryFinally::Exit(
|
FullCodeGenerator::NestedStatement* FullCodeGenerator::TryFinally::Exit(
|
||||||
int* stack_depth, int* context_length) {
|
int* context_length) {
|
||||||
// The macros used here must preserve the result register.
|
// The macros used here must preserve the result register.
|
||||||
|
|
||||||
|
// Calculate how many operands to drop to get down to handler block.
|
||||||
|
int stack_drop = codegen_->operand_stack_depth_ - GetStackDepthAtTarget();
|
||||||
|
DCHECK_GE(stack_drop, 0);
|
||||||
|
|
||||||
// Because the handler block contains the context of the finally
|
// Because the handler block contains the context of the finally
|
||||||
// code, we can restore it directly from there for the finally code
|
// code, we can restore it directly from there for the finally code
|
||||||
// rather than iteratively unwinding contexts via their previous
|
// rather than iteratively unwinding contexts via their previous
|
||||||
// links.
|
// links.
|
||||||
if (*context_length > 0) {
|
if (*context_length > 0) {
|
||||||
__ Drop(*stack_depth); // Down to the handler block.
|
__ Drop(stack_drop); // Down to the handler block.
|
||||||
// Restore the context to its dedicated register and the stack.
|
// Restore the context to its dedicated register and the stack.
|
||||||
STATIC_ASSERT(TryFinally::kElementCount == 1);
|
STATIC_ASSERT(TryBlockConstant::kElementCount == 1);
|
||||||
__ Pop(codegen_->context_register());
|
__ Pop(codegen_->context_register());
|
||||||
codegen_->StoreToFrameField(StandardFrameConstants::kContextOffset,
|
codegen_->StoreToFrameField(StandardFrameConstants::kContextOffset,
|
||||||
codegen_->context_register());
|
codegen_->context_register());
|
||||||
} else {
|
} else {
|
||||||
// Down to the handler block and also drop context.
|
// Down to the handler block and also drop context.
|
||||||
__ Drop(*stack_depth + kElementCount);
|
__ Drop(stack_drop + TryBlockConstant::kElementCount);
|
||||||
}
|
}
|
||||||
*stack_depth = 0;
|
|
||||||
*context_length = 0;
|
// The caller will ignore outputs.
|
||||||
|
*context_length = -1;
|
||||||
return previous_;
|
return previous_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +108,9 @@ class FullCodeGenerator: public AstVisitor {
|
|||||||
|
|
||||||
class NestedStatement BASE_EMBEDDED {
|
class NestedStatement BASE_EMBEDDED {
|
||||||
public:
|
public:
|
||||||
explicit NestedStatement(FullCodeGenerator* codegen) : codegen_(codegen) {
|
explicit NestedStatement(FullCodeGenerator* codegen)
|
||||||
|
: codegen_(codegen),
|
||||||
|
stack_depth_at_target_(codegen->operand_stack_depth_) {
|
||||||
// Link into codegen's nesting stack.
|
// Link into codegen's nesting stack.
|
||||||
previous_ = codegen->nesting_stack_;
|
previous_ = codegen->nesting_stack_;
|
||||||
codegen->nesting_stack_ = this;
|
codegen->nesting_stack_ = this;
|
||||||
@ -130,18 +132,20 @@ class FullCodeGenerator: public AstVisitor {
|
|||||||
// Notify the statement that we are exiting it via break, continue, or
|
// Notify the statement that we are exiting it via break, continue, or
|
||||||
// return and give it a chance to generate cleanup code. Return the
|
// return and give it a chance to generate cleanup code. Return the
|
||||||
// next outer statement in the nesting stack. We accumulate in
|
// next outer statement in the nesting stack. We accumulate in
|
||||||
// *stack_depth the amount to drop the stack and in *context_length the
|
// {*context_length} the number of context chain links to unwind as we
|
||||||
// number of context chain links to unwind as we traverse the nesting
|
// traverse the nesting stack from an exit to its target.
|
||||||
// stack from an exit to its target.
|
virtual NestedStatement* Exit(int* context_length) { return previous_; }
|
||||||
virtual NestedStatement* Exit(int* stack_depth, int* context_length) {
|
|
||||||
return previous_;
|
// Determine the expected operand stack depth when this statement is being
|
||||||
}
|
// used as the target of an exit. The caller will drop to this depth.
|
||||||
|
int GetStackDepthAtTarget() { return stack_depth_at_target_; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
MacroAssembler* masm() { return codegen_->masm(); }
|
MacroAssembler* masm() { return codegen_->masm(); }
|
||||||
|
|
||||||
FullCodeGenerator* codegen_;
|
FullCodeGenerator* codegen_;
|
||||||
NestedStatement* previous_;
|
NestedStatement* previous_;
|
||||||
|
int stack_depth_at_target_;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DISALLOW_COPY_AND_ASSIGN(NestedStatement);
|
DISALLOW_COPY_AND_ASSIGN(NestedStatement);
|
||||||
@ -192,7 +196,7 @@ class FullCodeGenerator: public AstVisitor {
|
|||||||
: Breakable(codegen, block) {
|
: Breakable(codegen, block) {
|
||||||
}
|
}
|
||||||
|
|
||||||
NestedStatement* Exit(int* stack_depth, int* context_length) override {
|
NestedStatement* Exit(int* context_length) override {
|
||||||
auto block_scope = statement()->AsBlock()->scope();
|
auto block_scope = statement()->AsBlock()->scope();
|
||||||
if (block_scope != nullptr) {
|
if (block_scope != nullptr) {
|
||||||
if (block_scope->ContextLocalCount() > 0) ++(*context_length);
|
if (block_scope->ContextLocalCount() > 0) ++(*context_length);
|
||||||
@ -201,19 +205,6 @@ class FullCodeGenerator: public AstVisitor {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// The try block of a try/catch statement.
|
|
||||||
class TryCatch : public NestedStatement {
|
|
||||||
public:
|
|
||||||
static const int kElementCount = TryBlockConstant::kElementCount;
|
|
||||||
|
|
||||||
explicit TryCatch(FullCodeGenerator* codegen) : NestedStatement(codegen) {}
|
|
||||||
|
|
||||||
NestedStatement* Exit(int* stack_depth, int* context_length) override {
|
|
||||||
*stack_depth += kElementCount;
|
|
||||||
return previous_;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class DeferredCommands {
|
class DeferredCommands {
|
||||||
public:
|
public:
|
||||||
enum Command { kReturn, kThrow, kBreak, kContinue };
|
enum Command { kReturn, kThrow, kBreak, kContinue };
|
||||||
@ -254,12 +245,10 @@ class FullCodeGenerator: public AstVisitor {
|
|||||||
// The try block of a try/finally statement.
|
// The try block of a try/finally statement.
|
||||||
class TryFinally : public NestedStatement {
|
class TryFinally : public NestedStatement {
|
||||||
public:
|
public:
|
||||||
static const int kElementCount = TryBlockConstant::kElementCount;
|
|
||||||
|
|
||||||
TryFinally(FullCodeGenerator* codegen, DeferredCommands* commands)
|
TryFinally(FullCodeGenerator* codegen, DeferredCommands* commands)
|
||||||
: NestedStatement(codegen), deferred_commands_(commands) {}
|
: NestedStatement(codegen), deferred_commands_(commands) {}
|
||||||
|
|
||||||
NestedStatement* Exit(int* stack_depth, int* context_length) override;
|
NestedStatement* Exit(int* context_length) override;
|
||||||
|
|
||||||
bool IsTryFinally() override { return true; }
|
bool IsTryFinally() override { return true; }
|
||||||
TryFinally* AsTryFinally() override { return this; }
|
TryFinally* AsTryFinally() override { return this; }
|
||||||
@ -270,35 +259,6 @@ class FullCodeGenerator: public AstVisitor {
|
|||||||
DeferredCommands* deferred_commands_;
|
DeferredCommands* deferred_commands_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// The finally block of a try/finally statement.
|
|
||||||
class Finally : public NestedStatement {
|
|
||||||
public:
|
|
||||||
static const int kElementCount = 3;
|
|
||||||
|
|
||||||
explicit Finally(FullCodeGenerator* codegen) : NestedStatement(codegen) {}
|
|
||||||
|
|
||||||
NestedStatement* Exit(int* stack_depth, int* context_length) override {
|
|
||||||
*stack_depth += kElementCount;
|
|
||||||
return previous_;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// The body of a for/in loop.
|
|
||||||
class ForIn : public Iteration {
|
|
||||||
public:
|
|
||||||
static const int kElementCount = 5;
|
|
||||||
|
|
||||||
ForIn(FullCodeGenerator* codegen, ForInStatement* statement)
|
|
||||||
: Iteration(codegen, statement) {
|
|
||||||
}
|
|
||||||
|
|
||||||
NestedStatement* Exit(int* stack_depth, int* context_length) override {
|
|
||||||
*stack_depth += kElementCount;
|
|
||||||
return previous_;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// The body of a with or catch.
|
// The body of a with or catch.
|
||||||
class WithOrCatch : public NestedStatement {
|
class WithOrCatch : public NestedStatement {
|
||||||
public:
|
public:
|
||||||
@ -306,7 +266,7 @@ class FullCodeGenerator: public AstVisitor {
|
|||||||
: NestedStatement(codegen) {
|
: NestedStatement(codegen) {
|
||||||
}
|
}
|
||||||
|
|
||||||
NestedStatement* Exit(int* stack_depth, int* context_length) override {
|
NestedStatement* Exit(int* context_length) override {
|
||||||
++(*context_length);
|
++(*context_length);
|
||||||
return previous_;
|
return previous_;
|
||||||
}
|
}
|
||||||
|
@ -934,14 +934,14 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
|
|||||||
|
|
||||||
FeedbackVectorSlot slot = stmt->ForInFeedbackSlot();
|
FeedbackVectorSlot slot = stmt->ForInFeedbackSlot();
|
||||||
|
|
||||||
Label loop, exit;
|
|
||||||
ForIn loop_statement(this, stmt);
|
|
||||||
increment_loop_depth();
|
|
||||||
|
|
||||||
// Get the object to enumerate over.
|
// Get the object to enumerate over.
|
||||||
SetExpressionAsStatementPosition(stmt->enumerable());
|
SetExpressionAsStatementPosition(stmt->enumerable());
|
||||||
VisitForAccumulatorValue(stmt->enumerable());
|
VisitForAccumulatorValue(stmt->enumerable());
|
||||||
OperandStackDepthIncrement(ForIn::kElementCount);
|
OperandStackDepthIncrement(5);
|
||||||
|
|
||||||
|
Label loop, exit;
|
||||||
|
Iteration loop_statement(this, stmt);
|
||||||
|
increment_loop_depth();
|
||||||
|
|
||||||
// If the object is null or undefined, skip over the loop, otherwise convert
|
// If the object is null or undefined, skip over the loop, otherwise convert
|
||||||
// it to a JS receiver. See ECMA-262 version 5, section 12.6.4.
|
// it to a JS receiver. See ECMA-262 version 5, section 12.6.4.
|
||||||
@ -1089,8 +1089,7 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
|
|||||||
|
|
||||||
// Remove the pointers stored on the stack.
|
// Remove the pointers stored on the stack.
|
||||||
__ bind(loop_statement.break_label());
|
__ bind(loop_statement.break_label());
|
||||||
__ add(esp, Immediate(5 * kPointerSize));
|
DropOperands(5);
|
||||||
OperandStackDepthDecrement(ForIn::kElementCount);
|
|
||||||
|
|
||||||
// Exit and decrement the loop depth.
|
// Exit and decrement the loop depth.
|
||||||
PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS);
|
PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS);
|
||||||
@ -1805,7 +1804,7 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
|
|||||||
case Yield::kSuspend:
|
case Yield::kSuspend:
|
||||||
// Pop value from top-of-stack slot; box result into result register.
|
// Pop value from top-of-stack slot; box result into result register.
|
||||||
EmitCreateIteratorResult(false);
|
EmitCreateIteratorResult(false);
|
||||||
__ push(result_register());
|
PushOperand(result_register());
|
||||||
// Fall through.
|
// Fall through.
|
||||||
case Yield::kInitial: {
|
case Yield::kInitial: {
|
||||||
Label suspend, continuation, post_runtime, resume;
|
Label suspend, continuation, post_runtime, resume;
|
||||||
@ -1824,6 +1823,7 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
|
|||||||
EmitUnwindAndReturn();
|
EmitUnwindAndReturn();
|
||||||
|
|
||||||
__ bind(&suspend);
|
__ bind(&suspend);
|
||||||
|
OperandStackDepthIncrement(1); // Not popped on this path.
|
||||||
VisitForAccumulatorValue(expr->generator_object());
|
VisitForAccumulatorValue(expr->generator_object());
|
||||||
DCHECK(continuation.pos() > 0 && Smi::IsValid(continuation.pos()));
|
DCHECK(continuation.pos() > 0 && Smi::IsValid(continuation.pos()));
|
||||||
__ mov(FieldOperand(eax, JSGeneratorObject::kContinuationOffset),
|
__ mov(FieldOperand(eax, JSGeneratorObject::kContinuationOffset),
|
||||||
@ -1850,7 +1850,6 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
|
|||||||
|
|
||||||
case Yield::kFinal: {
|
case Yield::kFinal: {
|
||||||
// Pop value from top-of-stack slot, box result into result register.
|
// Pop value from top-of-stack slot, box result into result register.
|
||||||
OperandStackDepthDecrement(1);
|
|
||||||
EmitCreateIteratorResult(true);
|
EmitCreateIteratorResult(true);
|
||||||
EmitUnwindAndReturn();
|
EmitUnwindAndReturn();
|
||||||
break;
|
break;
|
||||||
@ -1991,6 +1990,7 @@ void FullCodeGenerator::EmitCreateIteratorResult(bool done) {
|
|||||||
__ mov(FieldOperand(eax, JSIteratorResult::kDoneOffset),
|
__ mov(FieldOperand(eax, JSIteratorResult::kDoneOffset),
|
||||||
isolate()->factory()->ToBoolean(done));
|
isolate()->factory()->ToBoolean(done));
|
||||||
STATIC_ASSERT(JSIteratorResult::kSize == 5 * kPointerSize);
|
STATIC_ASSERT(JSIteratorResult::kSize == 5 * kPointerSize);
|
||||||
|
OperandStackDepthDecrement(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -990,15 +990,15 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
|
|||||||
|
|
||||||
FeedbackVectorSlot slot = stmt->ForInFeedbackSlot();
|
FeedbackVectorSlot slot = stmt->ForInFeedbackSlot();
|
||||||
|
|
||||||
Label loop, exit;
|
|
||||||
ForIn loop_statement(this, stmt);
|
|
||||||
increment_loop_depth();
|
|
||||||
|
|
||||||
// Get the object to enumerate over.
|
// Get the object to enumerate over.
|
||||||
SetExpressionAsStatementPosition(stmt->enumerable());
|
SetExpressionAsStatementPosition(stmt->enumerable());
|
||||||
VisitForAccumulatorValue(stmt->enumerable());
|
VisitForAccumulatorValue(stmt->enumerable());
|
||||||
__ mov(a0, result_register());
|
__ mov(a0, result_register());
|
||||||
OperandStackDepthIncrement(ForIn::kElementCount);
|
OperandStackDepthIncrement(5);
|
||||||
|
|
||||||
|
Label loop, exit;
|
||||||
|
Iteration loop_statement(this, stmt);
|
||||||
|
increment_loop_depth();
|
||||||
|
|
||||||
// If the object is null or undefined, skip over the loop, otherwise convert
|
// If the object is null or undefined, skip over the loop, otherwise convert
|
||||||
// it to a JS receiver. See ECMA-262 version 5, section 12.6.4.
|
// it to a JS receiver. See ECMA-262 version 5, section 12.6.4.
|
||||||
@ -1886,7 +1886,7 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
|
|||||||
case Yield::kSuspend:
|
case Yield::kSuspend:
|
||||||
// Pop value from top-of-stack slot; box result into result register.
|
// Pop value from top-of-stack slot; box result into result register.
|
||||||
EmitCreateIteratorResult(false);
|
EmitCreateIteratorResult(false);
|
||||||
__ push(result_register());
|
PushOperand(result_register());
|
||||||
// Fall through.
|
// Fall through.
|
||||||
case Yield::kInitial: {
|
case Yield::kInitial: {
|
||||||
Label suspend, continuation, post_runtime, resume;
|
Label suspend, continuation, post_runtime, resume;
|
||||||
@ -1905,6 +1905,7 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
|
|||||||
EmitUnwindAndReturn();
|
EmitUnwindAndReturn();
|
||||||
|
|
||||||
__ bind(&suspend);
|
__ bind(&suspend);
|
||||||
|
OperandStackDepthIncrement(1); // Not popped on this path.
|
||||||
VisitForAccumulatorValue(expr->generator_object());
|
VisitForAccumulatorValue(expr->generator_object());
|
||||||
DCHECK(continuation.pos() > 0 && Smi::IsValid(continuation.pos()));
|
DCHECK(continuation.pos() > 0 && Smi::IsValid(continuation.pos()));
|
||||||
__ li(a1, Operand(Smi::FromInt(continuation.pos())));
|
__ li(a1, Operand(Smi::FromInt(continuation.pos())));
|
||||||
@ -1929,7 +1930,6 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
|
|||||||
|
|
||||||
case Yield::kFinal: {
|
case Yield::kFinal: {
|
||||||
// Pop value from top-of-stack slot, box result into result register.
|
// Pop value from top-of-stack slot, box result into result register.
|
||||||
OperandStackDepthDecrement(1);
|
|
||||||
EmitCreateIteratorResult(true);
|
EmitCreateIteratorResult(true);
|
||||||
EmitUnwindAndReturn();
|
EmitUnwindAndReturn();
|
||||||
break;
|
break;
|
||||||
@ -2079,7 +2079,7 @@ void FullCodeGenerator::EmitCreateIteratorResult(bool done) {
|
|||||||
|
|
||||||
__ bind(&done_allocate);
|
__ bind(&done_allocate);
|
||||||
__ LoadNativeContextSlot(Context::ITERATOR_RESULT_MAP_INDEX, a1);
|
__ LoadNativeContextSlot(Context::ITERATOR_RESULT_MAP_INDEX, a1);
|
||||||
__ pop(a2);
|
PopOperand(a2);
|
||||||
__ LoadRoot(a3,
|
__ LoadRoot(a3,
|
||||||
done ? Heap::kTrueValueRootIndex : Heap::kFalseValueRootIndex);
|
done ? Heap::kTrueValueRootIndex : Heap::kFalseValueRootIndex);
|
||||||
__ LoadRoot(t0, Heap::kEmptyFixedArrayRootIndex);
|
__ LoadRoot(t0, Heap::kEmptyFixedArrayRootIndex);
|
||||||
|
@ -990,16 +990,16 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
|
|||||||
|
|
||||||
FeedbackVectorSlot slot = stmt->ForInFeedbackSlot();
|
FeedbackVectorSlot slot = stmt->ForInFeedbackSlot();
|
||||||
|
|
||||||
Label loop, exit;
|
|
||||||
ForIn loop_statement(this, stmt);
|
|
||||||
increment_loop_depth();
|
|
||||||
|
|
||||||
// Get the object to enumerate over. If the object is null or undefined, skip
|
// Get the object to enumerate over. If the object is null or undefined, skip
|
||||||
// over the loop. See ECMA-262 version 5, section 12.6.4.
|
// over the loop. See ECMA-262 version 5, section 12.6.4.
|
||||||
SetExpressionAsStatementPosition(stmt->enumerable());
|
SetExpressionAsStatementPosition(stmt->enumerable());
|
||||||
VisitForAccumulatorValue(stmt->enumerable());
|
VisitForAccumulatorValue(stmt->enumerable());
|
||||||
__ mov(a0, result_register());
|
__ mov(a0, result_register());
|
||||||
OperandStackDepthIncrement(ForIn::kElementCount);
|
OperandStackDepthIncrement(5);
|
||||||
|
|
||||||
|
Label loop, exit;
|
||||||
|
Iteration loop_statement(this, stmt);
|
||||||
|
increment_loop_depth();
|
||||||
|
|
||||||
// If the object is null or undefined, skip over the loop, otherwise convert
|
// If the object is null or undefined, skip over the loop, otherwise convert
|
||||||
// it to a JS receiver. See ECMA-262 version 5, section 12.6.4.
|
// it to a JS receiver. See ECMA-262 version 5, section 12.6.4.
|
||||||
@ -1888,7 +1888,7 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
|
|||||||
case Yield::kSuspend:
|
case Yield::kSuspend:
|
||||||
// Pop value from top-of-stack slot; box result into result register.
|
// Pop value from top-of-stack slot; box result into result register.
|
||||||
EmitCreateIteratorResult(false);
|
EmitCreateIteratorResult(false);
|
||||||
__ push(result_register());
|
PushOperand(result_register());
|
||||||
// Fall through.
|
// Fall through.
|
||||||
case Yield::kInitial: {
|
case Yield::kInitial: {
|
||||||
Label suspend, continuation, post_runtime, resume;
|
Label suspend, continuation, post_runtime, resume;
|
||||||
@ -1907,6 +1907,7 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
|
|||||||
EmitUnwindAndReturn();
|
EmitUnwindAndReturn();
|
||||||
|
|
||||||
__ bind(&suspend);
|
__ bind(&suspend);
|
||||||
|
OperandStackDepthIncrement(1); // Not popped on this path.
|
||||||
VisitForAccumulatorValue(expr->generator_object());
|
VisitForAccumulatorValue(expr->generator_object());
|
||||||
DCHECK(continuation.pos() > 0 && Smi::IsValid(continuation.pos()));
|
DCHECK(continuation.pos() > 0 && Smi::IsValid(continuation.pos()));
|
||||||
__ li(a1, Operand(Smi::FromInt(continuation.pos())));
|
__ li(a1, Operand(Smi::FromInt(continuation.pos())));
|
||||||
@ -1931,7 +1932,6 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
|
|||||||
|
|
||||||
case Yield::kFinal: {
|
case Yield::kFinal: {
|
||||||
// Pop value from top-of-stack slot, box result into result register.
|
// Pop value from top-of-stack slot, box result into result register.
|
||||||
OperandStackDepthDecrement(1);
|
|
||||||
EmitCreateIteratorResult(true);
|
EmitCreateIteratorResult(true);
|
||||||
EmitUnwindAndReturn();
|
EmitUnwindAndReturn();
|
||||||
break;
|
break;
|
||||||
@ -2083,7 +2083,7 @@ void FullCodeGenerator::EmitCreateIteratorResult(bool done) {
|
|||||||
|
|
||||||
__ bind(&done_allocate);
|
__ bind(&done_allocate);
|
||||||
__ LoadNativeContextSlot(Context::ITERATOR_RESULT_MAP_INDEX, a1);
|
__ LoadNativeContextSlot(Context::ITERATOR_RESULT_MAP_INDEX, a1);
|
||||||
__ pop(a2);
|
PopOperand(a2);
|
||||||
__ LoadRoot(a3,
|
__ LoadRoot(a3,
|
||||||
done ? Heap::kTrueValueRootIndex : Heap::kFalseValueRootIndex);
|
done ? Heap::kTrueValueRootIndex : Heap::kFalseValueRootIndex);
|
||||||
__ LoadRoot(a4, Heap::kEmptyFixedArrayRootIndex);
|
__ LoadRoot(a4, Heap::kEmptyFixedArrayRootIndex);
|
||||||
|
@ -947,14 +947,14 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
|
|||||||
|
|
||||||
FeedbackVectorSlot slot = stmt->ForInFeedbackSlot();
|
FeedbackVectorSlot slot = stmt->ForInFeedbackSlot();
|
||||||
|
|
||||||
Label loop, exit;
|
|
||||||
ForIn loop_statement(this, stmt);
|
|
||||||
increment_loop_depth();
|
|
||||||
|
|
||||||
// Get the object to enumerate over.
|
// Get the object to enumerate over.
|
||||||
SetExpressionAsStatementPosition(stmt->enumerable());
|
SetExpressionAsStatementPosition(stmt->enumerable());
|
||||||
VisitForAccumulatorValue(stmt->enumerable());
|
VisitForAccumulatorValue(stmt->enumerable());
|
||||||
OperandStackDepthIncrement(ForIn::kElementCount);
|
OperandStackDepthIncrement(5);
|
||||||
|
|
||||||
|
Label loop, exit;
|
||||||
|
Iteration loop_statement(this, stmt);
|
||||||
|
increment_loop_depth();
|
||||||
|
|
||||||
// If the object is null or undefined, skip over the loop, otherwise convert
|
// If the object is null or undefined, skip over the loop, otherwise convert
|
||||||
// it to a JS receiver. See ECMA-262 version 5, section 12.6.4.
|
// it to a JS receiver. See ECMA-262 version 5, section 12.6.4.
|
||||||
@ -1115,8 +1115,7 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
|
|||||||
|
|
||||||
// Remove the pointers stored on the stack.
|
// Remove the pointers stored on the stack.
|
||||||
__ bind(loop_statement.break_label());
|
__ bind(loop_statement.break_label());
|
||||||
__ addp(rsp, Immediate(5 * kPointerSize));
|
DropOperands(5);
|
||||||
OperandStackDepthDecrement(ForIn::kElementCount);
|
|
||||||
|
|
||||||
// Exit and decrement the loop depth.
|
// Exit and decrement the loop depth.
|
||||||
PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS);
|
PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS);
|
||||||
@ -1828,7 +1827,7 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
|
|||||||
case Yield::kSuspend:
|
case Yield::kSuspend:
|
||||||
// Pop value from top-of-stack slot; box result into result register.
|
// Pop value from top-of-stack slot; box result into result register.
|
||||||
EmitCreateIteratorResult(false);
|
EmitCreateIteratorResult(false);
|
||||||
__ Push(result_register());
|
PushOperand(result_register());
|
||||||
// Fall through.
|
// Fall through.
|
||||||
case Yield::kInitial: {
|
case Yield::kInitial: {
|
||||||
Label suspend, continuation, post_runtime, resume;
|
Label suspend, continuation, post_runtime, resume;
|
||||||
@ -1847,6 +1846,7 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
|
|||||||
EmitUnwindAndReturn();
|
EmitUnwindAndReturn();
|
||||||
|
|
||||||
__ bind(&suspend);
|
__ bind(&suspend);
|
||||||
|
OperandStackDepthIncrement(1); // Not popped on this path.
|
||||||
VisitForAccumulatorValue(expr->generator_object());
|
VisitForAccumulatorValue(expr->generator_object());
|
||||||
DCHECK(continuation.pos() > 0 && Smi::IsValid(continuation.pos()));
|
DCHECK(continuation.pos() > 0 && Smi::IsValid(continuation.pos()));
|
||||||
__ Move(FieldOperand(rax, JSGeneratorObject::kContinuationOffset),
|
__ Move(FieldOperand(rax, JSGeneratorObject::kContinuationOffset),
|
||||||
@ -1874,7 +1874,6 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
|
|||||||
|
|
||||||
case Yield::kFinal: {
|
case Yield::kFinal: {
|
||||||
// Pop value from top-of-stack slot, box result into result register.
|
// Pop value from top-of-stack slot, box result into result register.
|
||||||
OperandStackDepthDecrement(1);
|
|
||||||
EmitCreateIteratorResult(true);
|
EmitCreateIteratorResult(true);
|
||||||
EmitUnwindAndReturn();
|
EmitUnwindAndReturn();
|
||||||
break;
|
break;
|
||||||
@ -2014,6 +2013,7 @@ void FullCodeGenerator::EmitCreateIteratorResult(bool done) {
|
|||||||
__ LoadRoot(FieldOperand(rax, JSIteratorResult::kDoneOffset),
|
__ LoadRoot(FieldOperand(rax, JSIteratorResult::kDoneOffset),
|
||||||
done ? Heap::kTrueValueRootIndex : Heap::kFalseValueRootIndex);
|
done ? Heap::kTrueValueRootIndex : Heap::kFalseValueRootIndex);
|
||||||
STATIC_ASSERT(JSIteratorResult::kSize == 5 * kPointerSize);
|
STATIC_ASSERT(JSIteratorResult::kSize == 5 * kPointerSize);
|
||||||
|
OperandStackDepthDecrement(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
109
test/mjsunit/harmony/do-expressions-control.js
Normal file
109
test/mjsunit/harmony/do-expressions-control.js
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
// Copyright 2016 the V8 project authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
// Flags: --allow-natives-syntax --harmony-do-expressions
|
||||||
|
|
||||||
|
(function TestDoForInDoBreak() {
|
||||||
|
function f(o, i) {
|
||||||
|
var a = "result@" + do {
|
||||||
|
var r = "(";
|
||||||
|
for (var x in o) {
|
||||||
|
var b = "end@" + do {
|
||||||
|
if (x == i) { break } else { r += o[x]; x }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r + ")";
|
||||||
|
}
|
||||||
|
return a + "," + b;
|
||||||
|
}
|
||||||
|
assertEquals("result@(3),end@0", f([3], 2));
|
||||||
|
assertEquals("result@(35),end@1", f([3,5], 2));
|
||||||
|
assertEquals("result@(35),end@1", f([3,5,7], 2));
|
||||||
|
assertEquals("result@(35),end@1", f([3,5,7,9], 2));
|
||||||
|
%OptimizeFunctionOnNextCall(f);
|
||||||
|
assertEquals("result@(3),end@0", f([3], 2));
|
||||||
|
assertEquals("result@(35),end@1", f([3,5], 2));
|
||||||
|
assertEquals("result@(35),end@1", f([3,5,7], 2));
|
||||||
|
assertEquals("result@(35),end@1", f([3,5,7,9], 2));
|
||||||
|
})();
|
||||||
|
|
||||||
|
(function TestDoForInDoContinue() {
|
||||||
|
function f(o, i) {
|
||||||
|
var a = "result@" + do {
|
||||||
|
var r = "("
|
||||||
|
for (var x in o) {
|
||||||
|
var b = "end@" + do {
|
||||||
|
if (x == i) { continue } else { r += o[x]; x }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r + ")"
|
||||||
|
}
|
||||||
|
return a + "," + b
|
||||||
|
}
|
||||||
|
assertEquals("result@(3),end@0", f([3], 2));
|
||||||
|
assertEquals("result@(35),end@1", f([3,5], 2));
|
||||||
|
assertEquals("result@(35),end@1", f([3,5,7], 2));
|
||||||
|
assertEquals("result@(359),end@3", f([3,5,7,9], 2));
|
||||||
|
%OptimizeFunctionOnNextCall(f);
|
||||||
|
assertEquals("result@(3),end@0", f([3], 2));
|
||||||
|
assertEquals("result@(35),end@1", f([3,5], 2));
|
||||||
|
assertEquals("result@(35),end@1", f([3,5,7], 2));
|
||||||
|
assertEquals("result@(359),end@3", f([3,5,7,9], 2));
|
||||||
|
})();
|
||||||
|
|
||||||
|
(function TestDoForNestedWithTargetLabels() {
|
||||||
|
function f(mode) {
|
||||||
|
var loop = true;
|
||||||
|
var head = "<";
|
||||||
|
var tail = ">";
|
||||||
|
var middle =
|
||||||
|
"1" + do { loop1: for(; loop; head += "A") {
|
||||||
|
"2" + do { loop2: for(; loop; head += "B") {
|
||||||
|
"3" + do { loop3: for(; loop; head += "C") {
|
||||||
|
"4" + do { loop4: for(; loop; head += "D") {
|
||||||
|
"5" + do { loop5: for(; loop; head += "E") {
|
||||||
|
"6" + do { loop6: for(; loop; head += "F") {
|
||||||
|
loop = false;
|
||||||
|
switch (mode) {
|
||||||
|
case "b1": break loop1;
|
||||||
|
case "b2": break loop2;
|
||||||
|
case "b3": break loop3;
|
||||||
|
case "b4": break loop4;
|
||||||
|
case "b5": break loop5;
|
||||||
|
case "b6": break loop6;
|
||||||
|
case "c1": continue loop1;
|
||||||
|
case "c2": continue loop2;
|
||||||
|
case "c3": continue loop3;
|
||||||
|
case "c4": continue loop4;
|
||||||
|
case "c5": continue loop5;
|
||||||
|
case "c6": continue loop6;
|
||||||
|
default: "7";
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
return head + middle + tail;
|
||||||
|
}
|
||||||
|
function test() {
|
||||||
|
assertEquals( "<1undefined>", f("b1"));
|
||||||
|
assertEquals( "<A1undefined>", f("c1"));
|
||||||
|
assertEquals( "<A12undefined>", f("b2"));
|
||||||
|
assertEquals( "<BA12undefined>", f("c2"));
|
||||||
|
assertEquals( "<BA123undefined>", f("b3"));
|
||||||
|
assertEquals( "<CBA123undefined>", f("c3"));
|
||||||
|
assertEquals( "<CBA1234undefined>", f("b4"));
|
||||||
|
assertEquals( "<DCBA1234undefined>", f("c4"));
|
||||||
|
assertEquals( "<DCBA12345undefined>", f("b5"));
|
||||||
|
assertEquals( "<EDCBA12345undefined>", f("c5"));
|
||||||
|
assertEquals( "<EDCBA123456undefined>", f("b6"));
|
||||||
|
assertEquals("<FEDCBA123456undefined>", f("c6"));
|
||||||
|
assertEquals("<FEDCBA1234567>", f("xx"));
|
||||||
|
}
|
||||||
|
test();
|
||||||
|
%OptimizeFunctionOnNextCall(f);
|
||||||
|
test();
|
||||||
|
})();
|
Loading…
Reference in New Issue
Block a user