Fix improved LoadICs for dictionaries with callbacks.

This fixes the positive lookup performed by these LoadICs, to use the
holder instead of the receiver to perfrom the lookup on. It also extends
this improvement to KeyedLoadICs. And it fixes a bug introduced for the
JavaScript getter case of a LoadIC.

R=erik.corry@gmail.com
BUG=chromium:142088
TEST=cctest/test-api/Regress142088,cctest/test-api/Regress137002b

Review URL: https://chromiumcodereview.appspot.com/10828303

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@12311 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 071f7fdf
...@@ -1238,8 +1238,12 @@ void StubCompiler::GenerateDictionaryLoadCallback(Register receiver, ...@@ -1238,8 +1238,12 @@ void StubCompiler::GenerateDictionaryLoadCallback(Register receiver,
Handle<AccessorInfo> callback, Handle<AccessorInfo> callback,
Handle<String> name, Handle<String> name,
Label* miss) { Label* miss) {
ASSERT(!receiver.is(scratch1));
ASSERT(!receiver.is(scratch2));
ASSERT(!receiver.is(scratch3));
// Load the properties dictionary.
Register dictionary = scratch1; Register dictionary = scratch1;
Register index = scratch2;
__ ldr(dictionary, FieldMemOperand(receiver, JSObject::kPropertiesOffset)); __ ldr(dictionary, FieldMemOperand(receiver, JSObject::kPropertiesOffset));
// Probe the dictionary. // Probe the dictionary.
...@@ -1249,19 +1253,18 @@ void StubCompiler::GenerateDictionaryLoadCallback(Register receiver, ...@@ -1249,19 +1253,18 @@ void StubCompiler::GenerateDictionaryLoadCallback(Register receiver,
&probe_done, &probe_done,
dictionary, dictionary,
name_reg, name_reg,
index, // Set if we hit. scratch2,
scratch3); scratch3);
__ bind(&probe_done); __ bind(&probe_done);
// If probing finds an entry in the dictionary, check that the value is the // If probing finds an entry in the dictionary, scratch3 contains the
// callback. // pointer into the dictionary. Check that the value is the callback.
const int kElementsStartOffset = Register pointer = scratch3;
StringDictionary::kHeaderSize + const int kElementsStartOffset = StringDictionary::kHeaderSize +
StringDictionary::kElementsStartIndex * kPointerSize; StringDictionary::kElementsStartIndex * kPointerSize;
const int kValueOffset = kElementsStartOffset + kPointerSize; const int kValueOffset = kElementsStartOffset + kPointerSize;
__ add(scratch1, dictionary, Operand(kValueOffset - kHeapObjectTag)); __ ldr(scratch2, FieldMemOperand(pointer, kValueOffset));
__ ldr(scratch3, MemOperand(scratch1, index, LSL, kPointerSizeLog2)); __ cmp(scratch2, Operand(callback));
__ cmp(scratch3, Operand(callback));
__ b(ne, miss); __ b(ne, miss);
} }
...@@ -1273,6 +1276,7 @@ void StubCompiler::GenerateLoadCallback(Handle<JSObject> object, ...@@ -1273,6 +1276,7 @@ void StubCompiler::GenerateLoadCallback(Handle<JSObject> object,
Register scratch1, Register scratch1,
Register scratch2, Register scratch2,
Register scratch3, Register scratch3,
Register scratch4,
Handle<AccessorInfo> callback, Handle<AccessorInfo> callback,
Handle<String> name, Handle<String> name,
Label* miss) { Label* miss) {
...@@ -1285,7 +1289,7 @@ void StubCompiler::GenerateLoadCallback(Handle<JSObject> object, ...@@ -1285,7 +1289,7 @@ void StubCompiler::GenerateLoadCallback(Handle<JSObject> object,
if (!holder->HasFastProperties() && !holder->IsJSGlobalObject()) { if (!holder->HasFastProperties() && !holder->IsJSGlobalObject()) {
GenerateDictionaryLoadCallback( GenerateDictionaryLoadCallback(
receiver, name_reg, scratch1, scratch2, scratch3, callback, name, miss); reg, name_reg, scratch2, scratch3, scratch4, callback, name, miss);
} }
// Build AccessorInfo::args_ list on the stack and push property name below // Build AccessorInfo::args_ list on the stack and push property name below
...@@ -2914,7 +2918,7 @@ Handle<Code> LoadStubCompiler::CompileLoadCallback( ...@@ -2914,7 +2918,7 @@ Handle<Code> LoadStubCompiler::CompileLoadCallback(
// -- lr : return address // -- lr : return address
// ----------------------------------- // -----------------------------------
Label miss; Label miss;
GenerateLoadCallback(object, holder, r0, r2, r3, r1, r4, callback, name, GenerateLoadCallback(object, holder, r0, r2, r3, r1, r4, r5, callback, name,
&miss); &miss);
__ bind(&miss); __ bind(&miss);
GenerateLoadMiss(masm(), Code::LOAD_IC); GenerateLoadMiss(masm(), Code::LOAD_IC);
...@@ -3085,7 +3089,7 @@ Handle<Code> KeyedLoadStubCompiler::CompileLoadCallback( ...@@ -3085,7 +3089,7 @@ Handle<Code> KeyedLoadStubCompiler::CompileLoadCallback(
__ cmp(r0, Operand(name)); __ cmp(r0, Operand(name));
__ b(ne, &miss); __ b(ne, &miss);
GenerateLoadCallback(receiver, holder, r1, r0, r2, r3, r4, callback, name, GenerateLoadCallback(receiver, holder, r1, r0, r2, r3, r4, r5, callback, name,
&miss); &miss);
__ bind(&miss); __ bind(&miss);
GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC);
......
...@@ -1060,18 +1060,31 @@ void StubCompiler::GenerateDictionaryLoadCallback(Register receiver, ...@@ -1060,18 +1060,31 @@ void StubCompiler::GenerateDictionaryLoadCallback(Register receiver,
Handle<AccessorInfo> callback, Handle<AccessorInfo> callback,
Handle<String> name, Handle<String> name,
Label* miss) { Label* miss) {
ASSERT(!receiver.is(scratch2));
ASSERT(!receiver.is(scratch3));
Register dictionary = scratch1; Register dictionary = scratch1;
bool must_preserve_dictionary_reg = receiver.is(dictionary);
// Load the properties dictionary.
if (must_preserve_dictionary_reg) {
__ push(dictionary);
}
__ mov(dictionary, FieldOperand(receiver, JSObject::kPropertiesOffset)); __ mov(dictionary, FieldOperand(receiver, JSObject::kPropertiesOffset));
// Probe the dictionary. // Probe the dictionary.
Label probe_done; Label probe_done, pop_and_miss;
StringDictionaryLookupStub::GeneratePositiveLookup(masm(), StringDictionaryLookupStub::GeneratePositiveLookup(masm(),
miss, &pop_and_miss,
&probe_done, &probe_done,
dictionary, dictionary,
name_reg, name_reg,
scratch2, scratch2,
scratch3); scratch3);
__ bind(&pop_and_miss);
if (must_preserve_dictionary_reg) {
__ pop(dictionary);
}
__ jmp(miss);
__ bind(&probe_done); __ bind(&probe_done);
// If probing finds an entry in the dictionary, scratch2 contains the // If probing finds an entry in the dictionary, scratch2 contains the
...@@ -1083,6 +1096,9 @@ void StubCompiler::GenerateDictionaryLoadCallback(Register receiver, ...@@ -1083,6 +1096,9 @@ void StubCompiler::GenerateDictionaryLoadCallback(Register receiver,
const int kValueOffset = kElementsStartOffset + kPointerSize; const int kValueOffset = kElementsStartOffset + kPointerSize;
__ mov(scratch3, __ mov(scratch3,
Operand(dictionary, index, times_4, kValueOffset - kHeapObjectTag)); Operand(dictionary, index, times_4, kValueOffset - kHeapObjectTag));
if (must_preserve_dictionary_reg) {
__ pop(dictionary);
}
__ cmp(scratch3, callback); __ cmp(scratch3, callback);
__ j(not_equal, miss); __ j(not_equal, miss);
} }
...@@ -1095,6 +1111,7 @@ void StubCompiler::GenerateLoadCallback(Handle<JSObject> object, ...@@ -1095,6 +1111,7 @@ void StubCompiler::GenerateLoadCallback(Handle<JSObject> object,
Register scratch1, Register scratch1,
Register scratch2, Register scratch2,
Register scratch3, Register scratch3,
Register scratch4,
Handle<AccessorInfo> callback, Handle<AccessorInfo> callback,
Handle<String> name, Handle<String> name,
Label* miss) { Label* miss) {
...@@ -1107,7 +1124,7 @@ void StubCompiler::GenerateLoadCallback(Handle<JSObject> object, ...@@ -1107,7 +1124,7 @@ void StubCompiler::GenerateLoadCallback(Handle<JSObject> object,
if (!holder->HasFastProperties() && !holder->IsJSGlobalObject()) { if (!holder->HasFastProperties() && !holder->IsJSGlobalObject()) {
GenerateDictionaryLoadCallback( GenerateDictionaryLoadCallback(
receiver, name_reg, scratch1, scratch2, scratch3, callback, name, miss); reg, name_reg, scratch1, scratch2, scratch3, callback, name, miss);
} }
// Insert additional parameters into the stack frame above return address. // Insert additional parameters into the stack frame above return address.
...@@ -2945,8 +2962,8 @@ Handle<Code> LoadStubCompiler::CompileLoadCallback( ...@@ -2945,8 +2962,8 @@ Handle<Code> LoadStubCompiler::CompileLoadCallback(
// ----------------------------------- // -----------------------------------
Label miss; Label miss;
GenerateLoadCallback(object, holder, edx, ecx, ebx, eax, edi, callback, GenerateLoadCallback(object, holder, edx, ecx, ebx, eax, edi, no_reg,
name, &miss); callback, name, &miss);
__ bind(&miss); __ bind(&miss);
GenerateLoadMiss(masm(), Code::LOAD_IC); GenerateLoadMiss(masm(), Code::LOAD_IC);
...@@ -3135,8 +3152,8 @@ Handle<Code> KeyedLoadStubCompiler::CompileLoadCallback( ...@@ -3135,8 +3152,8 @@ Handle<Code> KeyedLoadStubCompiler::CompileLoadCallback(
__ cmp(ecx, Immediate(name)); __ cmp(ecx, Immediate(name));
__ j(not_equal, &miss); __ j(not_equal, &miss);
GenerateLoadCallback(receiver, holder, edx, ecx, ebx, eax, edi, callback, GenerateLoadCallback(receiver, holder, edx, ecx, ebx, eax, edi, no_reg,
name, &miss); callback, name, &miss);
__ bind(&miss); __ bind(&miss);
__ DecrementCounter(counters->keyed_load_callback(), 1); __ DecrementCounter(counters->keyed_load_callback(), 1);
......
...@@ -996,6 +996,7 @@ void LoadIC::UpdateCaches(LookupResult* lookup, ...@@ -996,6 +996,7 @@ void LoadIC::UpdateCaches(LookupResult* lookup,
Handle<Object> getter(Handle<AccessorPair>::cast(callback)->getter()); Handle<Object> getter(Handle<AccessorPair>::cast(callback)->getter());
if (!getter->IsJSFunction()) return; if (!getter->IsJSFunction()) return;
if (holder->IsGlobalObject()) return; if (holder->IsGlobalObject()) return;
if (!holder->HasFastProperties()) return;
code = isolate()->stub_cache()->ComputeLoadViaGetter( code = isolate()->stub_cache()->ComputeLoadViaGetter(
name, receiver, holder, Handle<JSFunction>::cast(getter)); name, receiver, holder, Handle<JSFunction>::cast(getter));
} else { } else {
...@@ -1264,7 +1265,6 @@ void KeyedLoadIC::UpdateCaches(LookupResult* lookup, ...@@ -1264,7 +1265,6 @@ void KeyedLoadIC::UpdateCaches(LookupResult* lookup,
Handle<AccessorInfo> callback = Handle<AccessorInfo> callback =
Handle<AccessorInfo>::cast(callback_object); Handle<AccessorInfo>::cast(callback_object);
if (v8::ToCData<Address>(callback->getter()) == 0) return; if (v8::ToCData<Address>(callback->getter()) == 0) return;
if (!holder->HasFastProperties()) return;
if (!callback->IsCompatibleReceiver(*receiver)) return; if (!callback->IsCompatibleReceiver(*receiver)) return;
code = isolate()->stub_cache()->ComputeKeyedLoadCallback( code = isolate()->stub_cache()->ComputeKeyedLoadCallback(
name, receiver, holder, callback); name, receiver, holder, callback);
......
...@@ -550,6 +550,7 @@ class StubCompiler BASE_EMBEDDED { ...@@ -550,6 +550,7 @@ class StubCompiler BASE_EMBEDDED {
Register scratch1, Register scratch1,
Register scratch2, Register scratch2,
Register scratch3, Register scratch3,
Register scratch4,
Handle<AccessorInfo> callback, Handle<AccessorInfo> callback,
Handle<String> name, Handle<String> name,
Label* miss); Label* miss);
......
...@@ -135,7 +135,7 @@ static void GenerateDictionaryLoad(MacroAssembler* masm, ...@@ -135,7 +135,7 @@ static void GenerateDictionaryLoad(MacroAssembler* masm,
r0, r0,
r1); r1);
// If probing finds an entry in the dictionary, r0 contains the // If probing finds an entry in the dictionary, r1 contains the
// index into the dictionary. Check that the value is a normal // index into the dictionary. Check that the value is a normal
// property. // property.
__ bind(&done); __ bind(&done);
...@@ -178,10 +178,9 @@ static void GenerateDictionaryStore(MacroAssembler* masm, ...@@ -178,10 +178,9 @@ static void GenerateDictionaryStore(MacroAssembler* masm,
// //
// value - holds the value to store and is unchanged. // value - holds the value to store and is unchanged.
// //
// scratch0 - used for index into the property dictionary and is clobbered. // scratch0 - used during the positive dictionary lookup and is clobbered.
// //
// scratch1 - used to hold the capacity of the property dictionary and is // scratch1 - used for index into the property dictionary and is clobbered.
// clobbered.
Label done; Label done;
// Probe the dictionary. // Probe the dictionary.
......
...@@ -1037,6 +1037,11 @@ void StubCompiler::GenerateDictionaryLoadCallback(Register receiver, ...@@ -1037,6 +1037,11 @@ void StubCompiler::GenerateDictionaryLoadCallback(Register receiver,
Handle<AccessorInfo> callback, Handle<AccessorInfo> callback,
Handle<String> name, Handle<String> name,
Label* miss) { Label* miss) {
ASSERT(!receiver.is(scratch1));
ASSERT(!receiver.is(scratch2));
ASSERT(!receiver.is(scratch3));
// Load the properties dictionary.
Register dictionary = scratch1; Register dictionary = scratch1;
__ movq(dictionary, FieldOperand(receiver, JSObject::kPropertiesOffset)); __ movq(dictionary, FieldOperand(receiver, JSObject::kPropertiesOffset));
...@@ -1051,17 +1056,18 @@ void StubCompiler::GenerateDictionaryLoadCallback(Register receiver, ...@@ -1051,17 +1056,18 @@ void StubCompiler::GenerateDictionaryLoadCallback(Register receiver,
scratch3); scratch3);
__ bind(&probe_done); __ bind(&probe_done);
// If probing finds an entry in the dictionary, scratch2 contains the // If probing finds an entry in the dictionary, scratch3 contains the
// index into the dictionary. Check that the value is the callback. // index into the dictionary. Check that the value is the callback.
Register index = scratch2; Register index = scratch3;
const int kElementsStartOffset = const int kElementsStartOffset =
StringDictionary::kHeaderSize + StringDictionary::kHeaderSize +
StringDictionary::kElementsStartIndex * kPointerSize; StringDictionary::kElementsStartIndex * kPointerSize;
const int kValueOffset = kElementsStartOffset + kPointerSize; const int kValueOffset = kElementsStartOffset + kPointerSize;
__ movq(scratch3, __ movq(scratch2,
Operand(dictionary, index, times_8, kValueOffset - kHeapObjectTag)); Operand(dictionary, index, times_pointer_size,
__ movq(scratch2, callback, RelocInfo::EMBEDDED_OBJECT); kValueOffset - kHeapObjectTag));
__ cmpq(scratch3, scratch2); __ movq(scratch3, callback, RelocInfo::EMBEDDED_OBJECT);
__ cmpq(scratch2, scratch3);
__ j(not_equal, miss); __ j(not_equal, miss);
} }
...@@ -1073,6 +1079,7 @@ void StubCompiler::GenerateLoadCallback(Handle<JSObject> object, ...@@ -1073,6 +1079,7 @@ void StubCompiler::GenerateLoadCallback(Handle<JSObject> object,
Register scratch1, Register scratch1,
Register scratch2, Register scratch2,
Register scratch3, Register scratch3,
Register scratch4,
Handle<AccessorInfo> callback, Handle<AccessorInfo> callback,
Handle<String> name, Handle<String> name,
Label* miss) { Label* miss) {
...@@ -1085,7 +1092,7 @@ void StubCompiler::GenerateLoadCallback(Handle<JSObject> object, ...@@ -1085,7 +1092,7 @@ void StubCompiler::GenerateLoadCallback(Handle<JSObject> object,
if (!holder->HasFastProperties() && !holder->IsJSGlobalObject()) { if (!holder->HasFastProperties() && !holder->IsJSGlobalObject()) {
GenerateDictionaryLoadCallback( GenerateDictionaryLoadCallback(
receiver, name_reg, scratch1, scratch2, scratch3, callback, name, miss); reg, name_reg, scratch2, scratch3, scratch4, callback, name, miss);
} }
// Insert additional parameters into the stack frame above return address. // Insert additional parameters into the stack frame above return address.
...@@ -2781,7 +2788,7 @@ Handle<Code> LoadStubCompiler::CompileLoadCallback( ...@@ -2781,7 +2788,7 @@ Handle<Code> LoadStubCompiler::CompileLoadCallback(
// -- rsp[0] : return address // -- rsp[0] : return address
// ----------------------------------- // -----------------------------------
Label miss; Label miss;
GenerateLoadCallback(object, holder, rax, rcx, rdx, rbx, rdi, callback, GenerateLoadCallback(object, holder, rax, rcx, rdx, rbx, rdi, r8, callback,
name, &miss); name, &miss);
__ bind(&miss); __ bind(&miss);
GenerateLoadMiss(masm(), Code::LOAD_IC); GenerateLoadMiss(masm(), Code::LOAD_IC);
...@@ -2964,7 +2971,7 @@ Handle<Code> KeyedLoadStubCompiler::CompileLoadCallback( ...@@ -2964,7 +2971,7 @@ Handle<Code> KeyedLoadStubCompiler::CompileLoadCallback(
__ Cmp(rax, name); __ Cmp(rax, name);
__ j(not_equal, &miss); __ j(not_equal, &miss);
GenerateLoadCallback(receiver, holder, rdx, rax, rbx, rcx, rdi, callback, GenerateLoadCallback(receiver, holder, rdx, rax, rbx, rcx, rdi, r8, callback,
name, &miss); name, &miss);
__ bind(&miss); __ bind(&miss);
__ DecrementCounter(counters->keyed_load_callback(), 1); __ DecrementCounter(counters->keyed_load_callback(), 1);
......
...@@ -14726,6 +14726,8 @@ THREADED_TEST(FunctionGetScriptId) { ...@@ -14726,6 +14726,8 @@ THREADED_TEST(FunctionGetScriptId) {
static v8::Handle<Value> GetterWhichReturns42(Local<String> name, static v8::Handle<Value> GetterWhichReturns42(Local<String> name,
const AccessorInfo& info) { const AccessorInfo& info) {
CHECK(v8::Utils::OpenHandle(*info.This())->IsJSObject());
CHECK(v8::Utils::OpenHandle(*info.Holder())->IsJSObject());
return v8_num(42); return v8_num(42);
} }
...@@ -14733,12 +14735,16 @@ static v8::Handle<Value> GetterWhichReturns42(Local<String> name, ...@@ -14733,12 +14735,16 @@ static v8::Handle<Value> GetterWhichReturns42(Local<String> name,
static void SetterWhichSetsYOnThisTo23(Local<String> name, static void SetterWhichSetsYOnThisTo23(Local<String> name,
Local<Value> value, Local<Value> value,
const AccessorInfo& info) { const AccessorInfo& info) {
CHECK(v8::Utils::OpenHandle(*info.This())->IsJSObject());
CHECK(v8::Utils::OpenHandle(*info.Holder())->IsJSObject());
info.This()->Set(v8_str("y"), v8_num(23)); info.This()->Set(v8_str("y"), v8_num(23));
} }
Handle<Value> FooGetInterceptor(Local<String> name, Handle<Value> FooGetInterceptor(Local<String> name,
const AccessorInfo& info) { const AccessorInfo& info) {
CHECK(v8::Utils::OpenHandle(*info.This())->IsJSObject());
CHECK(v8::Utils::OpenHandle(*info.Holder())->IsJSObject());
if (!name->Equals(v8_str("foo"))) return Handle<Value>(); if (!name->Equals(v8_str("foo"))) return Handle<Value>();
return v8_num(42); return v8_num(42);
} }
...@@ -14747,6 +14753,8 @@ Handle<Value> FooGetInterceptor(Local<String> name, ...@@ -14747,6 +14753,8 @@ Handle<Value> FooGetInterceptor(Local<String> name,
Handle<Value> FooSetInterceptor(Local<String> name, Handle<Value> FooSetInterceptor(Local<String> name,
Local<Value> value, Local<Value> value,
const AccessorInfo& info) { const AccessorInfo& info) {
CHECK(v8::Utils::OpenHandle(*info.This())->IsJSObject());
CHECK(v8::Utils::OpenHandle(*info.Holder())->IsJSObject());
if (!name->Equals(v8_str("foo"))) return Handle<Value>(); if (!name->Equals(v8_str("foo"))) return Handle<Value>();
info.This()->Set(v8_str("y"), v8_num(23)); info.This()->Set(v8_str("y"), v8_num(23));
return v8_num(23); return v8_num(23);
...@@ -17068,19 +17076,30 @@ THREADED_TEST(Regress137002b) { ...@@ -17068,19 +17076,30 @@ THREADED_TEST(Regress137002b) {
"function store2(x) { void 0; x.foo = void 0; }" "function store2(x) { void 0; x.foo = void 0; }"
"function keyed_load2(x, key) { void 0; return x[key]; }" "function keyed_load2(x, key) { void 0; return x[key]; }"
"obj.y = void 0;"
"obj.__proto__ = null;" "obj.__proto__ = null;"
"var subobj = {};" "var subobj = {};"
"subobj.y = void 0;"
"subobj.__proto__ = obj;" "subobj.__proto__ = obj;"
"%OptimizeObjectForAddingMultipleProperties(obj, 1);" "%OptimizeObjectForAddingMultipleProperties(obj, 1);"
// Make the ICs monomorphic. // Make the ICs monomorphic.
"load(obj); load(obj);" "load(obj); load(obj);"
"load2(subobj); load2(subobj);" "load2(subobj); load2(subobj);"
"store(obj);" "store(obj); store(obj);"
"store2(subobj);" "store2(subobj); store2(subobj);"
"keyed_load(obj, 'foo'); keyed_load(obj, 'foo');" "keyed_load(obj, 'foo'); keyed_load(obj, 'foo');"
"keyed_load2(subobj, 'foo'); keyed_load2(subobj, 'foo');" "keyed_load2(subobj, 'foo'); keyed_load2(subobj, 'foo');"
// Actually test the shiny new ICs and better not crash. This
// serves as a regression test for issue 142088 as well.
"load(obj);"
"load2(subobj);"
"store(obj);"
"store2(subobj);"
"keyed_load(obj, 'foo');"
"keyed_load2(subobj, 'foo');"
// Delete the accessor. It better not be called any more now. // Delete the accessor. It better not be called any more now.
"delete obj.foo;" "delete obj.foo;"
"obj.y = void 0;" "obj.y = void 0;"
...@@ -17103,6 +17122,23 @@ THREADED_TEST(Regress137002b) { ...@@ -17103,6 +17122,23 @@ THREADED_TEST(Regress137002b) {
} }
THREADED_TEST(Regress142088) {
i::FLAG_allow_natives_syntax = true;
v8::HandleScope scope;
LocalContext context;
Local<ObjectTemplate> templ = ObjectTemplate::New();
templ->SetAccessor(v8_str("foo"),
GetterWhichReturns42,
SetterWhichSetsYOnThisTo23);
context->Global()->Set(v8_str("obj"), templ->NewInstance());
CompileRun("function load(x) { return x.foo; }"
"var o = Object.create(obj);"
"%OptimizeObjectForAddingMultipleProperties(obj, 1);"
"load(o); load(o); load(o); load(o);");
}
THREADED_TEST(Regress137496) { THREADED_TEST(Regress137496) {
i::FLAG_expose_gc = true; i::FLAG_expose_gc = true;
v8::HandleScope scope; v8::HandleScope scope;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment