Implement the new semantics for 'super(...)'
authordslomov@chromium.org <dslomov@chromium.org>
Fri, 17 Oct 2014 09:16:03 +0000 (09:16 +0000)
committerdslomov@chromium.org <dslomov@chromium.org>
Fri, 17 Oct 2014 09:16:03 +0000 (09:16 +0000)
Per the latest ES6 draft, super(...) translates into a call
to function's prototype.

R=arv@chromium.org, ishell@chromium.org, verwaest@chromium.org
BUG=v8:3330
LOG=N

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

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@24683 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/arm/full-codegen-arm.cc
src/arm64/full-codegen-arm64.cc
src/ast.cc
src/ast.h
src/compiler/ast-graph-builder.cc
src/ia32/full-codegen-ia32.cc
src/x64/full-codegen-x64.cc
test/mjsunit/harmony/super.js

index bcec2bb..4533103 100644 (file)
@@ -3076,6 +3076,15 @@ void FullCodeGenerator::VisitCall(Call* expr) {
         EmitKeyedCallWithLoadIC(expr, property->key());
       }
     }
+  } else if (call_type == Call::SUPER_CALL) {
+    SuperReference* super_ref = callee->AsSuperReference();
+    DCHECK(super_ref != NULL);
+    __ ldr(r0, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
+    __ Push(r0);
+    __ CallRuntime(Runtime::kGetPrototype, 1);
+    __ Push(result_register());
+    VisitForStackValue(super_ref->this_var());
+    EmitCall(expr, CallICState::METHOD);
   } else {
     DCHECK(call_type == Call::OTHER_CALL);
     // Call to an arbitrary expression not handled specially above.
index 81ccf2a..c0eca3c 100644 (file)
@@ -2741,6 +2741,15 @@ void FullCodeGenerator::VisitCall(Call* expr) {
         EmitKeyedCallWithLoadIC(expr, property->key());
       }
     }
+  } else if (call_type == Call::SUPER_CALL) {
+    SuperReference* super_ref = callee->AsSuperReference();
+    DCHECK(super_ref != NULL);
+    __ Ldr(x0, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
+    __ Push(x0);
+    __ CallRuntime(Runtime::kGetPrototype, 1);
+    __ Push(result_register());
+    VisitForStackValue(super_ref->this_var());
+    EmitCall(expr, CallICState::METHOD);
   } else {
     DCHECK(call_type == Call::OTHER_CALL);
     // Call to an arbitrary expression not handled specially above.
index fdf2d55..2acb2f5 100644 (file)
@@ -581,6 +581,8 @@ Call::CallType Call::GetCallType(Isolate* isolate) const {
     }
   }
 
+  if (expression()->AsSuperReference() != NULL) return SUPER_CALL;
+
   Property* property = expression()->AsProperty();
   return property != NULL ? PROPERTY_CALL : OTHER_CALL;
 }
index 61f9732..847d9ca 100644 (file)
--- a/src/ast.h
+++ b/src/ast.h
@@ -1851,6 +1851,7 @@ class Call FINAL : public Expression {
     GLOBAL_CALL,
     LOOKUP_SLOT_CALL,
     PROPERTY_CALL,
+    SUPER_CALL,
     OTHER_CALL
   };
 
