Improve the generated code for the instanceof operator,
and extended the instanceof test case. Review URL: http://codereview.chromium.org/6341 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@470 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
614d78367f
commit
77643dbef6
@ -110,6 +110,8 @@ const char* CodeStub::MajorName(CodeStub::Major major_key) {
|
||||
return "RevertToNumber";
|
||||
case ToBoolean:
|
||||
return "ToBoolean";
|
||||
case Instanceof:
|
||||
return "Instanceof";
|
||||
case CounterOp:
|
||||
return "CounterOp";
|
||||
case ArgumentsAccess:
|
||||
|
@ -44,6 +44,7 @@ class CodeStub BASE_EMBEDDED {
|
||||
UnarySub,
|
||||
RevertToNumber,
|
||||
ToBoolean,
|
||||
Instanceof,
|
||||
CounterOp,
|
||||
ArgumentsAccess,
|
||||
Runtime,
|
||||
@ -82,7 +83,7 @@ class CodeStub BASE_EMBEDDED {
|
||||
virtual int MinorKey() = 0;
|
||||
|
||||
// Returns a name for logging/debugging purposes.
|
||||
virtual const char* GetName() = 0;
|
||||
virtual const char* GetName() { return MajorName(MajorKey()); }
|
||||
|
||||
#ifdef DEBUG
|
||||
virtual void Print() { PrintF("%s\n", GetName()); }
|
||||
|
@ -887,8 +887,6 @@ class GetPropertyStub : public CodeStub {
|
||||
Major MajorKey() { return GetProperty; }
|
||||
int MinorKey() { return 0; }
|
||||
void Generate(MacroAssembler* masm);
|
||||
|
||||
const char* GetName() { return "GetPropertyStub"; }
|
||||
};
|
||||
|
||||
|
||||
@ -900,8 +898,6 @@ class SetPropertyStub : public CodeStub {
|
||||
Major MajorKey() { return SetProperty; }
|
||||
int MinorKey() { return 0; }
|
||||
void Generate(MacroAssembler* masm);
|
||||
|
||||
const char* GetName() { return "GetPropertyStub"; }
|
||||
};
|
||||
|
||||
|
||||
@ -951,8 +947,6 @@ class InvokeBuiltinStub : public CodeStub {
|
||||
int MinorKey() { return (argc_ << 3) | static_cast<int>(kind_); }
|
||||
void Generate(MacroAssembler* masm);
|
||||
|
||||
const char* GetName() { return "InvokeBuiltinStub"; }
|
||||
|
||||
#ifdef DEBUG
|
||||
void Print() {
|
||||
PrintF("InvokeBuiltinStub (kind %d, argc, %d)\n",
|
||||
@ -1294,8 +1288,6 @@ class CallFunctionStub: public CodeStub {
|
||||
private:
|
||||
int argc_;
|
||||
|
||||
const char* GetName() { return "CallFuntionStub"; }
|
||||
|
||||
#if defined(DEBUG)
|
||||
void Print() { PrintF("CallFunctionStub (argc %d)\n", argc_); }
|
||||
#endif // defined(DEBUG)
|
||||
@ -3386,7 +3378,8 @@ void ArmCodeGenerator::VisitCompareOperation(CompareOperation* node) {
|
||||
case Token::INSTANCEOF:
|
||||
__ mov(r0, Operand(1)); // not counting receiver
|
||||
__ InvokeBuiltin(Builtins::INSTANCE_OF, CALL_JS);
|
||||
__ push(r0);
|
||||
__ tst(r0, Operand(r0));
|
||||
cc_reg_ = eq;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -894,18 +894,8 @@ class ToBooleanStub: public CodeStub {
|
||||
void Generate(MacroAssembler* masm);
|
||||
|
||||
private:
|
||||
|
||||
Major MajorKey() { return ToBoolean; }
|
||||
|
||||
int MinorKey() { return 0; }
|
||||
|
||||
const char* GetName() { return "ToBooleanStub"; }
|
||||
|
||||
#ifdef DEBUG
|
||||
void Print() {
|
||||
PrintF("ToBooleanStub\n");
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
@ -1476,8 +1466,6 @@ class CompareStub: public CodeStub {
|
||||
return (static_cast<int>(cc_) << 1) | (strict_ ? 1 : 0);
|
||||
}
|
||||
|
||||
const char* GetName() { return "CompareStub"; }
|
||||
|
||||
#ifdef DEBUG
|
||||
void Print() {
|
||||
PrintF("CompareStub (cc %d), (strict %s)\n",
|
||||
@ -1585,8 +1573,6 @@ class CallFunctionStub: public CodeStub {
|
||||
private:
|
||||
int argc_;
|
||||
|
||||
const char* GetName() { return "CallFunctionStub"; }
|
||||
|
||||
#ifdef DEBUG
|
||||
void Print() { PrintF("CallFunctionStub (args %d)\n", argc_); }
|
||||
#endif
|
||||
@ -3521,8 +3507,6 @@ class RevertToNumberStub: public CodeStub {
|
||||
int MinorKey() { return is_increment_ ? 1 : 0; }
|
||||
void Generate(MacroAssembler* masm);
|
||||
|
||||
const char* GetName() { return "RevertToNumberStub"; }
|
||||
|
||||
#ifdef DEBUG
|
||||
void Print() {
|
||||
PrintF("RevertToNumberStub (is_increment %s)\n",
|
||||
@ -3552,8 +3536,6 @@ class CounterOpStub: public CodeStub {
|
||||
}
|
||||
void Generate(MacroAssembler* masm);
|
||||
|
||||
const char* GetName() { return "CounterOpStub"; }
|
||||
|
||||
#ifdef DEBUG
|
||||
void Print() {
|
||||
PrintF("CounterOpStub (result_offset %d), (is_postfix %s),"
|
||||
@ -3754,6 +3736,18 @@ void Ia32CodeGenerator::VisitThisFunction(ThisFunction* node) {
|
||||
}
|
||||
|
||||
|
||||
class InstanceofStub: public CodeStub {
|
||||
public:
|
||||
InstanceofStub() { }
|
||||
|
||||
void Generate(MacroAssembler* masm);
|
||||
|
||||
private:
|
||||
Major MajorKey() { return Instanceof; }
|
||||
int MinorKey() { return 0; }
|
||||
};
|
||||
|
||||
|
||||
void Ia32CodeGenerator::VisitCompareOperation(CompareOperation* node) {
|
||||
Comment cmnt(masm_, "[ CompareOperation");
|
||||
|
||||
@ -3934,8 +3928,10 @@ void Ia32CodeGenerator::VisitCompareOperation(CompareOperation* node) {
|
||||
case Token::INSTANCEOF: {
|
||||
Load(left);
|
||||
Load(right);
|
||||
__ InvokeBuiltin(Builtins::INSTANCE_OF, CALL_FUNCTION);
|
||||
__ push(eax); // push the result
|
||||
InstanceofStub stub;
|
||||
__ CallStub(&stub);
|
||||
__ test(eax, Operand(eax));
|
||||
cc_reg_ = zero;
|
||||
return;
|
||||
}
|
||||
default:
|
||||
@ -5289,6 +5285,53 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
|
||||
}
|
||||
|
||||
|
||||
void InstanceofStub::Generate(MacroAssembler* masm) {
|
||||
// Get the object - go slow case if it's a smi.
|
||||
Label slow;
|
||||
__ mov(eax, Operand(esp, 2 * kPointerSize)); // 2 ~ return address, function
|
||||
__ test(eax, Immediate(kSmiTagMask));
|
||||
__ j(zero, &slow, not_taken);
|
||||
|
||||
// Check that the left hand is a JS object.
|
||||
__ mov(eax, FieldOperand(eax, HeapObject::kMapOffset)); // ebx - object map
|
||||
__ movzx_b(ecx, FieldOperand(eax, Map::kInstanceTypeOffset)); // ecx - type
|
||||
__ cmp(ecx, FIRST_JS_OBJECT_TYPE);
|
||||
__ j(less, &slow, not_taken);
|
||||
__ cmp(ecx, LAST_JS_OBJECT_TYPE);
|
||||
__ j(greater, &slow, not_taken);
|
||||
|
||||
// Get the prototype of the function.
|
||||
__ mov(edx, Operand(esp, 1 * kPointerSize)); // 1 ~ return address
|
||||
__ TryGetFunctionPrototype(edx, ebx, ecx, &slow);
|
||||
|
||||
// Register mapping: eax is object map and ebx is function prototype.
|
||||
__ mov(ecx, FieldOperand(eax, Map::kPrototypeOffset));
|
||||
|
||||
// Loop through the prototype chain looking for the function prototype.
|
||||
Label loop, is_instance, is_not_instance;
|
||||
__ bind(&loop);
|
||||
__ cmp(ecx, Operand(ebx));
|
||||
__ j(equal, &is_instance);
|
||||
__ cmp(Operand(ecx), Immediate(Factory::null_value()));
|
||||
__ j(equal, &is_not_instance);
|
||||
__ mov(ecx, FieldOperand(ecx, HeapObject::kMapOffset));
|
||||
__ mov(ecx, FieldOperand(ecx, Map::kPrototypeOffset));
|
||||
__ jmp(&loop);
|
||||
|
||||
__ bind(&is_instance);
|
||||
__ Set(eax, Immediate(0));
|
||||
__ ret(2 * kPointerSize);
|
||||
|
||||
__ bind(&is_not_instance);
|
||||
__ Set(eax, Immediate(Smi::FromInt(1)));
|
||||
__ ret(2 * kPointerSize);
|
||||
|
||||
// Slow-case: Go through the JavaScript implementation.
|
||||
__ bind(&slow);
|
||||
__ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_FUNCTION);
|
||||
}
|
||||
|
||||
|
||||
#undef __
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -99,8 +99,6 @@ class RecordWriteStub : public CodeStub {
|
||||
Register addr_;
|
||||
Register scratch_;
|
||||
|
||||
const char* GetName() { return "RecordWriteStub"; }
|
||||
|
||||
#ifdef DEBUG
|
||||
void Print() {
|
||||
PrintF("RecordWriteStub (object reg %d), (addr reg %d), (scratch reg %d)\n",
|
||||
@ -589,6 +587,57 @@ void MacroAssembler::NegativeZeroTest(Register result,
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::TryGetFunctionPrototype(Register function,
|
||||
Register result,
|
||||
Register scratch,
|
||||
Label* miss) {
|
||||
// Check that the receiver isn't a smi.
|
||||
test(function, Immediate(kSmiTagMask));
|
||||
j(zero, miss, not_taken);
|
||||
|
||||
// Check that the function really is a function.
|
||||
mov(result, FieldOperand(function, HeapObject::kMapOffset));
|
||||
movzx_b(scratch, FieldOperand(result, Map::kInstanceTypeOffset));
|
||||
cmp(scratch, JS_FUNCTION_TYPE);
|
||||
j(not_equal, miss, not_taken);
|
||||
|
||||
// Make sure that the function has an instance prototype.
|
||||
Label non_instance;
|
||||
movzx_b(scratch, FieldOperand(result, Map::kBitFieldOffset));
|
||||
test(scratch, Immediate(1 << Map::kHasNonInstancePrototype));
|
||||
j(not_zero, &non_instance, not_taken);
|
||||
|
||||
// Get the prototype or initial map from the function.
|
||||
mov(result,
|
||||
FieldOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
|
||||
|
||||
// If the prototype or initial map is the hole, don't return it and
|
||||
// simply miss the cache instead. This will allow us to allocate a
|
||||
// prototype object on-demand in the runtime system.
|
||||
cmp(Operand(result), Immediate(Factory::the_hole_value()));
|
||||
j(equal, miss, not_taken);
|
||||
|
||||
// If the function does not have an initial map, we're done.
|
||||
Label done;
|
||||
mov(scratch, FieldOperand(result, HeapObject::kMapOffset));
|
||||
movzx_b(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset));
|
||||
cmp(scratch, MAP_TYPE);
|
||||
j(not_equal, &done);
|
||||
|
||||
// Get the prototype from the initial map.
|
||||
mov(result, FieldOperand(result, Map::kPrototypeOffset));
|
||||
jmp(&done);
|
||||
|
||||
// Non-instance prototype: Fetch prototype from constructor field
|
||||
// in initial map.
|
||||
bind(&non_instance);
|
||||
mov(result, FieldOperand(result, Map::kConstructorOffset));
|
||||
|
||||
// All done.
|
||||
bind(&done);
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::CallStub(CodeStub* stub) {
|
||||
ASSERT(allow_stub_calls()); // calls are not allowed in some stubs
|
||||
call(stub->GetCode(), RelocInfo::CODE_TARGET);
|
||||
|
@ -179,6 +179,16 @@ class MacroAssembler: public Assembler {
|
||||
void NegativeZeroTest(Register result, Register op1, Register op2,
|
||||
Register scratch, Label* then_label);
|
||||
|
||||
// Try to get function prototype of a function and puts the value in
|
||||
// the result register. Checks that the function really is a
|
||||
// function and jumps to the miss label if the fast checks fail. The
|
||||
// function register will be untouched; the other registers may be
|
||||
// clobbered.
|
||||
void TryGetFunctionPrototype(Register function,
|
||||
Register result,
|
||||
Register scratch,
|
||||
Label* miss);
|
||||
|
||||
// Generates code for reporting that an illegal operation has
|
||||
// occurred.
|
||||
void IllegalOperation(int num_arguments);
|
||||
|
@ -274,7 +274,10 @@ function IN(x) {
|
||||
}
|
||||
|
||||
|
||||
// ECMA-262, section 11.8.6, page 54.
|
||||
// ECMA-262, section 11.8.6, page 54. To make the implementation more
|
||||
// efficient, the return value should be zero if the 'this' is an
|
||||
// instance of F, and non-zero if not. This makes it possible to avoid
|
||||
// an expensive ToBoolean conversion in the generated code.
|
||||
function INSTANCE_OF(F) {
|
||||
var V = this;
|
||||
if (!IS_FUNCTION(F)) {
|
||||
@ -283,7 +286,7 @@ function INSTANCE_OF(F) {
|
||||
|
||||
// If V is not an object, return false.
|
||||
if (IS_NULL(V) || (!IS_OBJECT(V) && !IS_FUNCTION(V))) {
|
||||
return false;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Get the prototype of F; if it is not an object, throw an error.
|
||||
@ -293,7 +296,7 @@ function INSTANCE_OF(F) {
|
||||
}
|
||||
|
||||
// Return whether or not O is in the prototype chain of V.
|
||||
return %IsInPrototypeChain(O, V);
|
||||
return %IsInPrototypeChain(O, V) ? 0 : 1;
|
||||
}
|
||||
|
||||
|
||||
|
@ -232,51 +232,9 @@ void StubCompiler::GenerateLoadFunctionPrototype(MacroAssembler* masm,
|
||||
Register scratch1,
|
||||
Register scratch2,
|
||||
Label* miss_label) {
|
||||
// Check that the receiver isn't a smi.
|
||||
__ test(receiver, Immediate(kSmiTagMask));
|
||||
__ j(zero, miss_label, not_taken);
|
||||
|
||||
// Check that the receiver is a function.
|
||||
__ mov(scratch1, FieldOperand(receiver, HeapObject::kMapOffset));
|
||||
__ movzx_b(scratch2, FieldOperand(scratch1, Map::kInstanceTypeOffset));
|
||||
__ cmp(scratch2, JS_FUNCTION_TYPE);
|
||||
__ j(not_equal, miss_label, not_taken);
|
||||
|
||||
// Make sure that the function has an instance prototype.
|
||||
Label non_instance;
|
||||
__ movzx_b(scratch2, FieldOperand(scratch1, Map::kBitFieldOffset));
|
||||
__ test(scratch2, Immediate(1 << Map::kHasNonInstancePrototype));
|
||||
__ j(not_zero, &non_instance, not_taken);
|
||||
|
||||
// Get the prototype or initial map from the function.
|
||||
__ mov(scratch1,
|
||||
FieldOperand(receiver, JSFunction::kPrototypeOrInitialMapOffset));
|
||||
|
||||
// If the prototype or initial map is the hole, don't return it and
|
||||
// simply miss the cache instead. This will allow us to allocate a
|
||||
// prototype object on-demand in the runtime system.
|
||||
__ cmp(Operand(scratch1), Immediate(Factory::the_hole_value()));
|
||||
__ j(equal, miss_label, not_taken);
|
||||
__ TryGetFunctionPrototype(receiver, scratch1, scratch2, miss_label);
|
||||
__ mov(eax, Operand(scratch1));
|
||||
|
||||
// If the function does not have an initial map, we're done.
|
||||
Label done;
|
||||
__ mov(scratch1, FieldOperand(eax, HeapObject::kMapOffset));
|
||||
__ movzx_b(scratch2, FieldOperand(scratch1, Map::kInstanceTypeOffset));
|
||||
__ cmp(scratch2, MAP_TYPE);
|
||||
__ j(not_equal, &done);
|
||||
|
||||
// Get the prototype from the initial map.
|
||||
__ mov(eax, FieldOperand(eax, Map::kPrototypeOffset));
|
||||
|
||||
// All done: Return the prototype.
|
||||
__ bind(&done);
|
||||
__ ret(0);
|
||||
|
||||
// Non-instance prototype: Fetch prototype from constructor field
|
||||
// in initial map.
|
||||
__ bind(&non_instance);
|
||||
__ mov(eax, FieldOperand(scratch1, Map::kConstructorOffset));
|
||||
__ ret(0);
|
||||
}
|
||||
|
||||
|
@ -30,3 +30,58 @@ assertTrue([] instanceof Object);
|
||||
|
||||
assertFalse({} instanceof Array);
|
||||
assertTrue([] instanceof Array);
|
||||
|
||||
function TestChains() {
|
||||
var A = {};
|
||||
var B = {};
|
||||
var C = {};
|
||||
B.__proto__ = A;
|
||||
C.__proto__ = B;
|
||||
|
||||
function F() { }
|
||||
F.prototype = A;
|
||||
assertTrue(C instanceof F);
|
||||
assertTrue(B instanceof F);
|
||||
assertFalse(A instanceof F);
|
||||
|
||||
F.prototype = B;
|
||||
assertTrue(C instanceof F);
|
||||
assertFalse(B instanceof F);
|
||||
assertFalse(A instanceof F);
|
||||
|
||||
F.prototype = C;
|
||||
assertFalse(C instanceof F);
|
||||
assertFalse(B instanceof F);
|
||||
assertFalse(A instanceof F);
|
||||
}
|
||||
|
||||
TestChains();
|
||||
|
||||
|
||||
function TestExceptions() {
|
||||
function F() { }
|
||||
var items = [ 1, new Number(42),
|
||||
true,
|
||||
'string', new String('hest'),
|
||||
{}, [],
|
||||
F, new F(),
|
||||
Object, String ];
|
||||
|
||||
var exceptions = 0;
|
||||
var instanceofs = 0;
|
||||
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
for (var j = 0; j < items.length; j++) {
|
||||
try {
|
||||
if (items[i] instanceof items[j]) instanceofs++;
|
||||
} catch (e) {
|
||||
assertTrue(e instanceof TypeError);
|
||||
exceptions++;
|
||||
}
|
||||
}
|
||||
}
|
||||
assertEquals(10, instanceofs);
|
||||
assertEquals(88, exceptions);
|
||||
}
|
||||
|
||||
TestExceptions();
|
||||
|
Loading…
Reference in New Issue
Block a user