Revision: 9760
Author: [email protected]
Date: Mon Oct 24 08:56:18 2011
Log: Implement for-in loop for proxies.
Fix related corner case for Object.keys.
Remove obsolete GET_KEYS builtin.
[email protected]
BUG=
TEST=
Review URL: http://codereview.chromium.org/8256015
http://code.google.com/p/v8/source/detail?r=9760
Added:
/branches/bleeding_edge/test/mjsunit/harmony/proxies-for.js
Modified:
/branches/bleeding_edge/src/api.cc
/branches/bleeding_edge/src/arm/full-codegen-arm.cc
/branches/bleeding_edge/src/bootstrapper.cc
/branches/bleeding_edge/src/builtins.h
/branches/bleeding_edge/src/contexts.h
/branches/bleeding_edge/src/handles.cc
/branches/bleeding_edge/src/handles.h
/branches/bleeding_edge/src/ia32/full-codegen-ia32.cc
/branches/bleeding_edge/src/objects.cc
/branches/bleeding_edge/src/objects.h
/branches/bleeding_edge/src/proxy.js
/branches/bleeding_edge/src/runtime.cc
/branches/bleeding_edge/src/runtime.js
/branches/bleeding_edge/src/v8natives.js
/branches/bleeding_edge/src/x64/full-codegen-x64.cc
/branches/bleeding_edge/test/mjsunit/harmony/proxies.js
=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/harmony/proxies-for.js Mon Oct 24
08:56:18 2011
@@ -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" }
+ }
+}))
=======================================
--- /branches/bleeding_edge/src/api.cc Mon Oct 24 05:12:21 2011
+++ /branches/bleeding_edge/src/api.cc Mon Oct 24 08:56:18 2011
@@ -2874,8 +2874,10 @@
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 @@
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.
=======================================
--- /branches/bleeding_edge/src/arm/full-codegen-arm.cc Mon Oct 24 05:12:21
2011
+++ /branches/bleeding_edge/src/arm/full-codegen-arm.cc Mon Oct 24 08:56:18
2011
@@ -929,11 +929,17 @@
__ 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 @@
__ 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 @@
__ 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.
=======================================
--- /branches/bleeding_edge/src/bootstrapper.cc Mon Oct 24 05:12:21 2011
+++ /branches/bleeding_edge/src/bootstrapper.cc Mon Oct 24 08:56:18 2011
@@ -1376,6 +1376,7 @@
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);
}
}
=======================================
--- /branches/bleeding_edge/src/builtins.h Wed Oct 19 05:10:18 2011
+++ /branches/bleeding_edge/src/builtins.h Mon Oct 24 08:56:18 2011
@@ -238,7 +238,6 @@
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) \
=======================================
--- /branches/bleeding_edge/src/contexts.h Mon Oct 24 05:12:21 2011
+++ /branches/bleeding_edge/src/contexts.h Mon Oct 24 08:56:18 2011
@@ -139,6 +139,7 @@
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 @@
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.
=======================================
--- /branches/bleeding_edge/src/handles.cc Wed Oct 19 05:10:18 2011
+++ /branches/bleeding_edge/src/handles.cc Mon Oct 24 08:56:18 2011
@@ -691,7 +691,7 @@
// 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 @@
// 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 @@
}
-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 @@
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<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);
}
=======================================
--- /branches/bleeding_edge/src/handles.h Wed Oct 19 05:10:18 2011
+++ /branches/bleeding_edge/src/handles.h Mon Oct 24 08:56:18 2011
@@ -295,18 +295,19 @@
// 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);
=======================================
--- /branches/bleeding_edge/src/ia32/full-codegen-ia32.cc Mon Oct 24
06:53:08 2011
+++ /branches/bleeding_edge/src/ia32/full-codegen-ia32.cc Mon Oct 24
08:56:18 2011
@@ -920,11 +920,17 @@
__ 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 @@
__ 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 @@
__ 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.
=======================================
--- /branches/bleeding_edge/src/objects.cc Mon Oct 24 06:45:19 2011
+++ /branches/bleeding_edge/src/objects.cc Mon Oct 24 08:56:18 2011
@@ -4092,15 +4092,16 @@
// 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());
=======================================
--- /branches/bleeding_edge/src/objects.h Mon Oct 24 06:11:14 2011
+++ /branches/bleeding_edge/src/objects.h Mon Oct 24 08:56:18 2011
@@ -1365,6 +1365,9 @@
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 @@
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.
=======================================
--- /branches/bleeding_edge/src/proxy.js Tue Oct 4 05:48:16 2011
+++ /branches/bleeding_edge/src/proxy.js Mon Oct 24 08:56:18 2011
@@ -153,9 +153,32 @@
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")
+ }
+}
=======================================
--- /branches/bleeding_edge/src/runtime.cc Mon Oct 24 00:47:22 2011
+++ /branches/bleeding_edge/src/runtime.cc Mon Oct 24 08:56:18 2011
@@ -4749,8 +4749,11 @@
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_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 @@
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 @@
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 @@
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 @@
// 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());
=======================================
--- /branches/bleeding_edge/src/runtime.js Wed Oct 19 06:56:18 2011
+++ /branches/bleeding_edge/src/runtime.js Mon Oct 24 08:56:18 2011
@@ -390,13 +390,6 @@
// Return whether or not O is in the prototype chain of V.
return %IsInPrototypeChain(O, V) ? 0 : 1;
}
-
-
-// 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
=======================================
--- /branches/bleeding_edge/src/v8natives.js Thu Oct 20 00:55:30 2011
+++ /branches/bleeding_edge/src/v8natives.js Mon Oct 24 08:56:18 2011
@@ -937,14 +937,14 @@
}
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;
}
=======================================
--- /branches/bleeding_edge/src/x64/full-codegen-x64.cc Mon Oct 24 05:12:21
2011
+++ /branches/bleeding_edge/src/x64/full-codegen-x64.cc Mon Oct 24 08:56:18
2011
@@ -890,11 +890,17 @@
__ 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 @@
__ 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 @@
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.
=======================================
--- /branches/bleeding_edge/test/mjsunit/harmony/proxies.js Mon Oct 10
02:59:03 2011
+++ /branches/bleeding_edge/test/mjsunit/harmony/proxies.js Mon Oct 24
08:56:18 2011
@@ -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 @@
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 @@
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(["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(["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"}
+ }
}
})
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev