Commit b76acef7 authored by mstarzinger's avatar mstarzinger Committed by Commit bot

[turbofan] Implement super call support in TurboFan.

R=rossberg@chromium.org

Review URL: https://codereview.chromium.org/1238743002

Cr-Commit-Position: refs/heads/master@{#29696}
parent c6d42c7d
...@@ -2513,7 +2513,7 @@ void AstGraphBuilder::VisitCallSuper(Call* expr) { ...@@ -2513,7 +2513,7 @@ void AstGraphBuilder::VisitCallSuper(Call* expr) {
ZoneList<Expression*>* args = expr->arguments(); ZoneList<Expression*>* args = expr->arguments();
VisitForValues(args); 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()); VisitForValue(super->new_target_var());
// Create node to perform the super call. // Create node to perform the super call.
...@@ -2521,14 +2521,10 @@ void AstGraphBuilder::VisitCallSuper(Call* expr) { ...@@ -2521,14 +2521,10 @@ void AstGraphBuilder::VisitCallSuper(Call* expr) {
Node* value = ProcessArguments(call, args->length() + 2); Node* value = ProcessArguments(call, args->length() + 2);
PrepareFrameState(value, expr->id(), ast_context()->GetStateCombine()); PrepareFrameState(value, expr->id(), ast_context()->GetStateCombine());
// TODO(mstarzinger): It sure would be nice if this were desugared. Also we // TODO(mstarzinger): It sure would be nice if this were desugared.
// are still missing the hole-check in the assignment below, fix that.
FrameStateBeforeAndAfter states(this, BailoutId::None()); FrameStateBeforeAndAfter states(this, BailoutId::None());
BuildVariableAssignment(super->this_var()->var(), value, Token::INIT_CONST, BuildVariableAssignment(super->this_var()->var(), value, Token::INIT_CONST,
VectorSlotPair(), BailoutId::None(), states); VectorSlotPair(), expr->id(), states);
// TODO(mstarzinger): Remove bailout once lowering is correct.
SetStackOverflow();
ast_context()->ProduceValue(value); ast_context()->ProduceValue(value);
} }
...@@ -2541,7 +2537,7 @@ void AstGraphBuilder::VisitCallNew(CallNew* expr) { ...@@ -2541,7 +2537,7 @@ void AstGraphBuilder::VisitCallNew(CallNew* expr) {
ZoneList<Expression*>* args = expr->arguments(); ZoneList<Expression*>* args = expr->arguments();
VisitForValues(args); VisitForValues(args);
// Original receiver is the same as the callee. // Original constructor is the same as the callee.
environment()->Push(environment()->Peek(args->length())); environment()->Push(environment()->Peek(args->length()));
// Create node to perform the construct call. // Create node to perform the construct call.
...@@ -3272,7 +3268,7 @@ Node* AstGraphBuilder::BuildHoleCheckSilent(Node* value, Node* for_hole, ...@@ -3272,7 +3268,7 @@ Node* AstGraphBuilder::BuildHoleCheckSilent(Node* value, Node* for_hole,
} }
Node* AstGraphBuilder::BuildHoleCheckThrow(Node* value, Variable* variable, Node* AstGraphBuilder::BuildHoleCheckThenThrow(Node* value, Variable* variable,
Node* not_hole, Node* not_hole,
BailoutId bailout_id) { BailoutId bailout_id) {
IfBuilder hole_check(this); IfBuilder hole_check(this);
...@@ -3289,6 +3285,23 @@ Node* AstGraphBuilder::BuildHoleCheckThrow(Node* value, Variable* variable, ...@@ -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, Node* AstGraphBuilder::BuildThrowIfStaticPrototype(Node* name,
BailoutId bailout_id) { BailoutId bailout_id) {
IfBuilder prototype_check(this); IfBuilder prototype_check(this);
...@@ -3358,7 +3371,7 @@ Node* AstGraphBuilder::BuildVariableLoad(Variable* variable, ...@@ -3358,7 +3371,7 @@ Node* AstGraphBuilder::BuildVariableLoad(Variable* variable,
if (value->op() == the_hole->op()) { if (value->op() == the_hole->op()) {
value = BuildThrowReferenceError(variable, bailout_id); value = BuildThrowReferenceError(variable, bailout_id);
} else if (value->opcode() == IrOpcode::kPhi || variable->is_this()) { } 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; return value;
...@@ -3379,7 +3392,7 @@ Node* AstGraphBuilder::BuildVariableLoad(Variable* variable, ...@@ -3379,7 +3392,7 @@ Node* AstGraphBuilder::BuildVariableLoad(Variable* variable,
value = BuildHoleCheckSilent(value, undefined, value); value = BuildHoleCheckSilent(value, undefined, value);
} else if (mode == LET || mode == CONST) { } else if (mode == LET || mode == CONST) {
// Perform check for uninitialized let/const variables. // Perform check for uninitialized let/const variables.
value = BuildHoleCheckThrow(value, variable, value, bailout_id); value = BuildHoleCheckThenThrow(value, variable, value, bailout_id);
} }
return value; return value;
} }
...@@ -3410,7 +3423,7 @@ Node* AstGraphBuilder::BuildVariableLoad(Variable* variable, ...@@ -3410,7 +3423,7 @@ Node* AstGraphBuilder::BuildVariableLoad(Variable* variable,
value = BuildHoleCheckSilent(value, undefined, value); value = BuildHoleCheckSilent(value, undefined, value);
} else if (local_mode == LET || local_mode == CONST) { } else if (local_mode == LET || local_mode == CONST) {
// Perform check for uninitialized let/const variables. // 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) { } else if (mode == DYNAMIC) {
uint32_t check_bitset = DynamicGlobalAccess::kFullCheckRequired; uint32_t check_bitset = DynamicGlobalAccess::kFullCheckRequired;
...@@ -3519,7 +3532,15 @@ Node* AstGraphBuilder::BuildVariableAssignment( ...@@ -3519,7 +3532,15 @@ Node* AstGraphBuilder::BuildVariableAssignment(
if (current->op() == the_hole->op()) { if (current->op() == the_hole->op()) {
value = BuildThrowReferenceError(variable, bailout_id); value = BuildThrowReferenceError(variable, bailout_id);
} else if (value->opcode() == IrOpcode::kPhi) { } 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) { } else if (mode == CONST && op != Token::INIT_CONST) {
// Assignment to const is exception in all modes. // Assignment to const is exception in all modes.
...@@ -3527,7 +3548,7 @@ Node* AstGraphBuilder::BuildVariableAssignment( ...@@ -3527,7 +3548,7 @@ Node* AstGraphBuilder::BuildVariableAssignment(
if (current->op() == the_hole->op()) { if (current->op() == the_hole->op()) {
return BuildThrowReferenceError(variable, bailout_id); return BuildThrowReferenceError(variable, bailout_id);
} else if (value->opcode() == IrOpcode::kPhi) { } else if (value->opcode() == IrOpcode::kPhi) {
BuildHoleCheckThrow(current, variable, value, bailout_id); BuildHoleCheckThenThrow(current, variable, value, bailout_id);
} }
return BuildThrowConstAssignError(bailout_id); return BuildThrowConstAssignError(bailout_id);
} }
...@@ -3555,13 +3576,23 @@ Node* AstGraphBuilder::BuildVariableAssignment( ...@@ -3555,13 +3576,23 @@ Node* AstGraphBuilder::BuildVariableAssignment(
const Operator* op = const Operator* op =
javascript()->LoadContext(depth, variable->index(), false); javascript()->LoadContext(depth, variable->index(), false);
Node* current = NewNode(op, current_context()); 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) { } else if (mode == CONST && op != Token::INIT_CONST) {
// Assignment to const is exception in all modes. // Assignment to const is exception in all modes.
const Operator* op = const Operator* op =
javascript()->LoadContext(depth, variable->index(), false); javascript()->LoadContext(depth, variable->index(), false);
Node* current = NewNode(op, current_context()); Node* current = NewNode(op, current_context());
BuildHoleCheckThrow(current, variable, value, bailout_id); BuildHoleCheckThenThrow(current, variable, value, bailout_id);
return BuildThrowConstAssignError(bailout_id); return BuildThrowConstAssignError(bailout_id);
} }
const Operator* op = javascript()->StoreContext(depth, variable->index()); const Operator* op = javascript()->StoreContext(depth, variable->index());
......
...@@ -347,7 +347,9 @@ class AstGraphBuilder : public AstVisitor { ...@@ -347,7 +347,9 @@ class AstGraphBuilder : public AstVisitor {
// Builders for dynamic hole-checks at runtime. // Builders for dynamic hole-checks at runtime.
Node* BuildHoleCheckSilent(Node* value, Node* for_hole, Node* not_hole); Node* BuildHoleCheckSilent(Node* value, Node* for_hole, Node* not_hole);
Node* BuildHoleCheckThrow(Node* value, Variable* var, Node* not_hole, Node* BuildHoleCheckThenThrow(Node* value, Variable* var, Node* not_hole,
BailoutId bailout_id);
Node* BuildHoleCheckElseThrow(Node* value, Variable* var, Node* for_hole,
BailoutId bailout_id); BailoutId bailout_id);
// Builders for conditional errors. // Builders for conditional errors.
......
...@@ -161,6 +161,11 @@ void ALAA::VisitProperty(Property* e) { ...@@ -161,6 +161,11 @@ void ALAA::VisitProperty(Property* e) {
void ALAA::VisitCall(Call* e) { void ALAA::VisitCall(Call* e) {
Visit(e->expression()); Visit(e->expression());
VisitExpressions(e->arguments()); VisitExpressions(e->arguments());
// TODO(mstarzinger): It sure would be nice if this were desugared.
if (e->GetCallType(isolate()) == Call::SUPER_CALL) {
SuperCallReference* super = e->expression()->AsSuperCallReference();
AnalyzeAssignment(super->this_var()->var());
}
} }
......
...@@ -537,7 +537,7 @@ void JSGenericLowering::LowerJSCreateCatchContext(Node* node) { ...@@ -537,7 +537,7 @@ void JSGenericLowering::LowerJSCreateCatchContext(Node* node) {
void JSGenericLowering::LowerJSCallConstruct(Node* node) { void JSGenericLowering::LowerJSCallConstruct(Node* node) {
int arity = OpParameter<int>(node); int arity = OpParameter<int>(node);
CallConstructStub stub(isolate(), NO_CALL_CONSTRUCTOR_FLAGS); CallConstructStub stub(isolate(), SUPER_CONSTRUCTOR_CALL);
CallInterfaceDescriptor d = stub.GetCallInterfaceDescriptor(); CallInterfaceDescriptor d = stub.GetCallInterfaceDescriptor();
CallDescriptor::Flags flags = AdjustFrameStatesForCall(node); CallDescriptor::Flags flags = AdjustFrameStatesForCall(node);
CallDescriptor* desc = CallDescriptor* desc =
......
...@@ -2181,6 +2181,47 @@ TestKeyedSetterCreatingOwnPropertiesNonConfigurable(42, 43, 44); ...@@ -2181,6 +2181,47 @@ TestKeyedSetterCreatingOwnPropertiesNonConfigurable(42, 43, 44);
})(); })();
(function TestSuperCallInLoop() {
'use strict';
class Base {
constructor(x) {
this.x = x;
}
}
class Derived extends Base {
constructor(x, n) {
for (var i = 0; i < n; ++i) {
super(x);
}
}
}
let o = new Derived(23, 1);
assertEquals(23, o.x);
assertInstanceof(o, Derived);
assertThrows("new Derived(42, 0)", ReferenceError);
assertThrows("new Derived(65, 2)", ReferenceError);
})();
(function TestSuperCallReentrant() {
'use strict';
class Base {
constructor(fun) {
this.x = fun();
}
}
class Derived extends Base {
constructor(x) {
let f = () => super(() => x)
super(f);
}
}
assertThrows("new Derived(23)", ReferenceError);
})();
(function TestSuperCallSpreadInEval() { (function TestSuperCallSpreadInEval() {
'use strict'; 'use strict';
class Base { class Base {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment