Handle overwriting valueOf on String objects correctly when adding

This adds a check to the fast case string add to ensure that the String object still have the default valueOf function. The default valueOf is sitting on a hidden prototype of String.prototype.

Before using the fast case valueOf the object is checked for a local valueOf property. For slow case objects this check always reports true (the dictionary is not probed, so valueOf might be there) and for fast case objects the descriptor array is checked for the valueOf symbol (just liniar scan). After that the prototype is checked for beeing the initial value of String.prototype. If this all pass (that is the default valueOf is still in place) this result is cached on the map making the check fast the next time.

This is only implemented in the optimizing compiler, as the two usages of %_IsStringWrapperSafeForDefaultValueOf is never hit by the full compiler.

I will port to x64 and ARM when this has been reviewed for ia32.

I will remove the performance counters prior to final commit.

BUG=http://code.google.com/p/v8/issues/detail?id=760
TEST=test/mjsunit/regress/regress-760-1.js
TEST=test/mjsunit/regress/regress-760-2.js

Review URL: http://codereview.chromium.org/3117006

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5252 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
sgjesse@chromium.org 2010-08-12 13:43:08 +00:00
parent bce9cb113a
commit 8e0cd6db8a
23 changed files with 635 additions and 19 deletions

View File

@ -4786,6 +4786,149 @@ void CodeGenerator::GenerateIsSpecObject(ZoneList<Expression*>* args) {
}
// Deferred code to check whether the String JavaScript object is safe for using
// default value of. This code is called after the bit caching this information
// in the map has been checked with the map for the object in the map_result_
// register. On return the register map_result_ contains 1 for true and 0 for
// false.
class DeferredIsStringWrapperSafeForDefaultValueOf : public DeferredCode {
public:
DeferredIsStringWrapperSafeForDefaultValueOf(Register object,
Register map_result,
Register scratch1,
Register scratch2)
: object_(object),
map_result_(map_result),
scratch1_(scratch1),
scratch2_(scratch2) { }
virtual void Generate() {
Label false_result;
// Check that map is loaded as expected.
if (FLAG_debug_code) {
__ ldr(ip, FieldMemOperand(object_, HeapObject::kMapOffset));
__ cmp(map_result_, ip);
__ Assert(eq, "Map not in expected register");
}
// Check for fast case object. Generate false result for slow case object.
__ ldr(scratch1_, FieldMemOperand(object_, JSObject::kPropertiesOffset));
__ ldr(scratch1_, FieldMemOperand(scratch1_, HeapObject::kMapOffset));
__ LoadRoot(ip, Heap::kHashTableMapRootIndex);
__ cmp(scratch1_, ip);
__ b(eq, &false_result);
// Look for valueOf symbol in the descriptor array, and indicate false if
// found. The type is not checked, so if it is a transition it is a false
// negative.
__ ldr(map_result_,
FieldMemOperand(map_result_, Map::kInstanceDescriptorsOffset));
__ ldr(scratch2_, FieldMemOperand(map_result_, FixedArray::kLengthOffset));
// map_result_: descriptor array
// scratch2_: length of descriptor array
// Calculate the end of the descriptor array.
STATIC_ASSERT(kSmiTag == 0);
STATIC_ASSERT(kSmiTagSize == 1);
STATIC_ASSERT(kPointerSize == 4);
__ add(scratch1_,
map_result_,
Operand(FixedArray::kHeaderSize - kHeapObjectTag));
__ add(scratch1_,
scratch1_,
Operand(scratch2_, LSL, kPointerSizeLog2 - kSmiTagSize));
// Calculate location of the first key name.
__ add(map_result_,
map_result_,
Operand(FixedArray::kHeaderSize +
DescriptorArray::kFirstIndex * kPointerSize));
// Loop through all the keys in the descriptor array. If one of these is the
// symbol valueOf the result is false.
Label entry, loop;
__ jmp(&entry);
__ bind(&loop);
__ ldr(scratch2_, FieldMemOperand(map_result_, 0));
__ cmp(scratch2_, Operand(Factory::value_of_symbol()));
__ b(eq, &false_result);
__ add(map_result_, map_result_, Operand(kPointerSize));
__ bind(&entry);
__ cmp(map_result_, Operand(scratch1_));
__ b(ne, &loop);
// Reload map as register map_result_ was used as temporary above.
__ ldr(map_result_, FieldMemOperand(object_, HeapObject::kMapOffset));
// If a valueOf property is not found on the object check that it's
// prototype is the un-modified String prototype. If not result is false.
__ ldr(scratch1_, FieldMemOperand(map_result_, Map::kPrototypeOffset));
__ tst(scratch1_, Operand(kSmiTagMask));
__ b(eq, &false_result);
__ ldr(scratch1_, FieldMemOperand(scratch1_, HeapObject::kMapOffset));
__ ldr(scratch2_,
CodeGenerator::ContextOperand(cp, Context::GLOBAL_INDEX));
__ ldr(scratch2_,
FieldMemOperand(scratch2_, GlobalObject::kGlobalContextOffset));
__ ldr(scratch2_,
CodeGenerator::ContextOperand(
scratch2_, Context::STRING_FUNCTION_PROTOTYPE_MAP_INDEX));
__ cmp(scratch1_, scratch2_);
__ b(ne, &false_result);
// Set the bit in the map to indicate that it has been checked safe for
// default valueOf and set true result.
__ ldr(scratch1_, FieldMemOperand(map_result_, Map::kBitField2Offset));
__ orr(scratch1_,
scratch1_,
Operand(1 << Map::kStringWrapperSafeForDefaultValueOf));
__ str(scratch1_, FieldMemOperand(map_result_, Map::kBitField2Offset));
__ mov(map_result_, Operand(1));
__ jmp(exit_label());
__ bind(&false_result);
// Set false result.
__ mov(map_result_, Operand(0));
}
private:
Register object_;
Register map_result_;
Register scratch1_;
Register scratch2_;
};
void CodeGenerator::GenerateIsStringWrapperSafeForDefaultValueOf(
ZoneList<Expression*>* args) {
ASSERT(args->length() == 1);
Load(args->at(0));
Register obj = frame_->PopToRegister(); // Pop the string wrapper.
if (FLAG_debug_code) {
__ AbortIfSmi(obj);
}
// Check whether this map has already been checked to be safe for default
// valueOf.
Register map_result = VirtualFrame::scratch0();
__ ldr(map_result, FieldMemOperand(obj, HeapObject::kMapOffset));
__ ldrb(ip, FieldMemOperand(map_result, Map::kBitField2Offset));
__ tst(ip, Operand(1 << Map::kStringWrapperSafeForDefaultValueOf));
true_target()->Branch(ne);
// We need an additional two scratch registers for the deferred code.
Register scratch1 = VirtualFrame::scratch1();
// Use r6 without notifying the virtual frame.
Register scratch2 = r6;
DeferredIsStringWrapperSafeForDefaultValueOf* deferred =
new DeferredIsStringWrapperSafeForDefaultValueOf(
obj, map_result, scratch1, scratch2);
deferred->Branch(eq);
deferred->BindExit();
__ tst(map_result, Operand(map_result));
cc_reg_ = ne;
}
void CodeGenerator::GenerateIsFunction(ZoneList<Expression*>* args) {
// This generates a fast version of:
// (%_ClassOf(arg) === 'Function')

View File

@ -286,6 +286,10 @@ class CodeGenerator: public AstVisitor {
return inlined_write_barrier_size_ + 4;
}
static MemOperand ContextOperand(Register context, int index) {
return MemOperand(context, Context::SlotOffset(index));
}
private:
// Construction/Destruction
explicit CodeGenerator(MacroAssembler* masm);
@ -338,10 +342,6 @@ class CodeGenerator: public AstVisitor {
void LoadReference(Reference* ref);
void UnloadReference(Reference* ref);
static MemOperand ContextOperand(Register context, int index) {
return MemOperand(context, Context::SlotOffset(index));
}
MemOperand SlotOperand(Slot* slot, Register tmp);
MemOperand ContextSlotOperandCheckExtensions(Slot* slot,
@ -482,6 +482,8 @@ class CodeGenerator: public AstVisitor {
void GenerateIsSpecObject(ZoneList<Expression*>* args);
void GenerateIsFunction(ZoneList<Expression*>* args);
void GenerateIsUndetectableObject(ZoneList<Expression*>* args);
void GenerateIsStringWrapperSafeForDefaultValueOf(
ZoneList<Expression*>* args);
// Support for construct call checks.
void GenerateIsConstructCall(ZoneList<Expression*>* args);

View File

@ -1957,6 +1957,26 @@ void FullCodeGenerator::EmitIsUndetectableObject(ZoneList<Expression*>* args) {
}
void FullCodeGenerator::EmitIsStringWrapperSafeForDefaultValueOf(
ZoneList<Expression*>* args) {
ASSERT(args->length() == 1);
VisitForValue(args->at(0), kAccumulator);
Label materialize_true, materialize_false;
Label* if_true = NULL;
Label* if_false = NULL;
PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false);
// Just indicate false, as %_IsStringWrapperSafeForDefaultValueOf() is only
// used in a few functions in runtime.js which should not normally be hit by
// this compiler.
__ jmp(if_false);
Apply(context_, if_true, if_false);
}
void FullCodeGenerator::EmitIsFunction(ZoneList<Expression*>* args) {
ASSERT(args->length() == 1);

View File

@ -1655,6 +1655,13 @@ void MacroAssembler::JumpIfEitherSmi(Register reg1,
}
void MacroAssembler::AbortIfSmi(Register object) {
ASSERT_EQ(0, kSmiTag);
tst(object, Operand(kSmiTagMask));
Assert(ne, "Operand is a smi");
}
void MacroAssembler::JumpIfNonSmisNotBothSequentialAsciiStrings(
Register first,
Register second,

View File

@ -618,6 +618,9 @@ class MacroAssembler: public Assembler {
// Jump if either of the registers contain a smi.
void JumpIfEitherSmi(Register reg1, Register reg2, Label* on_either_smi);
// Abort execution if argument is a smi. Used in debug code.
void AbortIfSmi(Register object);
// ---------------------------------------------------------------------------
// String utilities

View File

@ -1234,6 +1234,14 @@ bool Genesis::InstallNatives() {
InstallNativeFunctions();
// Store the map for the string prototype after the natives has been compiled
// and the String function has been setup.
Handle<JSFunction> string_function(global_context()->string_function());
ASSERT(JSObject::cast(
string_function->initial_map()->prototype())->HasFastProperties());
global_context()->set_string_function_prototype_map(
HeapObject::cast(string_function->initial_map()->prototype())->map());
InstallCustomCallGenerators();
// Install Function.prototype.call and apply.

View File

@ -101,7 +101,8 @@ enum UncatchableExceptionType { OUT_OF_MEMORY, TERMINATION };
F(IsObject, 1, 1) \
F(IsFunction, 1, 1) \
F(IsUndetectableObject, 1, 1) \
F(IsSpecObject, 1, 1) \
F(IsSpecObject, 1, 1) \
F(IsStringWrapperSafeForDefaultValueOf, 1, 1) \
F(StringAdd, 2, 1) \
F(SubString, 3, 1) \
F(StringCompare, 2, 1) \

View File

@ -56,6 +56,7 @@ enum ContextLookupFlags {
V(BOOLEAN_FUNCTION_INDEX, JSFunction, boolean_function) \
V(NUMBER_FUNCTION_INDEX, JSFunction, number_function) \
V(STRING_FUNCTION_INDEX, JSFunction, string_function) \
V(STRING_FUNCTION_PROTOTYPE_MAP_INDEX, Map, string_function_prototype_map) \
V(OBJECT_FUNCTION_INDEX, JSFunction, object_function) \
V(ARRAY_FUNCTION_INDEX, JSFunction, array_function) \
V(DATE_FUNCTION_INDEX, JSFunction, date_function) \
@ -186,6 +187,7 @@ class Context: public FixedArray {
BOOLEAN_FUNCTION_INDEX,
NUMBER_FUNCTION_INDEX,
STRING_FUNCTION_INDEX,
STRING_FUNCTION_PROTOTYPE_MAP_INDEX,
OBJECT_FUNCTION_INDEX,
ARRAY_FUNCTION_INDEX,
DATE_FUNCTION_INDEX,

View File

@ -919,6 +919,9 @@ void FullCodeGenerator::EmitInlineRuntimeCall(CallRuntime* expr) {
EmitGetFromCache(expr->arguments());
} else if (strcmp("_IsRegExpEquivalent", *name->ToCString()) == 0) {
EmitIsRegExpEquivalent(expr->arguments());
} else if (strcmp("_IsStringWrapperSafeForDefaultValueOf",
*name->ToCString()) == 0) {
EmitIsStringWrapperSafeForDefaultValueOf(expr->arguments());
} else {
UNREACHABLE();
}

View File

@ -408,6 +408,8 @@ class FullCodeGenerator: public AstVisitor {
void EmitIsArray(ZoneList<Expression*>* arguments);
void EmitIsRegExp(ZoneList<Expression*>* arguments);
void EmitIsConstructCall(ZoneList<Expression*>* arguments);
void EmitIsStringWrapperSafeForDefaultValueOf(
ZoneList<Expression*>* arguments);
void EmitObjectEquals(ZoneList<Expression*>* arguments);
void EmitArguments(ZoneList<Expression*>* arguments);
void EmitArgumentsLength(ZoneList<Expression*>* arguments);

View File

@ -6760,7 +6760,7 @@ void CodeGenerator::GenerateIsObject(ZoneList<Expression*>* args) {
}
void CodeGenerator::GenerateIsSpecObject(ZoneList<Expression*>* args) {
void CodeGenerator::GenerateIsSpecObject(ZoneList<Expression*>* args) {
// This generates a fast version of:
// (typeof(arg) === 'object' || %_ClassOf(arg) == 'RegExp' ||
// typeof(arg) == function).
@ -6781,6 +6781,143 @@ void CodeGenerator::GenerateIsObject(ZoneList<Expression*>* args) {
}
// Deferred code to check whether the String JavaScript object is safe for using
// default value of. This code is called after the bit caching this information
// in the map has been checked with the map for the object in the map_result_
// register. On return the register map_result_ contains 1 for true and 0 for
// false.
class DeferredIsStringWrapperSafeForDefaultValueOf : public DeferredCode {
public:
DeferredIsStringWrapperSafeForDefaultValueOf(Register object,
Register map_result,
Register scratch1,
Register scratch2)
: object_(object),
map_result_(map_result),
scratch1_(scratch1),
scratch2_(scratch2) { }
virtual void Generate() {
Label false_result;
// Check that map is loaded as expected.
if (FLAG_debug_code) {
__ cmp(map_result_, FieldOperand(object_, HeapObject::kMapOffset));
__ Assert(equal, "Map not in expected register");
}
// Check for fast case object. Generate false result for slow case object.
__ mov(scratch1_, FieldOperand(object_, JSObject::kPropertiesOffset));
__ mov(scratch1_, FieldOperand(scratch1_, HeapObject::kMapOffset));
__ cmp(scratch1_, Factory::hash_table_map());
__ j(equal, &false_result);
// Look for valueOf symbol in the descriptor array, and indicate false if
// found. The type is not checked, so if it is a transition it is a false
// negative.
__ mov(map_result_,
FieldOperand(map_result_, Map::kInstanceDescriptorsOffset));
__ mov(scratch1_, FieldOperand(map_result_, FixedArray::kLengthOffset));
// map_result_: descriptor array
// scratch1_: length of descriptor array
// Calculate the end of the descriptor array.
STATIC_ASSERT(kSmiTag == 0);
STATIC_ASSERT(kSmiTagSize == 1);
STATIC_ASSERT(kPointerSize == 4);
__ lea(scratch1_,
Operand(map_result_, scratch1_, times_2, FixedArray::kHeaderSize));
// Calculate location of the first key name.
__ add(Operand(map_result_),
Immediate(FixedArray::kHeaderSize +
DescriptorArray::kFirstIndex * kPointerSize));
// Loop through all the keys in the descriptor array. If one of these is the
// symbol valueOf the result is false.
Label entry, loop;
__ jmp(&entry);
__ bind(&loop);
__ mov(scratch2_, FieldOperand(map_result_, 0));
__ cmp(scratch2_, Factory::value_of_symbol());
__ j(equal, &false_result);
__ add(Operand(map_result_), Immediate(kPointerSize));
__ bind(&entry);
__ cmp(map_result_, Operand(scratch1_));
__ j(not_equal, &loop);
// Reload map as register map_result_ was used as temporary above.
__ mov(map_result_, FieldOperand(object_, HeapObject::kMapOffset));
// If a valueOf property is not found on the object check that it's
// prototype is the un-modified String prototype. If not result is false.
__ mov(scratch1_, FieldOperand(map_result_, Map::kPrototypeOffset));
__ test(scratch1_, Immediate(kSmiTagMask));
__ j(zero, &false_result);
__ mov(scratch1_, FieldOperand(scratch1_, HeapObject::kMapOffset));
__ mov(scratch2_, Operand(esi, Context::SlotOffset(Context::GLOBAL_INDEX)));
__ mov(scratch2_,
FieldOperand(scratch2_, GlobalObject::kGlobalContextOffset));
__ cmp(scratch1_,
CodeGenerator::ContextOperand(
scratch2_, Context::STRING_FUNCTION_PROTOTYPE_MAP_INDEX));
__ j(not_equal, &false_result);
// Set the bit in the map to indicate that it has been checked safe for
// default valueOf and set true result.
__ or_(FieldOperand(map_result_, Map::kBitField2Offset),
Immediate(1 << Map::kStringWrapperSafeForDefaultValueOf));
__ Set(map_result_, Immediate(1));
__ jmp(exit_label());
__ bind(&false_result);
// Set false result.
__ Set(map_result_, Immediate(0));
}
private:
Register object_;
Register map_result_;
Register scratch1_;
Register scratch2_;
};
void CodeGenerator::GenerateIsStringWrapperSafeForDefaultValueOf(
ZoneList<Expression*>* args) {
ASSERT(args->length() == 1);
Load(args->at(0));
Result obj = frame_->Pop(); // Pop the string wrapper.
obj.ToRegister();
ASSERT(obj.is_valid());
if (FLAG_debug_code) {
__ AbortIfSmi(obj.reg());
}
// Check whether this map has already been checked to be safe for default
// valueOf.
Result map_result = allocator()->Allocate();
ASSERT(map_result.is_valid());
__ mov(map_result.reg(), FieldOperand(obj.reg(), HeapObject::kMapOffset));
__ test_b(FieldOperand(map_result.reg(), Map::kBitField2Offset),
1 << Map::kStringWrapperSafeForDefaultValueOf);
destination()->true_target()->Branch(not_zero);
// We need an additional two scratch registers for the deferred code.
Result temp1 = allocator()->Allocate();
ASSERT(temp1.is_valid());
Result temp2 = allocator()->Allocate();
ASSERT(temp2.is_valid());
DeferredIsStringWrapperSafeForDefaultValueOf* deferred =
new DeferredIsStringWrapperSafeForDefaultValueOf(
obj.reg(), map_result.reg(), temp1.reg(), temp2.reg());
deferred->Branch(zero);
deferred->BindExit();
__ test(map_result.reg(), Operand(map_result.reg()));
obj.Unuse();
map_result.Unuse();
temp1.Unuse();
temp2.Unuse();
destination()->Split(not_equal);
}
void CodeGenerator::GenerateIsFunction(ZoneList<Expression*>* args) {
// This generates a fast version of:
// (%_ClassOf(arg) === 'Function')

View File

@ -358,6 +358,10 @@ class CodeGenerator: public AstVisitor {
return FieldOperand(array, index_as_smi, times_half_pointer_size, offset);
}
static Operand ContextOperand(Register context, int index) {
return Operand(context, Context::SlotOffset(index));
}
private:
// Construction/Destruction
explicit CodeGenerator(MacroAssembler* masm);
@ -430,10 +434,6 @@ class CodeGenerator: public AstVisitor {
// The following are used by class Reference.
void LoadReference(Reference* ref);
static Operand ContextOperand(Register context, int index) {
return Operand(context, Context::SlotOffset(index));
}
Operand SlotOperand(Slot* slot, Register tmp);
Operand ContextSlotOperandCheckExtensions(Slot* slot,
@ -653,6 +653,8 @@ class CodeGenerator: public AstVisitor {
void GenerateIsSpecObject(ZoneList<Expression*>* args);
void GenerateIsFunction(ZoneList<Expression*>* args);
void GenerateIsUndetectableObject(ZoneList<Expression*>* args);
void GenerateIsStringWrapperSafeForDefaultValueOf(
ZoneList<Expression*>* args);
// Support for construct call checks.
void GenerateIsConstructCall(ZoneList<Expression*>* args);

View File

@ -2054,6 +2054,25 @@ void FullCodeGenerator::EmitIsUndetectableObject(ZoneList<Expression*>* args) {
}
void FullCodeGenerator::EmitIsStringWrapperSafeForDefaultValueOf(
ZoneList<Expression*>* args) {
ASSERT(args->length() == 1);
VisitForValue(args->at(0), kAccumulator);
Label materialize_true, materialize_false;
Label* if_true = NULL;
Label* if_false = NULL;
PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false);
// Just indicate false, as %_IsStringWrapperSafeForDefaultValueOf() is only
// used in a few functions in runtime.js which should not normally be hit by
// this compiler.
__ jmp(if_false);
Apply(context_, if_true, if_false);
}
void FullCodeGenerator::EmitIsFunction(ZoneList<Expression*>* args) {
ASSERT(args->length() == 1);

View File

@ -373,13 +373,13 @@ void MacroAssembler::AbortIfNotNumber(Register object) {
void MacroAssembler::AbortIfNotSmi(Register object) {
test(object, Immediate(kSmiTagMask));
Assert(equal, "Operand not a smi");
Assert(equal, "Operand is not a smi");
}
void MacroAssembler::AbortIfSmi(Register object) {
test(object, Immediate(kSmiTagMask));
Assert(not_equal, "Operand a smi");
Assert(not_equal, "Operand is a smi");
}

View File

@ -3207,6 +3207,7 @@ class Map: public HeapObject {
static const int kIsExtensible = 0;
static const int kFunctionWithPrototype = 1;
static const int kHasFastElements = 2;
static const int kStringWrapperSafeForDefaultValueOf = 3;
// Layout of the default cache. It holds alternating name and code objects.
static const int kCodeCacheEntrySize = 2;

View File

@ -175,7 +175,7 @@ function ADD(x) {
// Left operand (this) is already a string.
function STRING_ADD_LEFT(y) {
if (!IS_STRING(y)) {
if (IS_STRING_WRAPPER(y)) {
if (IS_STRING_WRAPPER(y) && %_IsStringWrapperSafeForDefaultValueOf(y)) {
y = %_ValueOf(y);
} else {
y = IS_NUMBER(y)
@ -191,7 +191,7 @@ function STRING_ADD_LEFT(y) {
function STRING_ADD_RIGHT(y) {
var x = this;
if (!IS_STRING(x)) {
if (IS_STRING_WRAPPER(x)) {
if (IS_STRING_WRAPPER(x) && %_IsStringWrapperSafeForDefaultValueOf(x)) {
x = %_ValueOf(x);
} else {
x = IS_NUMBER(x)

View File

@ -6040,6 +6040,143 @@ void CodeGenerator::GenerateIsSpecObject(ZoneList<Expression*>* args) {
}
// Deferred code to check whether the String JavaScript object is safe for using
// default value of. This code is called after the bit caching this information
// in the map has been checked with the map for the object in the map_result_
// register. On return the register map_result_ contains 1 for true and 0 for
// false.
class DeferredIsStringWrapperSafeForDefaultValueOf : public DeferredCode {
public:
DeferredIsStringWrapperSafeForDefaultValueOf(Register object,
Register map_result,
Register scratch1,
Register scratch2)
: object_(object),
map_result_(map_result),
scratch1_(scratch1),
scratch2_(scratch2) { }
virtual void Generate() {
Label false_result;
// Check that map is loaded as expected.
if (FLAG_debug_code) {
__ cmpq(map_result_, FieldOperand(object_, HeapObject::kMapOffset));
__ Assert(equal, "Map not in expected register");
}
// Check for fast case object. Generate false result for slow case object.
__ movq(scratch1_, FieldOperand(object_, JSObject::kPropertiesOffset));
__ movq(scratch1_, FieldOperand(scratch1_, HeapObject::kMapOffset));
__ CompareRoot(scratch1_, Heap::kHashTableMapRootIndex);
__ j(equal, &false_result);
// Look for valueOf symbol in the descriptor array, and indicate false if
// found. The type is not checked, so if it is a transition it is a false
// negative.
__ movq(map_result_,
FieldOperand(map_result_, Map::kInstanceDescriptorsOffset));
__ movq(scratch1_, FieldOperand(map_result_, FixedArray::kLengthOffset));
// map_result_: descriptor array
// scratch1_: length of descriptor array
// Calculate the end of the descriptor array.
SmiIndex index = masm_->SmiToIndex(scratch2_, scratch1_, kPointerSizeLog2);
__ lea(scratch1_,
Operand(
map_result_, index.reg, index.scale, FixedArray::kHeaderSize));
// Calculate location of the first key name.
__ addq(map_result_,
Immediate(FixedArray::kHeaderSize +
DescriptorArray::kFirstIndex * kPointerSize));
// Loop through all the keys in the descriptor array. If one of these is the
// symbol valueOf the result is false.
Label entry, loop;
__ jmp(&entry);
__ bind(&loop);
__ movq(scratch2_, FieldOperand(map_result_, 0));
__ Cmp(scratch2_, Factory::value_of_symbol());
__ j(equal, &false_result);
__ addq(map_result_, Immediate(kPointerSize));
__ bind(&entry);
__ cmpq(map_result_, scratch1_);
__ j(not_equal, &loop);
// Reload map as register map_result_ was used as temporary above.
__ movq(map_result_, FieldOperand(object_, HeapObject::kMapOffset));
// If a valueOf property is not found on the object check that it's
// prototype is the un-modified String prototype. If not result is false.
__ movq(scratch1_, FieldOperand(map_result_, Map::kPrototypeOffset));
__ testq(scratch1_, Immediate(kSmiTagMask));
__ j(zero, &false_result);
__ movq(scratch1_, FieldOperand(scratch1_, HeapObject::kMapOffset));
__ movq(scratch2_,
Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX)));
__ movq(scratch2_,
FieldOperand(scratch2_, GlobalObject::kGlobalContextOffset));
__ cmpq(scratch1_,
CodeGenerator::ContextOperand(
scratch2_, Context::STRING_FUNCTION_PROTOTYPE_MAP_INDEX));
__ j(not_equal, &false_result);
// Set the bit in the map to indicate that it has been checked safe for
// default valueOf and set true result.
__ or_(FieldOperand(map_result_, Map::kBitField2Offset),
Immediate(1 << Map::kStringWrapperSafeForDefaultValueOf));
__ Set(map_result_, 1);
__ jmp(exit_label());
__ bind(&false_result);
// Set false result.
__ Set(map_result_, 0);
}
private:
Register object_;
Register map_result_;
Register scratch1_;
Register scratch2_;
};
void CodeGenerator::GenerateIsStringWrapperSafeForDefaultValueOf(
ZoneList<Expression*>* args) {
ASSERT(args->length() == 1);
Load(args->at(0));
Result obj = frame_->Pop(); // Pop the string wrapper.
obj.ToRegister();
ASSERT(obj.is_valid());
if (FLAG_debug_code) {
__ AbortIfSmi(obj.reg());
}
// Check whether this map has already been checked to be safe for default
// valueOf.
Result map_result = allocator()->Allocate();
ASSERT(map_result.is_valid());
__ movq(map_result.reg(), FieldOperand(obj.reg(), HeapObject::kMapOffset));
__ testb(FieldOperand(map_result.reg(), Map::kBitField2Offset),
Immediate(1 << Map::kStringWrapperSafeForDefaultValueOf));
destination()->true_target()->Branch(not_zero);
// We need an additional two scratch registers for the deferred code.
Result temp1 = allocator()->Allocate();
ASSERT(temp1.is_valid());
Result temp2 = allocator()->Allocate();
ASSERT(temp2.is_valid());
DeferredIsStringWrapperSafeForDefaultValueOf* deferred =
new DeferredIsStringWrapperSafeForDefaultValueOf(
obj.reg(), map_result.reg(), temp1.reg(), temp2.reg());
deferred->Branch(zero);
deferred->BindExit();
__ testq(map_result.reg(), map_result.reg());
obj.Unuse();
map_result.Unuse();
temp1.Unuse();
temp2.Unuse();
destination()->Split(not_equal);
}
void CodeGenerator::GenerateIsFunction(ZoneList<Expression*>* args) {
// This generates a fast version of:
// (%_ClassOf(arg) === 'Function')

View File

@ -347,6 +347,10 @@ class CodeGenerator: public AstVisitor {
// expected arguments. Otherwise return -1.
static int InlineRuntimeCallArgumentsCount(Handle<String> name);
static Operand ContextOperand(Register context, int index) {
return Operand(context, Context::SlotOffset(index));
}
private:
// Construction/Destruction
explicit CodeGenerator(MacroAssembler* masm);
@ -406,10 +410,6 @@ class CodeGenerator: public AstVisitor {
void LoadReference(Reference* ref);
void UnloadReference(Reference* ref);
static Operand ContextOperand(Register context, int index) {
return Operand(context, Context::SlotOffset(index));
}
Operand SlotOperand(Slot* slot, Register tmp);
Operand ContextSlotOperandCheckExtensions(Slot* slot,
@ -611,6 +611,8 @@ class CodeGenerator: public AstVisitor {
void GenerateIsSpecObject(ZoneList<Expression*>* args);
void GenerateIsFunction(ZoneList<Expression*>* args);
void GenerateIsUndetectableObject(ZoneList<Expression*>* args);
void GenerateIsStringWrapperSafeForDefaultValueOf(
ZoneList<Expression*>* args);
// Support for construct call checks.
void GenerateIsConstructCall(ZoneList<Expression*>* args);

View File

@ -2059,6 +2059,25 @@ void FullCodeGenerator::EmitIsUndetectableObject(ZoneList<Expression*>* args) {
}
void FullCodeGenerator::EmitIsStringWrapperSafeForDefaultValueOf(
ZoneList<Expression*>* args) {
ASSERT(args->length() == 1);
VisitForValue(args->at(0), kAccumulator);
Label materialize_true, materialize_false;
Label* if_true = NULL;
Label* if_false = NULL;
PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false);
// Just indicate false, as %_IsStringWrapperSafeForDefaultValueOf() is only
// used in a few functions in runtime.js which should not normally be hit by
// this compiler.
__ jmp(if_false);
Apply(context_, if_true, if_false);
}
void FullCodeGenerator::EmitIsFunction(ZoneList<Expression*>* args) {
ASSERT(args->length() == 1);

View File

@ -1977,10 +1977,17 @@ void MacroAssembler::AbortIfNotNumber(Register object) {
}
void MacroAssembler::AbortIfSmi(Register object) {
Label ok;
Condition is_smi = CheckSmi(object);
Assert(NegateCondition(is_smi), "Operand is a smi");
}
void MacroAssembler::AbortIfNotSmi(Register object) {
Label ok;
Condition is_smi = CheckSmi(object);
Assert(is_smi, "Operand not a smi");
Assert(is_smi, "Operand is not a smi");
}

View File

@ -582,6 +582,9 @@ class MacroAssembler: public Assembler {
// Abort execution if argument is not a number. Used in debug code.
void AbortIfNotNumber(Register object);
// Abort execution if argument is a smi. Used in debug code.
void AbortIfSmi(Register object);
// Abort execution if argument is not a smi. Used in debug code.
void AbortIfNotSmi(Register object);

View File

@ -0,0 +1,49 @@
// Copyright 2010 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Check that when valueOf for a String object is overwritten it is called and
// the result used when that object is added with a string.
// See: http://code.google.com/p/v8/issues/detail?id=760
String.prototype.valueOf = function() { return 'y' };
function test() {
var o = Object('x');
assertEquals('y', o + '');
assertEquals('y', '' + o);
}
for (var i = 0; i < 10; i++) {
var o = Object('x');
assertEquals('y', o + '');
assertEquals('y', '' + o);
}
for (var i = 0; i < 10; i++) {
test()
}

View File

@ -0,0 +1,49 @@
// Copyright 2010 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Check that when valueOf for a String object is overwritten it is called and
// the result used when that object is added with a string.
// See: http://code.google.com/p/v8/issues/detail?id=760
function test() {
var o = Object('x');
o.valueOf = function() { return 'y' };
assertEquals('y', o + '');
assertEquals('y', '' + o);
}
for (var i = 0; i < 10; i++) {
var o = Object('x');
o.valueOf = function() { return 'y' };
assertEquals('y', o + '');
assertEquals('y', '' + o);
}
for (var i = 0; i < 10; i++) {
test()
}