Do not shortcut union of keys if lhs is empty.

The problem is other array may have holes, for example
when fixed array comes from JSArray (in case of named interceptor).

If that would prove to be a performance problem, we could
pass an additional argument into UnionOfKeys to hold actual length.

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5591 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
antonm@chromium.org 2010-10-05 13:10:43 +00:00
parent 6e1d8065a4
commit 7c238db829
3 changed files with 59 additions and 4 deletions

View File

@ -635,8 +635,19 @@ v8::Handle<v8::Array> GetKeysForIndexedInterceptor(Handle<JSObject> receiver,
}
static bool ContainsOnlyValidKeys(Handle<FixedArray> array) {
int len = array->length();
for (int i = 0; i < len; i++) {
Object* e = array->get(i);
if (!(e->IsString() || e->IsNumber())) return false;
}
return true;
}
Handle<FixedArray> GetKeysInFixedArrayFor(Handle<JSObject> object,
KeyCollectionType type) {
USE(ContainsOnlyValidKeys);
Handle<FixedArray> content = Factory::empty_fixed_array();
Handle<JSObject> arguments_boilerplate =
Handle<JSObject>(
@ -664,6 +675,7 @@ Handle<FixedArray> GetKeysInFixedArrayFor(Handle<JSObject> object,
Factory::NewFixedArray(current->NumberOfEnumElements());
current->GetEnumElementKeys(*element_keys);
content = UnionOfKeys(content, element_keys);
ASSERT(ContainsOnlyValidKeys(content));
// Add the element keys from the interceptor.
if (current->HasIndexedInterceptor()) {
@ -671,6 +683,7 @@ Handle<FixedArray> GetKeysInFixedArrayFor(Handle<JSObject> object,
GetKeysForIndexedInterceptor(object, current);
if (!result.IsEmpty())
content = AddKeysFromJSArray(content, v8::Utils::OpenHandle(*result));
ASSERT(ContainsOnlyValidKeys(content));
}
// We can cache the computed property keys if access checks are
@ -692,6 +705,7 @@ Handle<FixedArray> GetKeysInFixedArrayFor(Handle<JSObject> object,
// Compute the property keys and cache them if possible.
content =
UnionOfKeys(content, GetEnumPropertyKeys(current, cache_enum_keys));
ASSERT(ContainsOnlyValidKeys(content));
// Add the property keys from the interceptor.
if (current->HasNamedInterceptor()) {
@ -699,6 +713,7 @@ Handle<FixedArray> GetKeysInFixedArrayFor(Handle<JSObject> object,
GetKeysForNamedInterceptor(object, current);
if (!result.IsEmpty())
content = AddKeysFromJSArray(content, v8::Utils::OpenHandle(*result));
ASSERT(ContainsOnlyValidKeys(content));
}
// If we only want local properties we bail out after the first

View File

@ -3601,9 +3601,17 @@ Object* FixedArray::AddKeysFromJSArray(JSArray* array) {
Object* FixedArray::UnionOfKeys(FixedArray* other) {
int len0 = length();
#ifdef DEBUG
if (FLAG_enable_slow_asserts) {
for (int i = 0; i < len0; i++) {
ASSERT(get(i)->IsString() || get(i)->IsNumber());
}
}
#endif
int len1 = other->length();
// Optimize if either is empty.
if (len0 == 0) return other;
// Optimize if 'other' is empty.
// We cannot optimize if 'this' is empty, as other may have holes
// or non keys.
if (len1 == 0) return this;
// Compute how many elements are not in this.
@ -3623,14 +3631,18 @@ Object* FixedArray::UnionOfKeys(FixedArray* other) {
FixedArray* result = FixedArray::cast(obj);
WriteBarrierMode mode = result->GetWriteBarrierMode(no_gc);
for (int i = 0; i < len0; i++) {
result->set(i, get(i), mode);
Object* e = get(i);
ASSERT(e->IsString() || e->IsNumber());
result->set(i, e, mode);
}
// Fill in the extra keys.
int index = 0;
for (int y = 0; y < len1; y++) {
Object* value = other->get(y);
if (!value->IsTheHole() && !HasKey(this, value)) {
result->set(len0 + index, other->get(y), mode);
Object* e = other->get(y);
ASSERT(e->IsString() || e->IsNumber());
result->set(len0 + index, e, mode);
index++;
}
}

View File

@ -11727,3 +11727,31 @@ TEST(RegExp) {
context->Global()->Set(v8_str("ex"), try_catch.Exception());
ExpectTrue("ex instanceof SyntaxError");
}
static v8::Handle<v8::Value> Getter(v8::Local<v8::String> property,
const v8::AccessorInfo& info ) {
return v8_str("42!");
}
static v8::Handle<v8::Array> Enumerator(const v8::AccessorInfo& info) {
v8::Handle<v8::Array> result = v8::Array::New();
result->Set(0, v8_str("universalAnswer"));
return result;
}
TEST(NamedEnumeratorAndForIn) {
v8::HandleScope handle_scope;
LocalContext context;
v8::Context::Scope context_scope(context.local());
v8::Handle<v8::ObjectTemplate> tmpl = v8::ObjectTemplate::New();
tmpl->SetNamedPropertyHandler(Getter, NULL, NULL, NULL, Enumerator);
context->Global()->Set(v8_str("o"), tmpl->NewInstance());
v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun(
"var result = []; for (var k in o) result.push(k); result"));
CHECK_EQ(1, result->Length());
CHECK_EQ(v8_str("universalAnswer"), result->Get(0));
}