@@ -3474,16 +3475,7 @@ class AstNodeFactory FINAL BASE_EMBEDDED {
   Call* NewCall(Expression* expression,
                 ZoneList<Expression*>* arguments,
                 int pos) {
-    SuperReference* super_ref = expression->AsSuperReference();
-    Call* call;
-    if (super_ref != NULL) {
-      Literal* constructor =
-          NewStringLiteral(ast_value_factory_->constructor_string(), pos);
-      Property* superConstructor = NewProperty(super_ref, constructor, pos);
-      call = new (zone_) Call(zone_, superConstructor, arguments, pos, id_gen_);
-    } else {
-      call = new (zone_) Call(zone_, expression, arguments, pos, id_gen_);
-    }
+    Call* call = new (zone_) Call(zone_, expression, arguments, pos, id_gen_);
     VISIT_AND_RETURN(Call, call)
   }
 
index 9c11c65..ba10bf5 100644 (file)
@@ -1261,6 +1261,11 @@ void AstGraphBuilder::VisitCall(Call* expr) {
       flags = CALL_AS_METHOD;
       break;
     }
+    case Call::SUPER_CALL: {
+      // todo(dslomov): implement super calls in turbofan.
+      UNIMPLEMENTED();
+      break;
+    }
     case Call::POSSIBLY_EVAL_CALL:
       possibly_eval = true;
     // Fall through.
index 2dba03b..cddefa1 100644 (file)
@@ -2972,6 +2972,14 @@ void FullCodeGenerator::VisitCall(Call* expr) {
         EmitKeyedCallWithLoadIC(expr, property->key());
       }
     }
+  } else if (call_type == Call::SUPER_CALL) {
+    SuperReference* super_ref = callee->AsSuperReference();
+    DCHECK(super_ref != NULL);
+    __ push(Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
+    __ CallRuntime(Runtime::kGetPrototype, 1);
+    __ push(result_register());
+    VisitForStackValue(super_ref->this_var());
+    EmitCall(expr, CallICState::METHOD);
   } else {
     DCHECK(call_type == Call::OTHER_CALL);
     // Call to an arbitrary expression not handled specially above.
index 4817e50..9b63de9 100644 (file)
@@ -2971,6 +2971,14 @@ void FullCodeGenerator::VisitCall(Call* expr) {
         EmitKeyedCallWithLoadIC(expr, property->key());
       }
     }
+  } else if (call_type == Call::SUPER_CALL) {
+    SuperReference* super_ref = callee->AsSuperReference();
+    DCHECK(super_ref != NULL);
+    __ Push(Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
+    __ CallRuntime(Runtime::kGetPrototype, 1);
+    __ Push(result_register());
+    VisitForStackValue(super_ref->this_var());
+    EmitCall(expr, CallICState::METHOD);
   } else {
     DCHECK(call_type == Call::OTHER_CALL);
     // Call to an arbitrary expression not handled specially above.
index 130c010..c39944d 100644 (file)
 
 (function TestSuperCall() {
   function Subclass(base, constructor) {
-    var homeObject = { __proto__ : base.prototype };
-    var result = constructor.toMethod(homeObject);
-    homeObject.constructor = result;
-    result.prototype = homeObject;
-    return result;
+    var homeObject = {
+      __proto__: base.prototype,
+      constructor: constructor
+    };
+    constructor.__proto__ = base;
+    constructor.prototype = homeObject;
+    // not doing toMethod: home object is not required for
+    // super constructor calls.
+    return constructor;
   }
 
   var baseCalled = 0;
   var d = new Derived2("base", "derived");
   assertEquals("base", d.fromBase);
   assertEquals("derived", d.fromDerived);
+
+  function ImplicitSubclassOfFunction() {
+    super();
+    this.x = 123;
+  }
+
+  var o = new ImplicitSubclassOfFunction();
+  assertEquals(123, o.x);
+
+  var calls = 0;
+  function G() {
+    calls++;
+  }
+  function F() {
+    super();
+  }
+  F.__proto__ = G;
+  new F();
+  assertEquals(1, calls);
+  F.__proto__ = function() {};
+  new F();
+  assertEquals(1, calls);
+}());
+
+
+(function TestSuperCallErrorCases() {
+  function T() {
+    super();
+  }
+  T.__proto__ = null;
+  // Spec says ReferenceError here, but for other IsCallable failures
+  // we throw TypeError.
+  // Filed https://bugs.ecmascript.org/show_bug.cgi?id=3282
+  assertThrows(function() { new T(); }, TypeError);
 }());