Implement for-in loop for proxies.

Fix related corner case for Object.keys.
Remove obsolete GET_KEYS builtin.

R=ricow@chromium.org
BUG=
TEST=

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@9760 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
rossberg@chromium.org 2011-10-24 15:56:18 +00:00
parent f92da58e13
commit 70dc2fe968
17 changed files with 365 additions and 85 deletions

View File

@ -2874,8 +2874,10 @@ Local<Array> v8::Object::GetPropertyNames() {
ENTER_V8(isolate);
i::HandleScope scope(isolate);
i::Handle<i::JSObject> self = Utils::OpenHandle(this);
bool threw = false;
i::Handle<i::FixedArray> value =
i::GetKeysInFixedArrayFor(self, i::INCLUDE_PROTOS);
i::GetKeysInFixedArrayFor(self, i::INCLUDE_PROTOS, &threw);
if (threw) return Local<v8::Array>();
// Because we use caching to speed up enumeration it is important
// to never change the result of the basic enumeration function so
// we clone the result.
@ -2893,8 +2895,10 @@ Local<Array> v8::Object::GetOwnPropertyNames() {
ENTER_V8(isolate);
i::HandleScope scope(isolate);
i::Handle<i::JSObject> self = Utils::OpenHandle(this);
bool threw = false;
i::Handle<i::FixedArray> value =
i::GetKeysInFixedArrayFor(self, i::LOCAL_ONLY);
i::GetKeysInFixedArrayFor(self, i::LOCAL_ONLY, &threw);
if (threw) return Local<v8::Array>();
// Because we use caching to speed up enumeration it is important
// to never change the result of the basic enumeration function so
// we clone the result.

View File

@ -929,11 +929,17 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
__ bind(&done_convert);
__ push(r0);
// Check for proxies.
Label call_runtime;
STATIC_ASSERT(FIRST_JS_PROXY_TYPE == FIRST_SPEC_OBJECT_TYPE);
__ CompareObjectType(r0, r1, r1, LAST_JS_PROXY_TYPE);
__ b(le, &call_runtime);
// Check cache validity in generated code. This is a fast case for
// the JSObject::IsSimpleEnum cache validity checks. If we cannot
// guarantee cache validity, call the runtime system to check cache
// validity or get the property names in a fixed array.
Label next, call_runtime;
Label next;
// Preload a couple of values used in the loop.
Register empty_fixed_array_value = r6;
__ LoadRoot(empty_fixed_array_value, Heap::kEmptyFixedArrayRootIndex);
@ -1012,9 +1018,16 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
__ jmp(&loop);
// We got a fixed array in register r0. Iterate through that.
Label non_proxy;
__ bind(&fixed_array);
__ mov(r1, Operand(Smi::FromInt(0))); // Map (0) - force slow check.
__ Push(r1, r0);
__ mov(r1, Operand(Smi::FromInt(1))); // Smi indicates slow check
__ ldr(r2, MemOperand(sp, 0 * kPointerSize)); // Get enumerated object
STATIC_ASSERT(FIRST_JS_PROXY_TYPE == FIRST_SPEC_OBJECT_TYPE);
__ CompareObjectType(r2, r3, r3, LAST_JS_PROXY_TYPE);
__ b(gt, &non_proxy);
__ mov(r1, Operand(Smi::FromInt(0))); // Zero indicates proxy
__ bind(&non_proxy);
__ Push(r1, r0); // Smi and array
__ ldr(r1, FieldMemOperand(r0, FixedArray::kLengthOffset));
__ mov(r0, Operand(Smi::FromInt(0)));
__ Push(r1, r0); // Fixed array length (as smi) and initial index.
@ -1031,18 +1044,23 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
__ add(r2, r2, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
__ ldr(r3, MemOperand(r2, r0, LSL, kPointerSizeLog2 - kSmiTagSize));
// Get the expected map from the stack or a zero map in the
// Get the expected map from the stack or a smi in the
// permanent slow case into register r2.
__ ldr(r2, MemOperand(sp, 3 * kPointerSize));
// Check if the expected map still matches that of the enumerable.
// If not, we have to filter the key.
// If not, we may have to filter the key.
Label update_each;
__ ldr(r1, MemOperand(sp, 4 * kPointerSize));
__ ldr(r4, FieldMemOperand(r1, HeapObject::kMapOffset));
__ cmp(r4, Operand(r2));
__ b(eq, &update_each);
// For proxies, no filtering is done.
// TODO(rossberg): What if only a prototype is a proxy? Not specified yet.
__ cmp(r2, Operand(Smi::FromInt(0)));
__ b(eq, &update_each);
// Convert the entry to a string or (smi) 0 if it isn't a property
// any more. If the property has been removed while iterating, we
// just skip it.

View File

@ -1376,6 +1376,7 @@ void Genesis::InstallExperimentalNativeFunctions() {
INSTALL_NATIVE(JSFunction, "DerivedHasTrap", derived_has_trap);
INSTALL_NATIVE(JSFunction, "DerivedGetTrap", derived_get_trap);
INSTALL_NATIVE(JSFunction, "DerivedSetTrap", derived_set_trap);
INSTALL_NATIVE(JSFunction, "ProxyEnumerate", proxy_enumerate);
}
}

View File

@ -238,7 +238,6 @@ enum BuiltinExtraArguments {
V(DELETE, 2) \
V(IN, 1) \
V(INSTANCE_OF, 1) \
V(GET_KEYS, 0) \
V(FILTER_KEY, 1) \
V(CALL_NON_FUNCTION, 0) \
V(CALL_NON_FUNCTION_AS_CONSTRUCTOR, 0) \

View File

@ -139,6 +139,7 @@ enum BindingFlags {
V(DERIVED_HAS_TRAP_INDEX, JSFunction, derived_has_trap) \
V(DERIVED_GET_TRAP_INDEX, JSFunction, derived_get_trap) \
V(DERIVED_SET_TRAP_INDEX, JSFunction, derived_set_trap) \
V(PROXY_ENUMERATE, JSFunction, proxy_enumerate) \
V(RANDOM_SEED_INDEX, ByteArray, random_seed)
// JSFunctions are pairs (context, function code), sometimes also called
@ -260,6 +261,7 @@ class Context: public FixedArray {
DERIVED_HAS_TRAP_INDEX,
DERIVED_GET_TRAP_INDEX,
DERIVED_SET_TRAP_INDEX,
PROXY_ENUMERATE,
RANDOM_SEED_INDEX,
// Properties from here are treated as weak references by the full GC.

View File

@ -691,7 +691,7 @@ void CustomArguments::IterateInstance(ObjectVisitor* v) {
// Compute the property keys from the interceptor.
v8::Handle<v8::Array> GetKeysForNamedInterceptor(Handle<JSObject> receiver,
v8::Handle<v8::Array> GetKeysForNamedInterceptor(Handle<JSReceiver> receiver,
Handle<JSObject> object) {
Isolate* isolate = receiver->GetIsolate();
Handle<InterceptorInfo> interceptor(object->GetNamedInterceptor());
@ -713,7 +713,7 @@ v8::Handle<v8::Array> GetKeysForNamedInterceptor(Handle<JSObject> receiver,
// Compute the element keys from the interceptor.
v8::Handle<v8::Array> GetKeysForIndexedInterceptor(Handle<JSObject> receiver,
v8::Handle<v8::Array> GetKeysForIndexedInterceptor(Handle<JSReceiver> receiver,
Handle<JSObject> object) {
Isolate* isolate = receiver->GetIsolate();
Handle<InterceptorInfo> interceptor(object->GetIndexedInterceptor());
@ -744,8 +744,9 @@ static bool ContainsOnlyValidKeys(Handle<FixedArray> array) {
}
Handle<FixedArray> GetKeysInFixedArrayFor(Handle<JSObject> object,
KeyCollectionType type) {
Handle<FixedArray> GetKeysInFixedArrayFor(Handle<JSReceiver> object,
KeyCollectionType type,
bool* threw) {
USE(ContainsOnlyValidKeys);
Isolate* isolate = object->GetIsolate();
Handle<FixedArray> content = isolate->factory()->empty_fixed_array();
@ -760,6 +761,16 @@ Handle<FixedArray> GetKeysInFixedArrayFor(Handle<JSObject> object,
for (Handle<Object> p = object;
*p != isolate->heap()->null_value();
p = Handle<Object>(p->GetPrototype(), isolate)) {
if (p->IsJSProxy()) {
Handle<JSProxy> proxy(JSProxy::cast(*p), isolate);
Handle<Object> args[] = { proxy };
Handle<Object> names = Execution::Call(
isolate->proxy_enumerate(), object, ARRAY_SIZE(args), args, threw);
if (*threw) return content;
content = AddKeysFromJSArray(content, Handle<JSArray>::cast(names));
break;
}
Handle<JSObject> current(JSObject::cast(*p), isolate);
// Check access rights if required.
@ -826,11 +837,11 @@ Handle<FixedArray> GetKeysInFixedArrayFor(Handle<JSObject> object,
}
Handle<JSArray> GetKeysFor(Handle<JSObject> object) {
Handle<JSArray> GetKeysFor(Handle<JSReceiver> object, bool* threw) {
Isolate* isolate = object->GetIsolate();
isolate->counters()->for_in()->Increment();
Handle<FixedArray> elements = GetKeysInFixedArrayFor(object,
INCLUDE_PROTOS);
Handle<FixedArray> elements =
GetKeysInFixedArrayFor(object, INCLUDE_PROTOS, threw);
return isolate->factory()->NewJSArrayWithElements(elements);
}

View File

@ -295,18 +295,19 @@ int GetScriptLineNumberSafe(Handle<Script> script, int code_position);
// Computes the enumerable keys from interceptors. Used for debug mirrors and
// by GetKeysInFixedArrayFor below.
v8::Handle<v8::Array> GetKeysForNamedInterceptor(Handle<JSObject> receiver,
v8::Handle<v8::Array> GetKeysForNamedInterceptor(Handle<JSReceiver> receiver,
Handle<JSObject> object);
v8::Handle<v8::Array> GetKeysForIndexedInterceptor(Handle<JSObject> receiver,
v8::Handle<v8::Array> GetKeysForIndexedInterceptor(Handle<JSReceiver> receiver,
Handle<JSObject> object);
enum KeyCollectionType { LOCAL_ONLY, INCLUDE_PROTOS };
// Computes the enumerable keys for a JSObject. Used for implementing
// "for (n in object) { }".
Handle<FixedArray> GetKeysInFixedArrayFor(Handle<JSObject> object,
KeyCollectionType type);
Handle<JSArray> GetKeysFor(Handle<JSObject> object);
Handle<FixedArray> GetKeysInFixedArrayFor(Handle<JSReceiver> object,
KeyCollectionType type,
bool* threw);
Handle<JSArray> GetKeysFor(Handle<JSReceiver> object, bool* threw);
Handle<FixedArray> GetEnumPropertyKeys(Handle<JSObject> object,
bool cache_result);

View File

@ -920,11 +920,17 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
__ push(eax);
increment_stack_height();
// Check for proxies.
Label call_runtime;
STATIC_ASSERT(FIRST_JS_PROXY_TYPE == FIRST_SPEC_OBJECT_TYPE);
__ CmpObjectType(eax, LAST_JS_PROXY_TYPE, ecx);
__ j(below_equal, &call_runtime);
// Check cache validity in generated code. This is a fast case for
// the JSObject::IsSimpleEnum cache validity checks. If we cannot
// guarantee cache validity, call the runtime system to check cache
// validity or get the property names in a fixed array.
Label next, call_runtime;
Label next;
__ mov(ecx, eax);
__ bind(&next);
@ -995,9 +1001,17 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
__ jmp(&loop);
// We got a fixed array in register eax. Iterate through that.
Label non_proxy;
__ bind(&fixed_array);
__ push(Immediate(Smi::FromInt(0))); // Map (0) - force slow check.
__ push(eax);
__ mov(ebx, Immediate(Smi::FromInt(1))); // Smi indicates slow check
__ mov(ecx, Operand(esp, 0 * kPointerSize)); // Get enumerated object
STATIC_ASSERT(FIRST_JS_PROXY_TYPE == FIRST_SPEC_OBJECT_TYPE);
__ CmpObjectType(ecx, LAST_JS_PROXY_TYPE, ecx);
__ j(above, &non_proxy);
__ mov(ebx, Immediate(Smi::FromInt(0))); // Zero indicates proxy
__ bind(&non_proxy);
__ push(ebx); // Smi
__ push(eax); // Array
__ mov(eax, FieldOperand(eax, FixedArray::kLengthOffset));
__ push(eax); // Fixed array length (as smi).
__ push(Immediate(Smi::FromInt(0))); // Initial index.
@ -1014,17 +1028,23 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
__ mov(ebx, Operand(esp, 2 * kPointerSize));
__ mov(ebx, FieldOperand(ebx, eax, times_2, FixedArray::kHeaderSize));
// Get the expected map from the stack or a zero map in the
// Get the expected map from the stack or a smi in the
// permanent slow case into register edx.
__ mov(edx, Operand(esp, 3 * kPointerSize));
// Check if the expected map still matches that of the enumerable.
// If not, we have to filter the key.
// If not, we may have to filter the key.
Label update_each;
__ mov(ecx, Operand(esp, 4 * kPointerSize));
__ cmp(edx, FieldOperand(ecx, HeapObject::kMapOffset));
__ j(equal, &update_each, Label::kNear);
// For proxies, no filtering is done.
// TODO(rossberg): What if only a prototype is a proxy? Not specified yet.
ASSERT(Smi::FromInt(0) == 0);
__ test(edx, edx);
__ j(zero, &update_each);
// Convert the entry to a string or null if it isn't a property
// anymore. If the property has been removed while iterating, we
// just skip it.

View File

@ -4092,15 +4092,16 @@ MaybeObject* JSObject::PreventExtensions() {
// Tests for the fast common case for property enumeration:
// - This object and all prototypes has an enum cache (which means that it has
// no interceptors and needs no access checks).
// - This object and all prototypes has an enum cache (which means that
// it is no proxy, has no interceptors and needs no access checks).
// - This object has no elements.
// - No prototype has enumerable properties/elements.
bool JSObject::IsSimpleEnum() {
bool JSReceiver::IsSimpleEnum() {
Heap* heap = GetHeap();
for (Object* o = this;
o != heap->null_value();
o = JSObject::cast(o)->GetPrototype()) {
if (!o->IsJSObject()) return false;
JSObject* curr = JSObject::cast(o);
if (!curr->map()->instance_descriptors()->HasEnumCache()) return false;
ASSERT(!curr->HasNamedInterceptor());

View File

@ -1365,6 +1365,9 @@ class JSReceiver: public HeapObject {
StrictModeFlag strict_mode,
bool check_prototype);
// Tests for the fast common case for property enumeration.
bool IsSimpleEnum();
// Returns the class name ([[Class]] property in the specification).
String* class_name();
@ -1617,9 +1620,6 @@ class JSObject: public JSReceiver {
MUST_USE_RESULT MaybeObject* DeleteProperty(String* name, DeleteMode mode);
MUST_USE_RESULT MaybeObject* DeleteElement(uint32_t index, DeleteMode mode);
// Tests for the fast common case for property enumeration.
bool IsSimpleEnum();
inline void ValidateSmiOnlyElements();
// Makes sure that this object can contain non-smi Object as elements.

View File

@ -153,9 +153,32 @@ function DerivedKeysTrap() {
var enumerableNames = []
for (var i = 0, count = 0; i < names.length; ++i) {
var name = names[i]
if (this.getOwnPropertyDescriptor(TO_STRING_INLINE(name)).enumerable) {
var desc = this.getOwnPropertyDescriptor(TO_STRING_INLINE(name))
if (!IS_UNDEFINED(desc) && desc.enumerable) {
enumerableNames[count++] = names[i]
}
}
return enumerableNames
}
function DerivedEnumerateTrap() {
var names = this.getPropertyNames()
var enumerableNames = []
for (var i = 0, count = 0; i < names.length; ++i) {
var name = names[i]
var desc = this.getPropertyDescriptor(TO_STRING_INLINE(name))
if (!IS_UNDEFINED(desc) && desc.enumerable) {
enumerableNames[count++] = names[i]
}
}
return enumerableNames
}
function ProxyEnumerate(proxy) {
var handler = %GetHandler(proxy)
if (IS_UNDEFINED(handler.enumerate)) {
return %Apply(DerivedEnumerateTrap, handler, [], 0, 0)
} else {
return ToStringArray(handler.enumerate(), "enumerate")
}
}

View File

@ -4749,8 +4749,11 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_IsPropertyEnumerable) {
RUNTIME_FUNCTION(MaybeObject*, Runtime_GetPropertyNames) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_CHECKED(JSObject, object, 0);
return *GetKeysFor(object);
CONVERT_ARG_CHECKED(JSReceiver, object, 0);
bool threw = false;
Handle<JSArray> result = GetKeysFor(object, &threw);
if (threw) return Failure::Exception();
return *result;
}
@ -4762,14 +4765,16 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetPropertyNames) {
RUNTIME_FUNCTION(MaybeObject*, Runtime_GetPropertyNamesFast) {
ASSERT(args.length() == 1);
CONVERT_CHECKED(JSObject, raw_object, args[0]);
CONVERT_CHECKED(JSReceiver, raw_object, args[0]);
if (raw_object->IsSimpleEnum()) return raw_object->map();
HandleScope scope(isolate);
Handle<JSObject> object(raw_object);
Handle<FixedArray> content = GetKeysInFixedArrayFor(object,
INCLUDE_PROTOS);
Handle<JSReceiver> object(raw_object);
bool threw = false;
Handle<FixedArray> content =
GetKeysInFixedArrayFor(object, INCLUDE_PROTOS, &threw);
if (threw) return Failure::Exception();
// Test again, since cache may have been built by preceding call.
if (object->IsSimpleEnum()) return object->map();
@ -4966,8 +4971,11 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_LocalKeys) {
object = Handle<JSObject>::cast(proto);
}
Handle<FixedArray> contents = GetKeysInFixedArrayFor(object,
LOCAL_ONLY);
bool threw = false;
Handle<FixedArray> contents =
GetKeysInFixedArrayFor(object, LOCAL_ONLY, &threw);
if (threw) return Failure::Exception();
// Some fast paths through GetKeysInFixedArrayFor reuse a cached
// property array and since the result is mutable we have to create
// a fresh clone on each invocation.
@ -10132,7 +10140,11 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetArrayKeys) {
if (array->elements()->IsDictionary()) {
// Create an array and get all the keys into it, then remove all the
// keys that are not integers in the range 0 to length-1.
Handle<FixedArray> keys = GetKeysInFixedArrayFor(array, INCLUDE_PROTOS);
bool threw = false;
Handle<FixedArray> keys =
GetKeysInFixedArrayFor(array, INCLUDE_PROTOS, &threw);
if (threw) return Failure::Exception();
int keys_length = keys->length();
for (int i = 0; i < keys_length; i++) {
Object* key = keys->get(i);
@ -10951,7 +10963,11 @@ static Handle<JSObject> MaterializeLocalScope(
if (function_context->has_extension() &&
!function_context->IsGlobalContext()) {
Handle<JSObject> ext(JSObject::cast(function_context->extension()));
Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
bool threw = false;
Handle<FixedArray> keys =
GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS, &threw);
if (threw) return Handle<JSObject>();
for (int i = 0; i < keys->length(); i++) {
// Names of variables introduced by eval are strings.
ASSERT(keys->get(i)->IsString());
@ -10999,7 +11015,11 @@ static Handle<JSObject> MaterializeClosure(Isolate* isolate,
// be variables introduced by eval.
if (context->has_extension()) {
Handle<JSObject> ext(JSObject::cast(context->extension()));
Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
bool threw = false;
Handle<FixedArray> keys =
GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS, &threw);
if (threw) return Handle<JSObject>();
for (int i = 0; i < keys->length(); i++) {
// Names of variables introduced by eval are strings.
ASSERT(keys->get(i)->IsString());

View File

@ -392,13 +392,6 @@ function INSTANCE_OF(F) {
}
// Get an array of property keys for the given object. Used in
// for-in statements.
function GET_KEYS() {
return %GetPropertyNames(this);
}
// Filter a given key against an object by checking if the object
// has a property with the given key; return the key as a string if
// it has. Otherwise returns 0 (smi). Used in for-in statements.

View File

@ -937,14 +937,14 @@ function ToStringArray(obj, trap) {
}
var n = ToUint32(obj.length);
var array = new $Array(n);
var names = {}
var names = {} // TODO(rossberg): use sets once they are ready.
for (var index = 0; index < n; index++) {
var s = ToString(obj[index]);
if (s in names) {
throw MakeTypeError("proxy_repeated_prop_name", [obj, trap, s])
}
array[index] = s;
names.s = 0;
names[s] = 0;
}
return array;
}

View File

@ -890,11 +890,17 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
__ bind(&done_convert);
__ push(rax);
// Check for proxies.
Label call_runtime;
STATIC_ASSERT(FIRST_JS_PROXY_TYPE == FIRST_SPEC_OBJECT_TYPE);
__ CmpObjectType(rax, LAST_JS_PROXY_TYPE, rcx);
__ j(below_equal, &call_runtime);
// Check cache validity in generated code. This is a fast case for
// the JSObject::IsSimpleEnum cache validity checks. If we cannot
// guarantee cache validity, call the runtime system to check cache
// validity or get the property names in a fixed array.
Label next, call_runtime;
Label next;
Register empty_fixed_array_value = r8;
__ LoadRoot(empty_fixed_array_value, Heap::kEmptyFixedArrayRootIndex);
Register empty_descriptor_array_value = r9;
@ -970,9 +976,17 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
__ jmp(&loop);
// We got a fixed array in register rax. Iterate through that.
Label non_proxy;
__ bind(&fixed_array);
__ Push(Smi::FromInt(0)); // Map (0) - force slow check.
__ push(rax);
__ Move(rbx, Smi::FromInt(1)); // Smi indicates slow check
__ movq(rcx, Operand(rsp, 0 * kPointerSize)); // Get enumerated object
STATIC_ASSERT(FIRST_JS_PROXY_TYPE == FIRST_SPEC_OBJECT_TYPE);
__ CmpObjectType(rcx, LAST_JS_PROXY_TYPE, rcx);
__ j(above, &non_proxy);
__ Move(rbx, Smi::FromInt(0)); // Zero indicates proxy
__ bind(&non_proxy);
__ push(rbx); // Smi
__ push(rax); // Array
__ movq(rax, FieldOperand(rax, FixedArray::kLengthOffset));
__ push(rax); // Fixed array length (as smi).
__ Push(Smi::FromInt(0)); // Initial index.
@ -991,17 +1005,22 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
index.scale,
FixedArray::kHeaderSize));
// Get the expected map from the stack or a zero map in the
// Get the expected map from the stack or a smi in the
// permanent slow case into register rdx.
__ movq(rdx, Operand(rsp, 3 * kPointerSize));
// Check if the expected map still matches that of the enumerable.
// If not, we have to filter the key.
// If not, we may have to filter the key.
Label update_each;
__ movq(rcx, Operand(rsp, 4 * kPointerSize));
__ cmpq(rdx, FieldOperand(rcx, HeapObject::kMapOffset));
__ j(equal, &update_each, Label::kNear);
// For proxies, no filtering is done.
// TODO(rossberg): What if only a prototype is a proxy? Not specified yet.
__ Cmp(rdx, Smi::FromInt(0));
__ j(equal, &update_each, Label::kNear);
// Convert the entry to a string or null if it isn't a property
// anymore. If the property has been removed while iterating, we
// just skip it.

View File

@ -0,0 +1,168 @@
// Copyright 2011 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: --harmony-proxies
// Helper.
function TestWithProxies(test, x, y, z) {
test(Proxy.create, x, y, z)
test(function(h) {return Proxy.createFunction(h, function() {})}, x, y, z)
}
// Iterate over a proxy.
function TestForIn(properties, handler) {
TestWithProxies(TestForIn2, properties, handler)
}
function TestForIn2(create, properties, handler) {
var p = create(handler)
var found = []
for (var x in p) found.push(x)
assertArrayEquals(properties, found)
}
TestForIn(["0", "a"], {
enumerate: function() { return [0, "a"] }
})
TestForIn(["null", "a"], {
enumerate: function() { return this.enumerate2() },
enumerate2: function() { return [null, "a"] }
})
TestForIn(["b", "d"], {
getPropertyNames: function() { return ["a", "b", "c", "d", "e"] },
getPropertyDescriptor: function(k) {
switch (k) {
case "a": return {enumerable: false, value: "3"};
case "b": return {enumerable: true, get get() {}};
case "c": return {value: 4};
case "d": return {get enumerable() { return true }};
default: return undefined;
}
}
})
TestForIn(["b", "a", "0", "c"], Proxy.create({
get: function(pr, pk) {
return function() { return ["b", "a", 0, "c"] }
}
}))
// Iterate over an object with a proxy prototype.
function TestForInDerived(properties, handler) {
TestWithProxies(TestForInDerived2, properties, handler)
}
function TestForInDerived2(create, properties, handler) {
var p = create(handler)
var o = Object.create(p)
o.z = 0
var found = []
for (var x in o) found.push(x)
assertArrayEquals(["z"].concat(properties), found)
var oo = Object.create(o)
oo.y = 0
var found = []
for (var x in oo) found.push(x)
assertArrayEquals(["y", "z"].concat(properties), found)
}
TestForInDerived(["0", "a"], {
enumerate: function() { return [0, "a"] },
getPropertyDescriptor: function(k) {
return k == "0" || k == "a" ? {} : undefined
}
})
TestForInDerived(["null", "a"], {
enumerate: function() { return this.enumerate2() },
enumerate2: function() { return [null, "a"] },
getPropertyDescriptor: function(k) {
return k == "null" || k == "a" ? {} : undefined
}
})
TestForInDerived(["b", "d"], {
getPropertyNames: function() { return ["a", "b", "c", "d", "e"] },
getPropertyDescriptor: function(k) {
switch (k) {
case "a": return {enumerable: false, value: "3"};
case "b": return {enumerable: true, get get() {}};
case "c": return {value: 4};
case "d": return {get enumerable() { return true }};
default: return undefined;
}
}
})
// Throw exception in enumerate trap.
function TestForInThrow(handler) {
TestWithProxies(TestForInThrow2, handler)
}
function TestForInThrow2(create, handler) {
var p = create(handler)
var o = Object.create(p)
assertThrows(function(){ for (var x in p) {} }, "myexn")
assertThrows(function(){ for (var x in o) {} }, "myexn")
}
TestForInThrow({
enumerate: function() { throw "myexn" }
})
TestForInThrow({
enumerate: function() { return this.enumerate2() },
enumerate2: function() { throw "myexn" }
})
TestForInThrow({
getPropertyNames: function() { throw "myexn" }
})
TestForInThrow({
getPropertyNames: function() { return ["a"] },
getPropertyDescriptor: function() { throw "myexn" }
})
TestForInThrow(Proxy.create({
get: function(pr, pk) {
return function() { throw "myexn" }
}
}))

View File

@ -28,9 +28,6 @@
// Flags: --harmony-proxies
// TODO(rossberg): for-in not implemented on proxies.
// Helper.
function TestWithProxies(test, x, y, z) {
@ -719,17 +716,17 @@ function TestDefine2(create, handler) {
assertEquals("zzz", key)
assertEquals(0, Object.getOwnPropertyNames(desc).length)
// TODO(rossberg): This test requires for-in on proxies.
// var d = create({
// get: function(r, k) { return (k === "value") ? 77 : void 0 },
// getOwnPropertyNames: function() { return ["value"] }
// })
// assertEquals(1, Object.getOwnPropertyNames(d).length)
// assertEquals(77, d.value)
// assertEquals(p, Object.defineProperty(p, "p", d))
// assertEquals("p", key)
// assertEquals(1, Object.getOwnPropertyNames(desc).length)
// assertEquals(77, desc.value)
var d = create({
get: function(r, k) { return (k === "value") ? 77 : void 0 },
getOwnPropertyNames: function() { return ["value"] },
enumerate: function() { return ["value"] }
})
assertEquals(1, Object.getOwnPropertyNames(d).length)
assertEquals(77, d.value)
assertEquals(p, Object.defineProperty(p, "p", d))
assertEquals("p", key)
assertEquals(1, Object.getOwnPropertyNames(desc).length)
assertEquals(77, desc.value)
var props = {
'11': {},
@ -774,17 +771,16 @@ function TestDefineThrow2(create, handler) {
assertThrows(function(){ Object.defineProperty(p, "a", {value: 44})}, "myexn")
assertThrows(function(){ Object.defineProperty(p, 0, {value: 44})}, "myexn")
// TODO(rossberg): These tests require for-in on proxies.
// var d1 = create({
// get: function(r, k) { throw "myexn" },
// getOwnPropertyNames: function() { return ["value"] }
// })
// assertThrows(function(){ Object.defineProperty(p, "p", d1) }, "myexn")
// var d2 = create({
// get: function(r, k) { return 77 },
// getOwnPropertyNames: function() { throw "myexn" }
// })
// assertThrows(function(){ Object.defineProperty(p, "p", d2) }, "myexn")
var d1 = create({
get: function(r, k) { throw "myexn" },
getOwnPropertyNames: function() { return ["value"] }
})
assertThrows(function(){ Object.defineProperty(p, "p", d1) }, "myexn")
var d2 = create({
get: function(r, k) { return 77 },
getOwnPropertyNames: function() { throw "myexn" }
})
assertThrows(function(){ Object.defineProperty(p, "p", d2) }, "myexn")
var props = {bla: {get value() { throw "otherexn" }}}
assertThrows(function(){ Object.defineProperties(p, props) }, "otherexn")
@ -1606,7 +1602,9 @@ TestKeys(["[object Object]"], {
TestKeys(["a", "0"], {
getOwnPropertyNames: function() { return ["a", 23, "zz", "", 0] },
getOwnPropertyDescriptor: function(k) { return {enumerable: k.length == 1} }
getOwnPropertyDescriptor: function(k) {
return k == "" ? undefined : {enumerable: k.length == 1}
}
})
TestKeys(["23", "zz", ""], {
@ -1620,10 +1618,12 @@ TestKeys(["23", "zz", ""], {
TestKeys(["a", "b", "c", "5"], {
get getOwnPropertyNames() {
return function() { return ["0", 4, "a", "b", "c", 5] }
return function() { return ["0", 4, "a", "b", "c", 5, "ety"] }
},
get getOwnPropertyDescriptor() {
return function(k) { return {enumerable: k >= "44"} }
return function(k) {
return k == "ety" ? undefined : {enumerable: k >= "44"}
}
}
})