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:
parent
f92da58e13
commit
70dc2fe968
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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) \
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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());
|
||||
|
@ -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.
|
||||
|
25
src/proxy.js
25
src/proxy.js
@ -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")
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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.
|
||||
|
168
test/mjsunit/harmony/proxies-for.js
Normal file
168
test/mjsunit/harmony/proxies-for.js
Normal 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" }
|
||||
}
|
||||
}))
|
@ -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"}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user