Chromium Code Reviews| Index: src/objects.cc |
| diff --git a/src/objects.cc b/src/objects.cc |
| index 5649a56464bb69300c1b031dd5c5cf4a45fbf53e..b6702dd361c4911f0d180c0be279028e1ebc61e6 100644 |
| --- a/src/objects.cc |
| +++ b/src/objects.cc |
| @@ -1764,14 +1764,11 @@ MaybeObject* JSObject::SetPropertyPostInterceptor( |
| // found. Use set property to handle all these cases. |
| return SetProperty(&result, name, value, attributes, strict_mode); |
| } |
| - bool found = false; |
| + bool done = false; |
| MaybeObject* result_object; |
| - result_object = SetPropertyWithCallbackSetterInPrototypes(name, |
| - value, |
| - attributes, |
| - &found, |
| - strict_mode); |
| - if (found) return result_object; |
| + result_object = SetPropertyViaPrototypes( |
|
Michael Starzinger
2012/05/30 13:08:55
The method call should fit into one line if we bre
rossberg
2012/05/31 13:31:28
Done.
|
| + name, value, attributes, strict_mode, &done); |
| + if (done) return result_object; |
| // Add a new real property. |
| return AddProperty(name, value, attributes, strict_mode); |
| } |
| @@ -2061,11 +2058,7 @@ void JSObject::LookupCallbackSetterInPrototypes(String* name, |
| return result->HandlerResult(JSProxy::cast(pt)); |
| } |
| JSObject::cast(pt)->LocalLookupRealNamedProperty(name, result); |
| - if (result->IsProperty()) { |
| - if (result->type() == CALLBACKS && !result->IsReadOnly()) return; |
| - // Found non-callback or read-only callback, stop looking. |
| - break; |
| - } |
| + if (result->IsProperty()) return; |
| } |
| result->NotFound(); |
| } |
| @@ -2087,8 +2080,8 @@ MaybeObject* JSObject::SetElementWithCallbackSetterInPrototypes( |
| *found = true; // Force abort |
| return maybe; |
| } |
| - return JSProxy::cast(pt)->SetPropertyWithHandlerIfDefiningSetter( |
| - name, value, NONE, strict_mode, found); |
| + return JSProxy::cast(pt)->SetPropertyViaPrototypesWithHandler( |
| + this, name, value, NONE, strict_mode, found); |
| } |
| if (!JSObject::cast(pt)->HasDictionaryElements()) { |
| continue; |
| @@ -2112,45 +2105,59 @@ MaybeObject* JSObject::SetElementWithCallbackSetterInPrototypes( |
| return heap->the_hole_value(); |
| } |
| -MaybeObject* JSObject::SetPropertyWithCallbackSetterInPrototypes( |
| +MaybeObject* JSObject::SetPropertyViaPrototypes( |
| String* name, |
| Object* value, |
| PropertyAttributes attributes, |
| - bool* found, |
| - StrictModeFlag strict_mode) { |
| + StrictModeFlag strict_mode, |
| + bool* done) { |
| Heap* heap = GetHeap(); |
| + Isolate* isolate = heap->isolate(); |
| + |
| + *done = false; |
| // We could not find a local property so let's check whether there is an |
| - // accessor that wants to handle the property. |
| - LookupResult accessor_result(heap->isolate()); |
| - LookupCallbackSetterInPrototypes(name, &accessor_result); |
| - if (accessor_result.IsFound()) { |
| - *found = true; |
| - if (accessor_result.type() == CALLBACKS) { |
| - return SetPropertyWithCallback(accessor_result.GetCallbackObject(), |
| - name, |
| - value, |
| - accessor_result.holder(), |
| - strict_mode); |
| - } else if (accessor_result.type() == HANDLER) { |
| - // There is a proxy in the prototype chain. Invoke its |
| - // getPropertyDescriptor trap. |
| - bool found = false; |
| - // SetPropertyWithHandlerIfDefiningSetter can cause GC, |
| - // make sure to use the handlified references after calling |
| - // the function. |
| - Handle<JSObject> self(this); |
| - Handle<String> hname(name); |
| - Handle<Object> hvalue(value); |
| - MaybeObject* result = |
| - accessor_result.proxy()->SetPropertyWithHandlerIfDefiningSetter( |
| - name, value, attributes, strict_mode, &found); |
| - if (found) return result; |
| - // The proxy does not define the property as an accessor. |
| - // Consequently, it has no effect on setting the receiver. |
| - return self->AddProperty(*hname, *hvalue, attributes, strict_mode); |
| + // accessor that wants to handle the property, or whether the property is |
| + // read-only on the prototype chain. |
| + LookupResult result(isolate); |
| + LookupCallbackSetterInPrototypes(name, &result); |
| + if (result.IsFound()) { |
| + switch (result.type()) { |
| + case NORMAL: |
| + case FIELD: |
| + case CONSTANT_FUNCTION: |
| + *done = result.IsReadOnly(); |
| + break; |
| + case INTERCEPTOR: { |
| + PropertyAttributes attr = |
| + result.holder()->GetPropertyAttributeWithInterceptor( |
| + this, name, true); |
| + *done = !!(attr & READ_ONLY); |
|
Michael Starzinger
2012/05/30 13:08:55
Why not not not use no negation?
rossberg
2012/05/31 13:31:28
Standard JS idiom for (explicitly) converting to a
|
| + break; |
| + } |
| + case CALLBACKS: { |
| + *done = true; |
| + return SetPropertyWithCallback(result.GetCallbackObject(), |
| + name, value, result.holder(), strict_mode); |
| + } |
| + case HANDLER: { |
| + return result.proxy()->SetPropertyViaPrototypesWithHandler( |
| + this, name, value, attributes, strict_mode, done); |
| + } |
| + case MAP_TRANSITION: |
| + case CONSTANT_TRANSITION: |
| + case NULL_DESCRIPTOR: |
| + case ELEMENTS_TRANSITION: |
| + ; // Fall through |
|
Michael Starzinger
2012/05/30 13:08:55
Fall through to nothing makes no sense, can we jus
rossberg
2012/05/31 13:31:28
Done.
|
| } |
| } |
| - *found = false; |
| + |
| + // If we get here with *done true, we have encountered a read-only property. |
| + if (*done) { |
| + if (strict_mode == kNonStrictMode) return value; |
| + Handle<Object> args[] = { Handle<Object>(name), Handle<Object>(this)}; |
| + return isolate->Throw(*isolate->factory()->NewTypeError( |
| + "strict_read_only_property", HandleVector(args, ARRAY_SIZE(args)))); |
| + } |
| return heap->the_hole_value(); |
| } |
| @@ -2630,7 +2637,7 @@ bool JSProxy::HasPropertyWithHandler(String* name_raw) { |
| Handle<Object> args[] = { name }; |
| Handle<Object> result = CallTrap( |
| "has", isolate->derived_has_trap(), ARRAY_SIZE(args), args); |
| - if (isolate->has_pending_exception()) return Failure::Exception(); |
| + if (isolate->has_pending_exception()) return false; |
| return result->ToBoolean()->IsTrue(); |
| } |
| @@ -2655,77 +2662,92 @@ MUST_USE_RESULT MaybeObject* JSProxy::SetPropertyWithHandler( |
| } |
| -MUST_USE_RESULT MaybeObject* JSProxy::SetPropertyWithHandlerIfDefiningSetter( |
| +MUST_USE_RESULT MaybeObject* JSProxy::SetPropertyViaPrototypesWithHandler( |
| + JSObject* receiver_raw, |
| String* name_raw, |
| Object* value_raw, |
| PropertyAttributes attributes, |
| StrictModeFlag strict_mode, |
| - bool* found) { |
| - *found = true; // except where defined otherwise... |
| - Isolate* isolate = GetHeap()->isolate(); |
| + bool* done) { |
| + Isolate* isolate = GetIsolate(); |
| Handle<JSProxy> proxy(this); |
| - Handle<Object> handler(this->handler()); // Trap might morph proxy. |
| + Handle<JSObject> receiver(receiver_raw); |
| Handle<String> name(name_raw); |
| Handle<Object> value(value_raw); |
| + Handle<Object> handler(this->handler()); // Trap might morph proxy. |
| + |
| + *done = true; // except where redefined... |
| Handle<Object> args[] = { name }; |
| Handle<Object> result = proxy->CallTrap( |
| "getPropertyDescriptor", Handle<Object>(), ARRAY_SIZE(args), args); |
| if (isolate->has_pending_exception()) return Failure::Exception(); |
| - if (!result->IsUndefined()) { |
| - // The proxy handler cares about this property. |
| - // Check whether it is virtualized as an accessor. |
| - // Emulate [[GetProperty]] semantics for proxies. |
| - bool has_pending_exception; |
| - Handle<Object> argv[] = { result }; |
| - Handle<Object> desc = |
| - Execution::Call(isolate->to_complete_property_descriptor(), result, |
| - ARRAY_SIZE(argv), argv, &has_pending_exception); |
| - if (has_pending_exception) return Failure::Exception(); |
| - |
| - Handle<String> conf_name = |
| - isolate->factory()->LookupAsciiSymbol("configurable_"); |
| - Handle<Object> configurable(v8::internal::GetProperty(desc, conf_name)); |
| - ASSERT(!isolate->has_pending_exception()); |
| - if (configurable->IsFalse()) { |
| - Handle<String> trap = |
| - isolate->factory()->LookupAsciiSymbol("getPropertyDescriptor"); |
| - Handle<Object> args[] = { handler, trap, name }; |
| - Handle<Object> error = isolate->factory()->NewTypeError( |
| - "proxy_prop_not_configurable", HandleVector(args, ARRAY_SIZE(args))); |
| - return isolate->Throw(*error); |
| - } |
| - ASSERT(configurable->IsTrue()); |
| + if (result->IsUndefined()) { |
| + *done = false; |
| + return GetHeap()->the_hole_value(); |
| + } |
| + |
| + // Emulate [[GetProperty]] semantics for proxies. |
| + bool has_pending_exception; |
| + Handle<Object> argv[] = { result }; |
| + Handle<Object> desc = |
| + Execution::Call(isolate->to_complete_property_descriptor(), result, |
| + ARRAY_SIZE(argv), argv, &has_pending_exception); |
| + if (has_pending_exception) return Failure::Exception(); |
| - // Check for AccessorDescriptor. |
| - Handle<String> set_name = isolate->factory()->LookupAsciiSymbol("set_"); |
| - Handle<Object> setter(v8::internal::GetProperty(desc, set_name)); |
| + // [[GetProperty]] requires to check that all properties are configurable. |
| + Handle<String> configurable_name = |
| + isolate->factory()->LookupAsciiSymbol("configurable_"); |
| + Handle<Object> configurable( |
| + v8::internal::GetProperty(desc, configurable_name)); |
| + ASSERT(!isolate->has_pending_exception()); |
| + ASSERT(configurable->IsTrue() || configurable->IsFalse()); |
| + if (configurable->IsFalse()) { |
| + Handle<String> trap = |
| + isolate->factory()->LookupAsciiSymbol("getPropertyDescriptor"); |
| + Handle<Object> args[] = { handler, trap, name }; |
| + Handle<Object> error = isolate->factory()->NewTypeError( |
| + "proxy_prop_not_configurable", HandleVector(args, ARRAY_SIZE(args))); |
| + return isolate->Throw(*error); |
| + } |
| + ASSERT(configurable->IsTrue()); |
| + |
| + // Check for DataDescriptor. |
| + Handle<String> hasWritable_name = |
| + isolate->factory()->LookupAsciiSymbol("hasWritable_"); |
| + Handle<Object> hasWritable(v8::internal::GetProperty(desc, hasWritable_name)); |
| + ASSERT(!isolate->has_pending_exception()); |
| + ASSERT(hasWritable->IsTrue() || hasWritable->IsFalse()); |
| + if (hasWritable->IsTrue()) { |
| + Handle<String> writable_name = |
| + isolate->factory()->LookupAsciiSymbol("writable_"); |
| + Handle<Object> writable(v8::internal::GetProperty(desc, writable_name)); |
| ASSERT(!isolate->has_pending_exception()); |
| - if (!setter->IsUndefined()) { |
| - // We have a setter -- invoke it. |
| - // TODO(rossberg): nicer would be to cast to some JSCallable here... |
| - return proxy->SetPropertyWithDefinedSetter( |
| - JSReceiver::cast(*setter), *value); |
| - } else { |
| - Handle<String> get_name = isolate->factory()->LookupAsciiSymbol("get_"); |
| - Handle<Object> getter(v8::internal::GetProperty(desc, get_name)); |
| - ASSERT(!isolate->has_pending_exception()); |
| - if (!getter->IsUndefined()) { |
| - // We have a getter but no setter -- the property may not be |
| - // written. In strict mode, throw an error. |
| - if (strict_mode == kNonStrictMode) return *value; |
| - Handle<Object> args[] = { name, proxy }; |
| - Handle<Object> error = isolate->factory()->NewTypeError( |
| - "no_setter_in_callback", HandleVector(args, ARRAY_SIZE(args))); |
| - return isolate->Throw(*error); |
| - } |
| - } |
| - // Fall-through. |
| + ASSERT(writable->IsTrue() || writable->IsFalse()); |
| + *done = writable->IsFalse(); |
| + if (!*done) return GetHeap()->the_hole_value(); |
| + if (strict_mode == kNonStrictMode) return *value; |
| + Handle<Object> args[] = { name, receiver }; |
| + Handle<Object> error = isolate->factory()->NewTypeError( |
| + "strict_read_only_property", HandleVector(args, ARRAY_SIZE(args))); |
| + return isolate->Throw(*error); |
| } |
| - // The proxy does not define the property as an accessor. |
| - *found = false; |
| - return *value; |
| + // We have an AccessorDescriptor. |
| + Handle<String> set_name = isolate->factory()->LookupAsciiSymbol("set_"); |
| + Handle<Object> setter(v8::internal::GetProperty(desc, set_name)); |
| + ASSERT(!isolate->has_pending_exception()); |
| + if (!setter->IsUndefined()) { |
| + // TODO(rossberg): nicer would be to cast to some JSCallable here... |
| + return proxy->SetPropertyWithDefinedSetter( |
| + JSReceiver::cast(*setter), *value); |
| + } |
| + |
| + if (strict_mode == kNonStrictMode) return *value; |
| + Handle<Object> args2[] = { name, proxy }; |
| + Handle<Object> error = isolate->factory()->NewTypeError( |
| + "no_setter_in_callback", HandleVector(args2, ARRAY_SIZE(args2))); |
| + return isolate->Throw(*error); |
| } |
| @@ -2918,18 +2940,11 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* result, |
| } |
| if (!result->IsProperty() && !IsJSContextExtensionObject()) { |
| - bool found = false; |
| - MaybeObject* result_object; |
| - result_object = SetPropertyWithCallbackSetterInPrototypes(name, |
| - value, |
| - attributes, |
| - &found, |
| - strict_mode); |
| - if (found) return result_object; |
| - } |
| - |
| - // At this point, no GC should have happened, as this would invalidate |
| - // 'result', which we cannot handlify! |
| + bool done = false; |
| + MaybeObject* result_object = SetPropertyViaPrototypes( |
|
Michael Starzinger
2012/05/30 13:08:55
The method call should fit into one line if we bre
rossberg
2012/05/31 13:31:28
Done.
|
| + name, value, attributes, strict_mode, &done); |
| + if (done) return result_object; |
| + } |
| if (!result->IsFound()) { |
| // Neither properties nor transitions found. |