Support for super keyed loads where key is a name.

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

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

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@24403 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
dslomov@chromium.org 2014-10-06 08:25:27 +00:00
parent 955876b5f3
commit da86ab5d23
9 changed files with 425 additions and 60 deletions

View File

@ -2382,6 +2382,14 @@ void FullCodeGenerator::EmitKeyedPropertyLoad(Property* prop) {
}
void FullCodeGenerator::EmitKeyedSuperPropertyLoad(Property* prop) {
// Stack: receiver, home_object, key.
SetSourcePosition(prop->position());
__ CallRuntime(Runtime::kLoadKeyedFromSuper, 3);
}
void FullCodeGenerator::EmitInlineSmiBinaryOp(BinaryOperation* expr,
Token::Value op,
OverwriteMode mode,
@ -2685,11 +2693,19 @@ void FullCodeGenerator::VisitProperty(Property* expr) {
PrepareForBailoutForId(expr->LoadId(), TOS_REG);
context()->Plug(r0);
} else {
VisitForStackValue(expr->obj());
VisitForAccumulatorValue(expr->key());
__ Move(LoadDescriptor::NameRegister(), r0);
__ pop(LoadDescriptor::ReceiverRegister());
EmitKeyedPropertyLoad(expr);
if (!expr->IsSuperAccess()) {
VisitForStackValue(expr->obj());
VisitForAccumulatorValue(expr->key());
__ Move(LoadDescriptor::NameRegister(), r0);
__ pop(LoadDescriptor::ReceiverRegister());
EmitKeyedPropertyLoad(expr);
} else {
VisitForStackValue(expr->obj()->AsSuperReference()->this_var());
EmitLoadHomeObject(expr->obj()->AsSuperReference());
__ Push(result_register());
VisitForStackValue(expr->key());
EmitKeyedSuperPropertyLoad(expr);
}
context()->Plug(r0);
}
}
@ -2801,6 +2817,43 @@ void FullCodeGenerator::EmitKeyedCallWithLoadIC(Call* expr,
}
void FullCodeGenerator::EmitKeyedSuperCallWithLoadIC(Call* expr) {
Expression* callee = expr->expression();
DCHECK(callee->IsProperty());
Property* prop = callee->AsProperty();
DCHECK(prop->IsSuperAccess());
SetSourcePosition(prop->position());
// Load the function from the receiver.
const Register scratch = r1;
SuperReference* super_ref = prop->obj()->AsSuperReference();
EmitLoadHomeObject(super_ref);
__ Push(r0);
VisitForAccumulatorValue(super_ref->this_var());
__ Push(r0);
__ Push(r0);
__ ldr(scratch, MemOperand(sp, kPointerSize * 2));
__ Push(scratch);
VisitForStackValue(prop->key());
// Stack here:
// - home_object
// - this (receiver)
// - this (receiver) <-- LoadKeyedFromSuper will pop here and below.
// - home_object
// - key
__ CallRuntime(Runtime::kLoadKeyedFromSuper, 3);
// Replace home_object with target function.
__ str(r0, MemOperand(sp, kPointerSize));
// Stack here:
// - target function
// - this (receiver)
EmitCall(expr, CallICState::METHOD);
}
void FullCodeGenerator::EmitCall(Call* expr, CallICState::CallType call_type) {
// Load the arguments.
ZoneList<Expression*>* args = expr->arguments();
@ -2950,9 +3003,12 @@ void FullCodeGenerator::VisitCall(Call* expr) {
} else if (call_type == Call::PROPERTY_CALL) {
Property* property = callee->AsProperty();
bool is_named_call = property->key()->IsPropertyName();
// super.x() is handled in EmitCallWithLoadIC.
if (property->IsSuperAccess() && is_named_call) {
EmitSuperCallWithLoadIC(expr);
if (property->IsSuperAccess()) {
if (is_named_call) {
EmitSuperCallWithLoadIC(expr);
} else {
EmitKeyedSuperCallWithLoadIC(expr);
}
} else {
{
PreservePositionScope scope(masm()->positions_recorder());

View File

@ -2032,6 +2032,14 @@ void FullCodeGenerator::EmitKeyedPropertyLoad(Property* prop) {
}
void FullCodeGenerator::EmitKeyedSuperPropertyLoad(Property* prop) {
// Stack: receiver, home_object, key.
SetSourcePosition(prop->position());
__ CallRuntime(Runtime::kLoadKeyedFromSuper, 3);
}
void FullCodeGenerator::EmitInlineSmiBinaryOp(BinaryOperation* expr,
Token::Value op,
OverwriteMode mode,
@ -2350,11 +2358,19 @@ void FullCodeGenerator::VisitProperty(Property* expr) {
PrepareForBailoutForId(expr->LoadId(), TOS_REG);
context()->Plug(x0);
} else {
VisitForStackValue(expr->obj());
VisitForAccumulatorValue(expr->key());
__ Move(LoadDescriptor::NameRegister(), x0);
__ Pop(LoadDescriptor::ReceiverRegister());
EmitKeyedPropertyLoad(expr);
if (!expr->IsSuperAccess()) {
VisitForStackValue(expr->obj());
VisitForAccumulatorValue(expr->key());
__ Move(LoadDescriptor::NameRegister(), x0);
__ Pop(LoadDescriptor::ReceiverRegister());
EmitKeyedPropertyLoad(expr);
} else {
VisitForStackValue(expr->obj()->AsSuperReference()->this_var());
EmitLoadHomeObject(expr->obj()->AsSuperReference());
__ Push(result_register());
VisitForStackValue(expr->key());
EmitKeyedSuperPropertyLoad(expr);
}
context()->Plug(x0);
}
}
@ -2463,6 +2479,43 @@ void FullCodeGenerator::EmitKeyedCallWithLoadIC(Call* expr,
}
void FullCodeGenerator::EmitKeyedSuperCallWithLoadIC(Call* expr) {
Expression* callee = expr->expression();
DCHECK(callee->IsProperty());
Property* prop = callee->AsProperty();
DCHECK(prop->IsSuperAccess());
SetSourcePosition(prop->position());
// Load the function from the receiver.
const Register scratch = x10;
SuperReference* super_ref = callee->AsProperty()->obj()->AsSuperReference();
EmitLoadHomeObject(super_ref);
__ Push(x0);
VisitForAccumulatorValue(super_ref->this_var());
__ Push(x0);
__ Peek(scratch, kPointerSize);
__ Push(x0, scratch);
VisitForStackValue(prop->key());
// Stack here:
// - home_object
// - this (receiver)
// - this (receiver) <-- LoadKeyedFromSuper will pop here and below.
// - home_object
// - key
__ CallRuntime(Runtime::kLoadKeyedFromSuper, 3);
// Replace home_object with target function.
__ Poke(x0, kPointerSize);
// Stack here:
// - target function
// - this (receiver)
EmitCall(expr, CallICState::METHOD);
}
void FullCodeGenerator::EmitCall(Call* expr, CallICState::CallType call_type) {
// Load the arguments.
ZoneList<Expression*>* args = expr->arguments();
@ -2616,9 +2669,12 @@ void FullCodeGenerator::VisitCall(Call* expr) {
} else if (call_type == Call::PROPERTY_CALL) {
Property* property = callee->AsProperty();
bool is_named_call = property->key()->IsPropertyName();
// super.x() is handled in EmitCallWithLoadIC.
if (property->IsSuperAccess() && is_named_call) {
EmitSuperCallWithLoadIC(expr);
if (property->IsSuperAccess()) {
if (is_named_call) {
EmitSuperCallWithLoadIC(expr);
} else {
EmitKeyedSuperCallWithLoadIC(expr);
}
} else {
{
PreservePositionScope scope(masm()->positions_recorder());

View File

@ -479,6 +479,7 @@ class FullCodeGenerator: public AstVisitor {
void EmitCallWithLoadIC(Call* expr);
void EmitSuperCallWithLoadIC(Call* expr);
void EmitKeyedCallWithLoadIC(Call* expr, Expression* key);
void EmitKeyedSuperCallWithLoadIC(Call* expr);
// Platform-specific code for inline runtime calls.
InlineFunctionGenerator FindInlineFunctionGenerator(Runtime::FunctionId id);
@ -525,6 +526,10 @@ class FullCodeGenerator: public AstVisitor {
// Expect receiver ('this' value) and home_object on the stack.
void EmitNamedSuperPropertyLoad(Property* expr);
// Load a value from super[keyed] property.
// Expect receiver ('this' value), home_object and key on the stack.
void EmitKeyedSuperPropertyLoad(Property* expr);
// Load a value from a keyed property.
// The receiver and the key is left on the stack by the IC.
void EmitKeyedPropertyLoad(Property* expr);

View File

@ -2300,6 +2300,14 @@ void FullCodeGenerator::EmitKeyedPropertyLoad(Property* prop) {
}
void FullCodeGenerator::EmitKeyedSuperPropertyLoad(Property* prop) {
// Stack: receiver, home_object, key.
SetSourcePosition(prop->position());
__ CallRuntime(Runtime::kLoadKeyedFromSuper, 3);
}
void FullCodeGenerator::EmitInlineSmiBinaryOp(BinaryOperation* expr,
Token::Value op,
OverwriteMode mode,
@ -2605,11 +2613,19 @@ void FullCodeGenerator::VisitProperty(Property* expr) {
PrepareForBailoutForId(expr->LoadId(), TOS_REG);
context()->Plug(eax);
} else {
VisitForStackValue(expr->obj());
VisitForAccumulatorValue(expr->key());
__ pop(LoadDescriptor::ReceiverRegister()); // Object.
__ Move(LoadDescriptor::NameRegister(), result_register()); // Key.
EmitKeyedPropertyLoad(expr);
if (!expr->IsSuperAccess()) {
VisitForStackValue(expr->obj());
VisitForAccumulatorValue(expr->key());
__ pop(LoadDescriptor::ReceiverRegister()); // Object.
__ Move(LoadDescriptor::NameRegister(), result_register()); // Key.
EmitKeyedPropertyLoad(expr);
} else {
VisitForStackValue(expr->obj()->AsSuperReference()->this_var());
EmitLoadHomeObject(expr->obj()->AsSuperReference());
__ push(result_register());
VisitForStackValue(expr->key());
EmitKeyedSuperPropertyLoad(expr);
}
context()->Plug(eax);
}
}
@ -2712,6 +2728,40 @@ void FullCodeGenerator::EmitKeyedCallWithLoadIC(Call* expr,
}
void FullCodeGenerator::EmitKeyedSuperCallWithLoadIC(Call* expr) {
Expression* callee = expr->expression();
DCHECK(callee->IsProperty());
Property* prop = callee->AsProperty();
DCHECK(prop->IsSuperAccess());
SetSourcePosition(prop->position());
// Load the function from the receiver.
SuperReference* super_ref = callee->AsProperty()->obj()->AsSuperReference();
EmitLoadHomeObject(super_ref);
__ push(eax);
VisitForAccumulatorValue(super_ref->this_var());
__ push(eax);
__ push(eax);
__ push(Operand(esp, kPointerSize * 2));
VisitForStackValue(prop->key());
// Stack here:
// - home_object
// - this (receiver)
// - this (receiver) <-- LoadKeyedFromSuper will pop here and below.
// - home_object
// - key
__ CallRuntime(Runtime::kLoadKeyedFromSuper, 3);
// Replace home_object with target function.
__ mov(Operand(esp, kPointerSize), eax);
// Stack here:
// - target function
// - this (receiver)
EmitCall(expr, CallICState::METHOD);
}
void FullCodeGenerator::EmitCall(Call* expr, CallICState::CallType call_type) {
// Load the arguments.
ZoneList<Expression*>* args = expr->arguments();
@ -2853,9 +2903,12 @@ void FullCodeGenerator::VisitCall(Call* expr) {
} else if (call_type == Call::PROPERTY_CALL) {
Property* property = callee->AsProperty();
bool is_named_call = property->key()->IsPropertyName();
// super.x() is handled in EmitCallWithLoadIC.
if (property->IsSuperAccess() && is_named_call) {
EmitSuperCallWithLoadIC(expr);
if (property->IsSuperAccess()) {
if (is_named_call) {
EmitSuperCallWithLoadIC(expr);
} else {
EmitKeyedSuperCallWithLoadIC(expr);
}
} else {
{
PreservePositionScope scope(masm()->positions_recorder());

View File

@ -16,6 +16,28 @@ namespace v8 {
namespace internal {
RUNTIME_FUNCTION(Runtime_ThrowNonMethodError) {
HandleScope scope(isolate);
DCHECK(args.length() == 0);
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewReferenceError("non_method", HandleVector<Object>(NULL, 0)));
}
static Object* ThrowUnsupportedSuper(Isolate* isolate) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate,
NewReferenceError("unsupported_super", HandleVector<Object>(NULL, 0)));
}
RUNTIME_FUNCTION(Runtime_ThrowUnsupportedSuperError) {
HandleScope scope(isolate);
DCHECK(args.length() == 0);
return ThrowUnsupportedSuper(isolate);
}
RUNTIME_FUNCTION(Runtime_ToMethod) {
HandleScope scope(isolate);
DCHECK(args.length() == 2);
@ -35,13 +57,8 @@ RUNTIME_FUNCTION(Runtime_HomeObjectSymbol) {
}
RUNTIME_FUNCTION(Runtime_LoadFromSuper) {
HandleScope scope(isolate);
DCHECK(args.length() == 3);
CONVERT_ARG_HANDLE_CHECKED(Object, receiver, 0);
CONVERT_ARG_HANDLE_CHECKED(JSObject, home_object, 1);
CONVERT_ARG_HANDLE_CHECKED(Name, name, 2);
static Object* LoadFromSuper(Isolate* isolate, Handle<Object> receiver,
Handle<JSObject> home_object, Handle<Name> name) {
if (home_object->IsAccessCheckNeeded() &&
!isolate->MayNamedAccess(home_object, name, v8::ACCESS_GET)) {
isolate->ReportFailedAccessCheck(home_object, v8::ACCESS_GET);
@ -59,6 +76,35 @@ RUNTIME_FUNCTION(Runtime_LoadFromSuper) {
}
RUNTIME_FUNCTION(Runtime_LoadFromSuper) {
HandleScope scope(isolate);
DCHECK(args.length() == 3);
CONVERT_ARG_HANDLE_CHECKED(Object, receiver, 0);
CONVERT_ARG_HANDLE_CHECKED(JSObject, home_object, 1);
CONVERT_ARG_HANDLE_CHECKED(Name, name, 2);
return LoadFromSuper(isolate, receiver, home_object, name);
}
RUNTIME_FUNCTION(Runtime_LoadKeyedFromSuper) {
HandleScope scope(isolate);
DCHECK(args.length() == 3);
CONVERT_ARG_HANDLE_CHECKED(Object, receiver, 0);
CONVERT_ARG_HANDLE_CHECKED(JSObject, home_object, 1);
CONVERT_ARG_HANDLE_CHECKED(Object, key, 2);
Handle<Name> name;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, name,
Runtime::ToName(isolate, key));
uint32_t index;
if (name->AsArrayIndex(&index)) {
return ThrowUnsupportedSuper(isolate);
}
return LoadFromSuper(isolate, receiver, home_object, name);
}
static Object* StoreToSuper(Isolate* isolate, Handle<JSObject> home_object,
Handle<Object> receiver, Handle<Name> name,
Handle<Object> value, StrictMode strict_mode) {

View File

@ -463,8 +463,7 @@ MaybeHandle<Object> Runtime::GetElementOrCharAt(Isolate* isolate,
}
MUST_USE_RESULT
static MaybeHandle<Name> ToName(Isolate* isolate, Handle<Object> key) {
MaybeHandle<Name> Runtime::ToName(Isolate* isolate, Handle<Object> key) {
if (key->IsName()) {
return Handle<Name>::cast(key);
} else {
@ -1742,23 +1741,6 @@ RUNTIME_FUNCTION(Runtime_ThrowReferenceError) {
}
RUNTIME_FUNCTION(Runtime_ThrowNonMethodError) {
HandleScope scope(isolate);
DCHECK(args.length() == 0);
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewReferenceError("non_method", HandleVector<Object>(NULL, 0)));
}
RUNTIME_FUNCTION(Runtime_ThrowUnsupportedSuperError) {
HandleScope scope(isolate);
DCHECK(args.length() == 0);
THROW_NEW_ERROR_RETURN_FAILURE(
isolate,
NewReferenceError("unsupported_super", HandleVector<Object>(NULL, 0)));
}
RUNTIME_FUNCTION(Runtime_PromiseRejectEvent) {
DCHECK(args.length() == 3);
HandleScope scope(isolate);

View File

@ -190,6 +190,7 @@ namespace internal {
F(ThrowNonMethodError, 0, 1) \
F(ThrowUnsupportedSuperError, 0, 1) \
F(LoadFromSuper, 3, 1) \
F(LoadKeyedFromSuper, 3, 1) \
F(StoreToSuper_Strict, 4, 1) \
F(StoreToSuper_Sloppy, 4, 1)
@ -852,6 +853,9 @@ class Runtime : public AllStatic {
MUST_USE_RESULT static MaybeHandle<Object> GetObjectProperty(
Isolate* isolate, Handle<Object> object, Handle<Object> key);
MUST_USE_RESULT static MaybeHandle<Name> ToName(Isolate* isolate,
Handle<Object> key);
static void SetupArrayBuffer(Isolate* isolate,
Handle<JSArrayBuffer> array_buffer,
bool is_external, void* data,

View File

@ -2332,6 +2332,14 @@ void FullCodeGenerator::EmitKeyedPropertyLoad(Property* prop) {
}
void FullCodeGenerator::EmitKeyedSuperPropertyLoad(Property* prop) {
// Stack: receiver, home_object, key.
SetSourcePosition(prop->position());
__ CallRuntime(Runtime::kLoadKeyedFromSuper, 3);
}
void FullCodeGenerator::EmitInlineSmiBinaryOp(BinaryOperation* expr,
Token::Value op,
OverwriteMode mode,
@ -2599,11 +2607,19 @@ void FullCodeGenerator::VisitProperty(Property* expr) {
PrepareForBailoutForId(expr->LoadId(), TOS_REG);
context()->Plug(rax);
} else {
VisitForStackValue(expr->obj());
VisitForAccumulatorValue(expr->key());
__ Move(LoadDescriptor::NameRegister(), rax);
__ Pop(LoadDescriptor::ReceiverRegister());
EmitKeyedPropertyLoad(expr);
if (!expr->IsSuperAccess()) {
VisitForStackValue(expr->obj());
VisitForAccumulatorValue(expr->key());
__ Move(LoadDescriptor::NameRegister(), rax);
__ Pop(LoadDescriptor::ReceiverRegister());
EmitKeyedPropertyLoad(expr);
} else {
VisitForStackValue(expr->obj()->AsSuperReference()->this_var());
EmitLoadHomeObject(expr->obj()->AsSuperReference());
__ Push(result_register());
VisitForStackValue(expr->key());
EmitKeyedSuperPropertyLoad(expr);
}
context()->Plug(rax);
}
}
@ -2707,6 +2723,41 @@ void FullCodeGenerator::EmitKeyedCallWithLoadIC(Call* expr,
}
void FullCodeGenerator::EmitKeyedSuperCallWithLoadIC(Call* expr) {
Expression* callee = expr->expression();
DCHECK(callee->IsProperty());
Property* prop = callee->AsProperty();
DCHECK(prop->IsSuperAccess());
SetSourcePosition(prop->position());
// Load the function from the receiver.
SuperReference* super_ref = prop->obj()->AsSuperReference();
EmitLoadHomeObject(super_ref);
__ Push(rax);
VisitForAccumulatorValue(super_ref->this_var());
__ Push(rax);
__ Push(rax);
__ Push(Operand(rsp, kPointerSize * 2));
VisitForStackValue(prop->key());
// Stack here:
// - home_object
// - this (receiver)
// - this (receiver) <-- LoadKeyedFromSuper will pop here and below.
// - home_object
// - key
__ CallRuntime(Runtime::kLoadKeyedFromSuper, 3);
// Replace home_object with target function.
__ movp(Operand(rsp, kPointerSize), rax);
// Stack here:
// - target function
// - this (receiver)
EmitCall(expr, CallICState::METHOD);
}
void FullCodeGenerator::EmitCall(Call* expr, CallICState::CallType call_type) {
// Load the arguments.
ZoneList<Expression*>* args = expr->arguments();
@ -2849,9 +2900,12 @@ void FullCodeGenerator::VisitCall(Call* expr) {
} else if (call_type == Call::PROPERTY_CALL) {
Property* property = callee->AsProperty();
bool is_named_call = property->key()->IsPropertyName();
// super.x() is handled in EmitCallWithLoadIC.
if (property->IsSuperAccess() && is_named_call) {
EmitSuperCallWithLoadIC(expr);
if (property->IsSuperAccess()) {
if (is_named_call) {
EmitSuperCallWithLoadIC(expr);
} else {
EmitKeyedSuperCallWithLoadIC(expr);
}
} else {
{
PreservePositionScope scope(masm()->positions_recorder());

View File

@ -37,6 +37,41 @@
}());
(function TestSuperKeyedLoads() {
var x = 'x';
var derivedDataProperty = 'derivedDataProperty';
var f = 'f';
function Base() { }
function Derived() {
this[derivedDataProperty] = 'xxx';
}
Derived.prototype = Object.create(Base.prototype);
function fBase() { return "Base " + this.toString(); }
Base.prototype[f] = fBase.toMethod(Base.prototype);
function fDerived() {
assertEquals("Base this is Derived", super[f]());
var a = super[x];
assertEquals(15, a);
assertEquals(15, super[x]);
assertEquals(27, this[x]);
return "Derived"
}
Base.prototype[x] = 15;
Base.prototype.toString = function() { return "this is Base"; };
Derived.prototype.toString = function() { return "this is Derived"; };
Derived.prototype[x] = 27;
Derived.prototype[f] = fDerived.toMethod(Derived.prototype);
assertEquals("Base this is Base", new Base().f());
assertEquals("Derived", new Derived().f());
}());
(function TestSuperKeywordNonMethod() {
function f() {
super.unknown();
@ -79,6 +114,80 @@
}());
(function TestGetterKeyed() {
var x = 'x';
function Base() {}
var derived;
Base.prototype = {
constructor: Base,
get x() {
assertSame(this, derived);
return this._x;
},
_x: 'base'
};
function Derived() {}
Derived.__proto__ = Base;
Derived.prototype = {
__proto__: Base.prototype,
constructor: Derived,
_x: 'derived'
};
Derived.prototype.testGetter = function() {
return super[x];
}.toMethod(Derived.prototype);
Derived.prototype.testGetterStrict = function() {
'use strict';
return super[x];
}.toMethod(Derived.prototype);
Derived.prototype.testGetterWithToString = function() {
var toStringCalled;
var o = { toString: function() {
toStringCalled++;
return 'x';
} };
toStringCalled = 0;
assertEquals('derived', super[o]);
assertEquals(1, toStringCalled);
var eToThrow = new Error();
var oThrowsInToString = { toString: function() {
throw eToThrow;
} };
var ex = null;
try {
super[oThrowsInToString];
} catch(e) { ex = e }
assertEquals(eToThrow, ex);
var oReturnsNumericString = { toString: function() {
return "1";
} };
ex = null;
try {
super[oReturnsNumericString];
} catch(e) { ex = e }
assertTrue(ex instanceof ReferenceError);
ex = null;
try {
super[1]; // Indexed properties unsupported yet.
} catch (e) { ex = e; }
assertTrue(ex instanceof ReferenceError);
}.toMethod(Derived.prototype);
derived = new Derived();
assertEquals('derived', derived.testGetter());
derived = new Derived();
assertEquals('derived', derived.testGetterStrict());
derived = new Derived();
derived.testGetterWithToString();
}());
(function TestSetter() {
function Base() {}
Base.prototype = {
@ -536,6 +645,6 @@
function f1(x) { return super[x]; }
function f2(x) { super[x] = 5; }
var o = {};
assertThrows(function(){f1.toMethod(o)(x);}, ReferenceError);
assertThrows(function(){f2.toMethod(o)(x);}, ReferenceError);
assertThrows(function(){f1.toMethod(o)(15);}, ReferenceError);
assertThrows(function(){f2.toMethod(o)(15);}, ReferenceError);
}());