[turbofan] Implement super call support in TurboFan.
authormstarzinger <mstarzinger@chromium.org>
Thu, 16 Jul 2015 08:53:56 +0000 (01:53 -0700)
committerCommit bot <commit-bot@chromium.org>
Thu, 16 Jul 2015 08:54:05 +0000 (08:54 +0000)
R=rossberg@chromium.org

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

Cr-Commit-Position: refs/heads/master@{#29696}

src/compiler/ast-graph-builder.cc
src/compiler/ast-graph-builder.h
src/compiler/ast-loop-assignment-analyzer.cc
src/compiler/js-generic-lowering.cc
test/mjsunit/harmony/super.js

index f442017cd86b8fcdc4b463ea5e38ea242183dcc3..0c0bb02ea5bd24d1a5b83c207ff00652ca4608ce 100644 (file)
@@ -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());
index 201a20a18afec5c011bb5abbabcef55e1354d4d6..bb031ff447cadcaf2177a89fa73f158ce5616c81 100644 (file)
@@ -347,8 +347,10 @@ class AstGraphBuilder : public AstVisitor {
 
   // Builders for dynamic hole-checks at runtime.
   Node* BuildHoleCheckSilent(Node* value, Node* for_hole, Node* not_hole);
-  Node* BuildHoleCheckThrow(Node* value, Variable* var, Node* not_hole,
-                            BailoutId bailout_id);
+  Node* BuildHoleCheckThenThrow(Node* value, Variable* var, Node* not_hole,
+                                BailoutId bailout_id);
+  Node* BuildHoleCheckElseThrow(Node* value, Variable* var, Node* for_hole,
+                                BailoutId bailout_id);
 
   // Builders for conditional errors.
   Node* BuildThrowIfStaticPrototype(Node* name, BailoutId bailout_id);
index 61ed4f27cbba8e7d7b9287108a87ab9e8f3435e2..e0a92a464c487e7e1b74b88321d28298c2502f33 100644 (file)
@@ -161,6 +161,11 @@ void ALAA::VisitProperty(Property* e) {
 void ALAA::VisitCall(Call* e) {
   Visit(e->expression());
   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());
+  }
 }
 
 
index 84ee3933e22a28ba715417ed7222fdca7aa117bd..e3e4cddd8a3697723d31b376527f2b876153ca56 100644 (file)
@@ -537,7 +537,7 @@ void JSGenericLowering::LowerJSCreateCatchContext(Node* node) {
 
 void JSGenericLowering::LowerJSCallConstruct(Node* node) {
   int arity = OpParameter<int>(node);
-  CallConstructStub stub(isolate(), NO_CALL_CONSTRUCTOR_FLAGS);
+  CallConstructStub stub(isolate(), SUPER_CONSTRUCTOR_CALL);
   CallInterfaceDescriptor d = stub.GetCallInterfaceDescriptor();
   CallDescriptor::Flags flags = AdjustFrameStatesForCall(node);
   CallDescriptor* desc =
index 21b31d96c9b9bcac827f5c9a11e8f80f7050a164..270c159e80b22a1c9b1389e76d8d1262068205c2 100644 (file)
@@ -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() {
   'use strict';
   class Base {