Fix slack tracking when instance prototype changes.
This fixes a corner case when the instance prototype of a function is changed while inobject slack tracking is still in progress. This caused the intial map to be unrelated for functions with the same shared info and hence the shared construct stub is no longer generic enough to work for all those functions. R=danno@chromium.org BUG=chromium:157019 TEST=mjsunit/regress/regress-crbug-157019 Review URL: https://codereview.chromium.org/11293059 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@12896 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
bbcfa17134
commit
a31889e2de
@ -3467,7 +3467,13 @@ Handle<Code> ConstructStubCompiler::CompileConstructStub(
|
||||
// r1: constructor function
|
||||
// r2: initial map
|
||||
// r7: undefined
|
||||
ASSERT(function->has_initial_map());
|
||||
__ ldrb(r3, FieldMemOperand(r2, Map::kInstanceSizeOffset));
|
||||
#ifdef DEBUG
|
||||
int instance_size = function->initial_map()->instance_size();
|
||||
__ cmp(r3, Operand(instance_size >> kPointerSizeLog2));
|
||||
__ Check(eq, "Instance size of initial map changed.");
|
||||
#endif
|
||||
__ AllocateInNewSpace(r3, r4, r5, r6, &generic_stub_call, SIZE_IN_WORDS);
|
||||
|
||||
// Allocated the JSObject, now initialize the fields. Map is set to initial
|
||||
@ -3525,7 +3531,6 @@ Handle<Code> ConstructStubCompiler::CompileConstructStub(
|
||||
}
|
||||
|
||||
// Fill the unused in-object property fields with undefined.
|
||||
ASSERT(function->has_initial_map());
|
||||
for (int i = shared->this_property_assignments_count();
|
||||
i < function->initial_map()->inobject_properties();
|
||||
i++) {
|
||||
|
@ -3421,6 +3421,7 @@ Handle<Code> ConstructStubCompiler::CompileConstructStub(
|
||||
#endif
|
||||
|
||||
// Load the initial map and verify that it is in fact a map.
|
||||
// edi: constructor
|
||||
__ mov(ebx, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset));
|
||||
// Will both indicate a NULL and a Smi.
|
||||
__ JumpIfSmi(ebx, &generic_stub_call);
|
||||
@ -3429,19 +3430,23 @@ Handle<Code> ConstructStubCompiler::CompileConstructStub(
|
||||
|
||||
#ifdef DEBUG
|
||||
// Cannot construct functions this way.
|
||||
// edi: constructor
|
||||
// ebx: initial map
|
||||
__ CmpInstanceType(ebx, JS_FUNCTION_TYPE);
|
||||
__ Assert(not_equal, "Function constructed by construct stub.");
|
||||
__ Check(not_equal, "Function constructed by construct stub.");
|
||||
#endif
|
||||
|
||||
// Now allocate the JSObject on the heap by moving the new space allocation
|
||||
// top forward.
|
||||
// edi: constructor
|
||||
// ebx: initial map
|
||||
ASSERT(function->has_initial_map());
|
||||
int instance_size = function->initial_map()->instance_size();
|
||||
#ifdef DEBUG
|
||||
__ movzx_b(ecx, FieldOperand(ebx, Map::kInstanceSizeOffset));
|
||||
__ shl(ecx, kPointerSizeLog2);
|
||||
__ AllocateInNewSpace(ecx, edx, ecx, no_reg,
|
||||
__ cmp(ecx, Immediate(instance_size));
|
||||
__ Check(equal, "Instance size of initial map changed.");
|
||||
#endif
|
||||
__ AllocateInNewSpace(instance_size, edx, ecx, no_reg,
|
||||
&generic_stub_call, NO_ALLOCATION_FLAGS);
|
||||
|
||||
// Allocated the JSObject, now initialize the fields and add the heap tag.
|
||||
@ -3501,7 +3506,6 @@ Handle<Code> ConstructStubCompiler::CompileConstructStub(
|
||||
}
|
||||
|
||||
// Fill the unused in-object property fields with undefined.
|
||||
ASSERT(function->has_initial_map());
|
||||
for (int i = shared->this_property_assignments_count();
|
||||
i < function->initial_map()->inobject_properties();
|
||||
i++) {
|
||||
|
@ -3453,7 +3453,7 @@ Handle<Code> ConstructStubCompiler::CompileConstructStub(
|
||||
// t7: undefined
|
||||
__ lbu(a3, FieldMemOperand(a2, Map::kInstanceTypeOffset));
|
||||
__ Check(ne, "Function constructed by construct stub.",
|
||||
a3, Operand(JS_FUNCTION_TYPE));
|
||||
a3, Operand(JS_FUNCTION_TYPE));
|
||||
#endif
|
||||
|
||||
// Now allocate the JSObject in new space.
|
||||
@ -3461,7 +3461,13 @@ Handle<Code> ConstructStubCompiler::CompileConstructStub(
|
||||
// a1: constructor function
|
||||
// a2: initial map
|
||||
// t7: undefined
|
||||
ASSERT(function->has_initial_map());
|
||||
__ lbu(a3, FieldMemOperand(a2, Map::kInstanceSizeOffset));
|
||||
#ifdef DEBUG
|
||||
int instance_size = function->initial_map()->instance_size();
|
||||
__ Check(eq, "Instance size of initial map changed.",
|
||||
a3, Operand(instance_size >> kPointerSizeLog2));
|
||||
#endif
|
||||
__ AllocateInNewSpace(a3, t4, t5, t6, &generic_stub_call, SIZE_IN_WORDS);
|
||||
|
||||
// Allocated the JSObject, now initialize the fields. Map is set to initial
|
||||
@ -3524,7 +3530,6 @@ Handle<Code> ConstructStubCompiler::CompileConstructStub(
|
||||
}
|
||||
|
||||
// Fill the unused in-object property fields with undefined.
|
||||
ASSERT(function->has_initial_map());
|
||||
for (int i = shared->this_property_assignments_count();
|
||||
i < function->initial_map()->inobject_properties();
|
||||
i++) {
|
||||
|
@ -4436,42 +4436,6 @@ void JSFunction::set_initial_map(Map* value) {
|
||||
}
|
||||
|
||||
|
||||
MaybeObject* JSFunction::set_initial_map_and_cache_transitions(
|
||||
Map* initial_map) {
|
||||
Context* native_context = context()->native_context();
|
||||
Object* array_function =
|
||||
native_context->get(Context::ARRAY_FUNCTION_INDEX);
|
||||
if (array_function->IsJSFunction() &&
|
||||
this == JSFunction::cast(array_function)) {
|
||||
// Replace all of the cached initial array maps in the native context with
|
||||
// the appropriate transitioned elements kind maps.
|
||||
Heap* heap = GetHeap();
|
||||
MaybeObject* maybe_maps =
|
||||
heap->AllocateFixedArrayWithHoles(kElementsKindCount);
|
||||
FixedArray* maps;
|
||||
if (!maybe_maps->To(&maps)) return maybe_maps;
|
||||
|
||||
Map* current_map = initial_map;
|
||||
ElementsKind kind = current_map->elements_kind();
|
||||
ASSERT(kind == GetInitialFastElementsKind());
|
||||
maps->set(kind, current_map);
|
||||
for (int i = GetSequenceIndexFromFastElementsKind(kind) + 1;
|
||||
i < kFastElementsKindCount; ++i) {
|
||||
Map* new_map;
|
||||
ElementsKind next_kind = GetFastElementsKindFromSequenceIndex(i);
|
||||
MaybeObject* maybe_new_map =
|
||||
current_map->CopyAsElementsKind(next_kind, INSERT_TRANSITION);
|
||||
if (!maybe_new_map->To(&new_map)) return maybe_new_map;
|
||||
maps->set(next_kind, new_map);
|
||||
current_map = new_map;
|
||||
}
|
||||
native_context->set_js_array_maps(maps);
|
||||
}
|
||||
set_initial_map(initial_map);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
bool JSFunction::has_initial_map() {
|
||||
return prototype_or_initial_map()->IsMap();
|
||||
}
|
||||
|
@ -7854,6 +7854,35 @@ MaybeObject* JSObject::OptimizeAsPrototype() {
|
||||
}
|
||||
|
||||
|
||||
MUST_USE_RESULT static MaybeObject* CacheInitialJSArrayMaps(
|
||||
Context* native_context, Map* initial_map) {
|
||||
// Replace all of the cached initial array maps in the native context with
|
||||
// the appropriate transitioned elements kind maps.
|
||||
Heap* heap = native_context->GetHeap();
|
||||
MaybeObject* maybe_maps =
|
||||
heap->AllocateFixedArrayWithHoles(kElementsKindCount);
|
||||
FixedArray* maps;
|
||||
if (!maybe_maps->To(&maps)) return maybe_maps;
|
||||
|
||||
Map* current_map = initial_map;
|
||||
ElementsKind kind = current_map->elements_kind();
|
||||
ASSERT(kind == GetInitialFastElementsKind());
|
||||
maps->set(kind, current_map);
|
||||
for (int i = GetSequenceIndexFromFastElementsKind(kind) + 1;
|
||||
i < kFastElementsKindCount; ++i) {
|
||||
Map* new_map;
|
||||
ElementsKind next_kind = GetFastElementsKindFromSequenceIndex(i);
|
||||
MaybeObject* maybe_new_map =
|
||||
current_map->CopyAsElementsKind(next_kind, INSERT_TRANSITION);
|
||||
if (!maybe_new_map->To(&new_map)) return maybe_new_map;
|
||||
maps->set(next_kind, new_map);
|
||||
current_map = new_map;
|
||||
}
|
||||
native_context->set_js_array_maps(maps);
|
||||
return initial_map;
|
||||
}
|
||||
|
||||
|
||||
MaybeObject* JSFunction::SetInstancePrototype(Object* value) {
|
||||
ASSERT(value->IsJSReceiver());
|
||||
Heap* heap = GetHeap();
|
||||
@ -7868,14 +7897,29 @@ MaybeObject* JSFunction::SetInstancePrototype(Object* value) {
|
||||
// Now some logic for the maps of the objects that are created by using this
|
||||
// function as a constructor.
|
||||
if (has_initial_map()) {
|
||||
// If the function has allocated the initial map
|
||||
// replace it with a copy containing the new prototype.
|
||||
// If the function has allocated the initial map replace it with a
|
||||
// copy containing the new prototype. Also complete any in-object
|
||||
// slack tracking that is in progress at this point because it is
|
||||
// still tracking the old copy.
|
||||
if (shared()->IsInobjectSlackTrackingInProgress()) {
|
||||
shared()->CompleteInobjectSlackTracking();
|
||||
}
|
||||
Map* new_map;
|
||||
MaybeObject* maybe_new_map = initial_map()->Copy();
|
||||
if (!maybe_new_map->To(&new_map)) return maybe_new_map;
|
||||
MaybeObject* maybe_object = initial_map()->Copy();
|
||||
if (!maybe_object->To(&new_map)) return maybe_object;
|
||||
new_map->set_prototype(value);
|
||||
MaybeObject* maybe_object = set_initial_map_and_cache_transitions(new_map);
|
||||
if (maybe_object->IsFailure()) return maybe_object;
|
||||
|
||||
// If the function is used as the global Array function, cache the
|
||||
// initial map (and transitioned versions) in the native context.
|
||||
Context* native_context = context()->native_context();
|
||||
Object* array_function = native_context->get(Context::ARRAY_FUNCTION_INDEX);
|
||||
if (array_function->IsJSFunction() &&
|
||||
this == JSFunction::cast(array_function)) {
|
||||
MaybeObject* ok = CacheInitialJSArrayMaps(native_context, new_map);
|
||||
if (ok->IsFailure()) return ok;
|
||||
}
|
||||
|
||||
set_initial_map(new_map);
|
||||
} else {
|
||||
// Put the value in the initial map field until an initial map is
|
||||
// needed. At that point, a new initial map is created and the
|
||||
|
@ -6132,8 +6132,6 @@ class JSFunction: public JSObject {
|
||||
// The initial map for an object created by this constructor.
|
||||
inline Map* initial_map();
|
||||
inline void set_initial_map(Map* value);
|
||||
MUST_USE_RESULT inline MaybeObject* set_initial_map_and_cache_transitions(
|
||||
Map* value);
|
||||
inline bool has_initial_map();
|
||||
|
||||
// Get and set the prototype property on a JSFunction. If the
|
||||
|
@ -3240,6 +3240,7 @@ Handle<Code> ConstructStubCompiler::CompileConstructStub(
|
||||
#endif
|
||||
|
||||
// Load the initial map and verify that it is in fact a map.
|
||||
// rdi: constructor
|
||||
__ movq(rbx, FieldOperand(rdi, JSFunction::kPrototypeOrInitialMapOffset));
|
||||
// Will both indicate a NULL and a Smi.
|
||||
STATIC_ASSERT(kSmiTag == 0);
|
||||
@ -3249,18 +3250,22 @@ Handle<Code> ConstructStubCompiler::CompileConstructStub(
|
||||
|
||||
#ifdef DEBUG
|
||||
// Cannot construct functions this way.
|
||||
// rdi: constructor
|
||||
// rbx: initial map
|
||||
__ CmpInstanceType(rbx, JS_FUNCTION_TYPE);
|
||||
__ Assert(not_equal, "Function constructed by construct stub.");
|
||||
__ Check(not_equal, "Function constructed by construct stub.");
|
||||
#endif
|
||||
|
||||
// Now allocate the JSObject in new space.
|
||||
// rdi: constructor
|
||||
// rbx: initial map
|
||||
ASSERT(function->has_initial_map());
|
||||
int instance_size = function->initial_map()->instance_size();
|
||||
#ifdef DEBUG
|
||||
__ movzxbq(rcx, FieldOperand(rbx, Map::kInstanceSizeOffset));
|
||||
__ shl(rcx, Immediate(kPointerSizeLog2));
|
||||
__ AllocateInNewSpace(rcx, rdx, rcx, no_reg,
|
||||
__ cmpq(rcx, Immediate(instance_size));
|
||||
__ Check(equal, "Instance size of initial map changed.");
|
||||
#endif
|
||||
__ AllocateInNewSpace(instance_size, rdx, rcx, no_reg,
|
||||
&generic_stub_call, NO_ALLOCATION_FLAGS);
|
||||
|
||||
// Allocated the JSObject, now initialize the fields and add the heap tag.
|
||||
@ -3306,7 +3311,6 @@ Handle<Code> ConstructStubCompiler::CompileConstructStub(
|
||||
}
|
||||
|
||||
// Fill the unused in-object property fields with undefined.
|
||||
ASSERT(function->has_initial_map());
|
||||
for (int i = shared->this_property_assignments_count();
|
||||
i < function->initial_map()->inobject_properties();
|
||||
i++) {
|
||||
|
54
test/mjsunit/regress/regress-crbug-157019.js
Normal file
54
test/mjsunit/regress/regress-crbug-157019.js
Normal file
@ -0,0 +1,54 @@
|
||||
// Copyright 2012 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.
|
||||
|
||||
// Flags: --allow-natives-syntax --nocrankshaft
|
||||
|
||||
function makeConstructor() {
|
||||
return function() {
|
||||
this.a = 1;
|
||||
this.b = 2;
|
||||
};
|
||||
}
|
||||
|
||||
var c1 = makeConstructor();
|
||||
var o1 = new c1();
|
||||
|
||||
c1.prototype = {};
|
||||
|
||||
for (var i = 0; i < 10; i++) {
|
||||
var o = new c1();
|
||||
for (var j = 0; j < 8; j++) {
|
||||
o["x" + j] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
var c2 = makeConstructor();
|
||||
var o2 = new c2();
|
||||
|
||||
for (var i = 0; i < 50000; i++) {
|
||||
new c2();
|
||||
}
|
Loading…
Reference in New Issue
Block a user