Preserve constant function transition when adding the same function.

This should help in cases like:
function Constructor() {
  this.foo = constFunction;
  this.bar = "baz";
}

for (...) {
  o = new Constructor();
  // Constant call IC will work.
  o.foo();
  // Inlined property load will see the same map.
  use(o.bar);
}

This change also fixes a latent bug in custom call IC-s for strings
exposed by string-charcodeat.js.

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5254 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
vitalyr@chromium.org 2010-08-12 14:51:59 +00:00
parent 5cd4a9b78d
commit 421db370d9
6 changed files with 54 additions and 18 deletions

View File

@ -1571,6 +1571,9 @@ Object* CallStubCompiler::CompileStringCharCodeAtCall(Object* object,
// -- esp[(argc + 1) * 4] : receiver
// -----------------------------------
// If object is not a string, bail out to regular call.
if (!object->IsString()) return Heap::undefined_value();
const int argc = arguments().immediate();
Label miss;
@ -1581,6 +1584,7 @@ Object* CallStubCompiler::CompileStringCharCodeAtCall(Object* object,
GenerateDirectLoadGlobalFunctionPrototype(masm(),
Context::STRING_FUNCTION_INDEX,
eax);
ASSERT(object != holder);
CheckPrototypes(JSObject::cast(object->GetPrototype()), eax, holder,
ebx, edx, edi, name, &miss);
@ -1635,6 +1639,9 @@ Object* CallStubCompiler::CompileStringCharAtCall(Object* object,
// -- esp[(argc + 1) * 4] : receiver
// -----------------------------------
// If object is not a string, bail out to regular call.
if (!object->IsString()) return Heap::undefined_value();
const int argc = arguments().immediate();
Label miss;
@ -1646,6 +1653,7 @@ Object* CallStubCompiler::CompileStringCharAtCall(Object* object,
GenerateDirectLoadGlobalFunctionPrototype(masm(),
Context::STRING_FUNCTION_INDEX,
eax);
ASSERT(object != holder);
CheckPrototypes(JSObject::cast(object->GetPrototype()), eax, holder,
ebx, edx, edi, name, &miss);

View File

@ -554,10 +554,11 @@ void MarkCompactCollector::MarkDescriptorArray(
ASSERT(contents->IsFixedArray());
ASSERT(contents->length() >= 2);
SetMark(contents);
// Contents contains (value, details) pairs. If the details say
// that the type of descriptor is MAP_TRANSITION, CONSTANT_TRANSITION,
// or NULL_DESCRIPTOR, we don't mark the value as live. Only for
// type MAP_TRANSITION is the value a Object* (a Map*).
// Contents contains (value, details) pairs. If the details say that
// the type of descriptor is MAP_TRANSITION, CONSTANT_TRANSITION, or
// NULL_DESCRIPTOR, we don't mark the value as live. Only for
// MAP_TRANSITION and CONSTANT_TRANSITION is the value an Object* (a
// Map*).
for (int i = 0; i < contents->length(); i += 2) {
// If the pair (value, details) at index i, i+1 is not
// a transition or null descriptor, mark the value.

View File

@ -1493,6 +1493,16 @@ int DescriptorArray::Search(String* name) {
}
int DescriptorArray::SearchWithCache(String* name) {
int number = DescriptorLookupCache::Lookup(this, name);
if (number == DescriptorLookupCache::kAbsent) {
number = Search(name);
DescriptorLookupCache::Update(this, name, number);
}
return number;
}
String* DescriptorArray::GetKey(int descriptor_number) {
ASSERT(descriptor_number < number_of_descriptors());
return String::cast(get(ToKeyIndex(descriptor_number)));

View File

@ -1330,7 +1330,7 @@ Object* JSObject::AddConstantFunctionProperty(String* name,
if (attributes != NONE) {
return function;
}
ConstTransitionDescriptor mark(name);
ConstTransitionDescriptor mark(name, Map::cast(new_map));
new_descriptors =
old_map->instance_descriptors()->CopyInsert(&mark, KEEP_TRANSITIONS);
if (new_descriptors->IsFailure()) {
@ -1688,11 +1688,7 @@ bool JSObject::SetElementWithCallbackSetterInPrototypes(uint32_t index,
void JSObject::LookupInDescriptor(String* name, LookupResult* result) {
DescriptorArray* descriptors = map()->instance_descriptors();
int number = DescriptorLookupCache::Lookup(descriptors, name);
if (number == DescriptorLookupCache::kAbsent) {
number = descriptors->Search(name);
DescriptorLookupCache::Update(descriptors, name, number);
}
int number = descriptors->SearchWithCache(name);
if (number != DescriptorArray::kNotFound) {
result->DescriptorResult(this, descriptors->GetDetails(number), number);
} else {
@ -1889,10 +1885,25 @@ Object* JSObject::SetProperty(LookupResult* result,
result->holder());
case INTERCEPTOR:
return SetPropertyWithInterceptor(name, value, attributes);
case CONSTANT_TRANSITION:
// Replace with a MAP_TRANSITION to a new map with a FIELD, even
// if the value is a function.
case CONSTANT_TRANSITION: {
// If the same constant function is being added we can simply
// transition to the target map.
Map* target_map = result->GetTransitionMap();
DescriptorArray* target_descriptors = target_map->instance_descriptors();
int number = target_descriptors->SearchWithCache(name);
ASSERT(number != DescriptorArray::kNotFound);
ASSERT(target_descriptors->GetType(number) == CONSTANT_FUNCTION);
JSFunction* function =
JSFunction::cast(target_descriptors->GetValue(number));
ASSERT(!Heap::InNewSpace(function));
if (value == function) {
set_map(target_map);
return value;
}
// Otherwise, replace with a MAP_TRANSITION to a new map with a
// FIELD, even if the value is a constant function.
return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
}
case NULL_DESCRIPTOR:
return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
default:
@ -4936,7 +4947,8 @@ void String::PrintOn(FILE* file) {
void Map::CreateBackPointers() {
DescriptorArray* descriptors = instance_descriptors();
for (int i = 0; i < descriptors->number_of_descriptors(); i++) {
if (descriptors->GetType(i) == MAP_TRANSITION) {
if (descriptors->GetType(i) == MAP_TRANSITION ||
descriptors->GetType(i) == CONSTANT_TRANSITION) {
// Get target.
Map* target = Map::cast(descriptors->GetValue(i));
#ifdef DEBUG
@ -4977,7 +4989,8 @@ void Map::ClearNonLiveTransitions(Object* real_prototype) {
// map is not reached again by following a back pointer from a
// non-live object.
PropertyDetails details(Smi::cast(contents->get(i + 1)));
if (details.type() == MAP_TRANSITION) {
if (details.type() == MAP_TRANSITION ||
details.type() == CONSTANT_TRANSITION) {
Map* target = reinterpret_cast<Map*>(contents->get(i));
ASSERT(target->IsHeapObject());
if (!target->IsMarked()) {

View File

@ -1865,6 +1865,10 @@ class DescriptorArray: public FixedArray {
// Search the instance descriptors for given name.
inline int Search(String* name);
// As the above, but uses DescriptorLookupCache and updates it when
// necessary.
inline int SearchWithCache(String* name);
// Tells whether the name is present int the array.
bool Contains(String* name) { return kNotFound != Search(name); }

View File

@ -115,8 +115,8 @@ class MapTransitionDescriptor: public Descriptor {
// the same CONSTANT_FUNCTION field.
class ConstTransitionDescriptor: public Descriptor {
public:
explicit ConstTransitionDescriptor(String* key)
: Descriptor(key, Smi::FromInt(0), NONE, CONSTANT_TRANSITION) { }
explicit ConstTransitionDescriptor(String* key, Map* map)
: Descriptor(key, map, NONE, CONSTANT_TRANSITION) { }
};
@ -260,7 +260,7 @@ class LookupResult BASE_EMBEDDED {
Map* GetTransitionMap() {
ASSERT(lookup_type_ == DESCRIPTOR_TYPE);
ASSERT(type() == MAP_TRANSITION);
ASSERT(type() == MAP_TRANSITION || type() == CONSTANT_TRANSITION);
return Map::cast(GetValue());
}