 Chromium Code Reviews
 Chromium Code Reviews Issue 10388047:
  Implement correct checking for inherited readonliness on assignment.  (Closed) 
  Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
    
  
    Issue 10388047:
  Implement correct checking for inherited readonliness on assignment.  (Closed) 
  Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge| 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. |