Index: src/compiler/ast-graph-builder.cc |
diff --git a/src/compiler/ast-graph-builder.cc b/src/compiler/ast-graph-builder.cc |
index f442017cd86b8fcdc4b463ea5e38ea242183dcc3..0c0bb02ea5bd24d1a5b83c207ff00652ca4608ce 100644 |
--- a/src/compiler/ast-graph-builder.cc |
+++ b/src/compiler/ast-graph-builder.cc |
@@ -2513,7 +2513,7 @@ void AstGraphBuilder::VisitCallSuper(Call* expr) { |
ZoneList<Expression*>* args = expr->arguments(); |
VisitForValues(args); |
- // Original receiver is loaded from the {new.target} variable. |
+ // Original constructor is loaded from the {new.target} variable. |
VisitForValue(super->new_target_var()); |
// Create node to perform the super call. |
@@ -2521,14 +2521,10 @@ void AstGraphBuilder::VisitCallSuper(Call* expr) { |
Node* value = ProcessArguments(call, args->length() + 2); |
PrepareFrameState(value, expr->id(), ast_context()->GetStateCombine()); |
- // TODO(mstarzinger): It sure would be nice if this were desugared. Also we |
- // are still missing the hole-check in the assignment below, fix that. |
+ // TODO(mstarzinger): It sure would be nice if this were desugared. |
FrameStateBeforeAndAfter states(this, BailoutId::None()); |
BuildVariableAssignment(super->this_var()->var(), value, Token::INIT_CONST, |
- VectorSlotPair(), BailoutId::None(), states); |
- |
- // TODO(mstarzinger): Remove bailout once lowering is correct. |
- SetStackOverflow(); |
+ VectorSlotPair(), expr->id(), states); |
ast_context()->ProduceValue(value); |
} |
@@ -2541,7 +2537,7 @@ void AstGraphBuilder::VisitCallNew(CallNew* expr) { |
ZoneList<Expression*>* args = expr->arguments(); |
VisitForValues(args); |
- // Original receiver is the same as the callee. |
+ // Original constructor is the same as the callee. |
environment()->Push(environment()->Peek(args->length())); |
// Create node to perform the construct call. |
@@ -3272,9 +3268,9 @@ Node* AstGraphBuilder::BuildHoleCheckSilent(Node* value, Node* for_hole, |
} |
-Node* AstGraphBuilder::BuildHoleCheckThrow(Node* value, Variable* variable, |
- Node* not_hole, |
- BailoutId bailout_id) { |
+Node* AstGraphBuilder::BuildHoleCheckThenThrow(Node* value, Variable* variable, |
+ Node* not_hole, |
+ BailoutId bailout_id) { |
IfBuilder hole_check(this); |
Node* the_hole = jsgraph()->TheHoleConstant(); |
Node* check = NewNode(javascript()->StrictEqual(), value, the_hole); |
@@ -3289,6 +3285,23 @@ Node* AstGraphBuilder::BuildHoleCheckThrow(Node* value, Variable* variable, |
} |
+Node* AstGraphBuilder::BuildHoleCheckElseThrow(Node* value, Variable* variable, |
+ Node* for_hole, |
+ BailoutId bailout_id) { |
+ IfBuilder hole_check(this); |
+ Node* the_hole = jsgraph()->TheHoleConstant(); |
+ Node* check = NewNode(javascript()->StrictEqual(), value, the_hole); |
+ hole_check.If(check); |
+ hole_check.Then(); |
+ environment()->Push(for_hole); |
+ hole_check.Else(); |
+ Node* error = BuildThrowReferenceError(variable, bailout_id); |
+ environment()->Push(error); |
+ hole_check.End(); |
+ return environment()->Pop(); |
+} |
+ |
+ |
Node* AstGraphBuilder::BuildThrowIfStaticPrototype(Node* name, |
BailoutId bailout_id) { |
IfBuilder prototype_check(this); |
@@ -3358,7 +3371,7 @@ Node* AstGraphBuilder::BuildVariableLoad(Variable* variable, |
if (value->op() == the_hole->op()) { |
value = BuildThrowReferenceError(variable, bailout_id); |
} else if (value->opcode() == IrOpcode::kPhi || variable->is_this()) { |
- value = BuildHoleCheckThrow(value, variable, value, bailout_id); |
+ value = BuildHoleCheckThenThrow(value, variable, value, bailout_id); |
} |
} |
return value; |
@@ -3379,7 +3392,7 @@ Node* AstGraphBuilder::BuildVariableLoad(Variable* variable, |
value = BuildHoleCheckSilent(value, undefined, value); |
} else if (mode == LET || mode == CONST) { |
// Perform check for uninitialized let/const variables. |
- value = BuildHoleCheckThrow(value, variable, value, bailout_id); |
+ value = BuildHoleCheckThenThrow(value, variable, value, bailout_id); |
} |
return value; |
} |
@@ -3410,7 +3423,7 @@ Node* AstGraphBuilder::BuildVariableLoad(Variable* variable, |
value = BuildHoleCheckSilent(value, undefined, value); |
} else if (local_mode == LET || local_mode == CONST) { |
// Perform check for uninitialized let/const variables. |
- value = BuildHoleCheckThrow(value, local, value, bailout_id); |
+ value = BuildHoleCheckThenThrow(value, local, value, bailout_id); |
} |
} else if (mode == DYNAMIC) { |
uint32_t check_bitset = DynamicGlobalAccess::kFullCheckRequired; |
@@ -3519,7 +3532,15 @@ Node* AstGraphBuilder::BuildVariableAssignment( |
if (current->op() == the_hole->op()) { |
value = BuildThrowReferenceError(variable, bailout_id); |
} else if (value->opcode() == IrOpcode::kPhi) { |
- value = BuildHoleCheckThrow(current, variable, value, bailout_id); |
+ value = BuildHoleCheckThenThrow(current, variable, value, bailout_id); |
+ } |
+ } else if (mode == CONST && op == Token::INIT_CONST) { |
+ // Perform an initialization check for const {this} variables. |
+ // Note that the {this} variable is the only const variable being able |
+ // to trigger bind operations outside the TDZ, via {super} calls. |
+ Node* current = environment()->Lookup(variable); |
+ if (current->op() != the_hole->op() && variable->is_this()) { |
+ value = BuildHoleCheckElseThrow(current, variable, value, bailout_id); |
} |
} else if (mode == CONST && op != Token::INIT_CONST) { |
// Assignment to const is exception in all modes. |
@@ -3527,7 +3548,7 @@ Node* AstGraphBuilder::BuildVariableAssignment( |
if (current->op() == the_hole->op()) { |
return BuildThrowReferenceError(variable, bailout_id); |
} else if (value->opcode() == IrOpcode::kPhi) { |
- BuildHoleCheckThrow(current, variable, value, bailout_id); |
+ BuildHoleCheckThenThrow(current, variable, value, bailout_id); |
} |
return BuildThrowConstAssignError(bailout_id); |
} |
@@ -3555,13 +3576,23 @@ Node* AstGraphBuilder::BuildVariableAssignment( |
const Operator* op = |
javascript()->LoadContext(depth, variable->index(), false); |
Node* current = NewNode(op, current_context()); |
- value = BuildHoleCheckThrow(current, variable, value, bailout_id); |
+ value = BuildHoleCheckThenThrow(current, variable, value, bailout_id); |
+ } else if (mode == CONST && op == Token::INIT_CONST) { |
+ // Perform an initialization check for const {this} variables. |
+ // Note that the {this} variable is the only const variable being able |
+ // to trigger bind operations outside the TDZ, via {super} calls. |
+ if (variable->is_this()) { |
+ const Operator* op = |
+ javascript()->LoadContext(depth, variable->index(), false); |
+ Node* current = NewNode(op, current_context()); |
+ value = BuildHoleCheckElseThrow(current, variable, value, bailout_id); |
+ } |
} else if (mode == CONST && op != Token::INIT_CONST) { |
// Assignment to const is exception in all modes. |
const Operator* op = |
javascript()->LoadContext(depth, variable->index(), false); |
Node* current = NewNode(op, current_context()); |
- BuildHoleCheckThrow(current, variable, value, bailout_id); |
+ BuildHoleCheckThenThrow(current, variable, value, bailout_id); |
return BuildThrowConstAssignError(bailout_id); |
} |
const Operator* op = javascript()->StoreContext(depth, variable->index()); |