Implement the new semantics for 'super(...)'

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
This commit is contained in:
dslomov@chromium.org 2014-10-17 09:16:03 +00:00
parent 27b1c823ce
commit d4cbcfce6e
8 changed files with 86 additions and 15 deletions

View 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.

View 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.

View 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;
}

View File

@ -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)
}

View 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.

View 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.

View 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.

View File

@ -1192,11 +1192,15 @@
(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;
@ -1245,6 +1249,40 @@
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);
}());