Chromium Code Reviews| Index: src/objects.cc |
| diff --git a/src/objects.cc b/src/objects.cc |
| index 906f7feef6bb30f5bf9f3787e568f1b7f9405a30..472f45c79e79b2bdc8b134ace5072f58cdf631d6 100644 |
| --- a/src/objects.cc |
| +++ b/src/objects.cc |
| @@ -1677,6 +1677,7 @@ MaybeObject* JSObject::AddProperty(String* name, |
| ASSERT(!IsJSGlobalProxy()); |
| Map* map_of_this = map(); |
| Heap* heap = GetHeap(); |
| + MaybeObject* ret; |
|
Toon Verwaest
2012/11/05 13:33:22
Can we call this maybe_result for consistency?
rossberg
2012/11/05 17:11:08
Done.
|
| if (extensibility_check == PERFORM_EXTENSIBILITY_CHECK && |
| !map_of_this->is_extensible()) { |
| if (strict_mode == kNonStrictMode) { |
| @@ -1688,28 +1689,61 @@ MaybeObject* JSObject::AddProperty(String* name, |
| HandleVector(args, 1))); |
| } |
| } |
| + |
| if (HasFastProperties()) { |
| // Ensure the descriptor array does not get too big. |
| if (map_of_this->NumberOfOwnDescriptors() < |
| DescriptorArray::kMaxNumberOfDescriptors) { |
| if (value->IsJSFunction()) { |
| - return AddConstantFunctionProperty(name, |
| - JSFunction::cast(value), |
| - attributes); |
| + ret = AddConstantFunctionProperty(name, |
| + JSFunction::cast(value), |
|
rafaelw
2012/10/31 14:44:31
whitespace
rossberg
2012/11/05 17:11:08
Done.
|
| + attributes); |
| } else { |
| - return AddFastProperty(name, value, attributes, store_mode); |
| + ret = AddFastProperty(name, value, attributes, store_mode); |
| } |
| } else { |
| // Normalize the object to prevent very large instance descriptors. |
| // This eliminates unwanted N^2 allocation and lookup behavior. |
| Object* obj; |
| - { MaybeObject* maybe_obj = |
| - NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0); |
| - if (!maybe_obj->ToObject(&obj)) return maybe_obj; |
| - } |
| + ret = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0); |
| + if (!ret->ToObject(&obj)) return ret; |
|
Toon Verwaest
2012/11/05 13:33:22
if (ret->IsFailure()) return ret;
I actually pref
rossberg
2012/11/05 17:11:08
Done.
|
| + ret = AddSlowProperty(name, value, attributes); |
| } |
| + } else { |
| + ret = AddSlowProperty(name, value, attributes); |
| } |
| - return AddSlowProperty(name, value, attributes); |
| + |
| + if (FLAG_harmony_observation && map()->is_observed() && !ret->IsFailure()) { |
| + this->NotifyObservers("new", name, heap->the_hole_value()); |
| + } |
| + |
| + return ret; |
|
rafaelw
2012/10/31 14:44:31
Isn't this unsafe? The notifyObservers could have
Toon Verwaest
2012/11/05 13:33:22
Yes. We should not jump into handlified code from
rossberg
2012/11/05 17:11:08
Indeed.
rossberg
2012/11/05 17:11:08
Most of the methods in question are already docume
|
| +} |
| + |
| + |
| +void JSObject::NotifyObservers( |
| + const char* type_raw, String* name_raw, Object* oldValue_raw) { |
| + Isolate* isolate = GetIsolate(); |
| + HandleScope scope; |
| + Handle<String> type = isolate->factory()->LookupAsciiSymbol(type_raw); |
| + Handle<JSObject> object(this); |
| + Handle<String> name(name_raw); |
| + bool threw; |
| + if (oldValue_raw->IsTheHole()) { |
|
rafaelw
2012/10/31 14:44:31
nit: This can be cleaner if you make argv a Scoped
rossberg
2012/11/05 17:11:08
Well, that would make the code pretty verbose as w
|
| + Handle<Object> args[] = { type, object, name }; |
| + Execution::Call(Handle<JSFunction>(isolate->observers_notify_change()), |
| + Handle<Object>(isolate->heap()->undefined_value()), |
| + ARRAY_SIZE(args), args, |
| + &threw); |
| + } else { |
| + Handle<Object> oldValue(oldValue_raw); |
| + Handle<Object> args[] = { type, object, name, oldValue }; |
| + Execution::Call(Handle<JSFunction>(isolate->observers_notify_change()), |
| + Handle<Object>(isolate->heap()->undefined_value()), |
| + ARRAY_SIZE(args), args, |
| + &threw); |
| + } |
| + ASSERT(!threw); |
| } |
| @@ -2860,6 +2894,7 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* result, |
| return self->AddProperty( |
| *name, *value, attributes, strict_mode, store_mode); |
| } |
| + |
| if (result->IsProperty() && result->IsReadOnly()) { |
| if (strict_mode == kStrictMode) { |
| Handle<Object> args[] = { name, self }; |
| @@ -2870,19 +2905,28 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* result, |
| } |
| } |
| + Object* oldValue = heap->the_hole_value(); |
| + if (FLAG_harmony_observation && map()->is_observed()) { |
| + // TODO(observe): save oldValue |
| + } |
| + |
| // This is a real property that is not read-only, or it is a |
| // transition or null descriptor and there are no setters in the prototypes. |
| + MaybeObject* ret = *value; |
| switch (result->type()) { |
| case NORMAL: |
| - return self->SetNormalizedProperty(result, *value); |
| + ret = self->SetNormalizedProperty(result, *value); |
| + break; |
| case FIELD: |
| - return self->FastPropertyAtPut(result->GetFieldIndex(), *value); |
| + ret = self->FastPropertyAtPut(result->GetFieldIndex(), *value); |
| + break; |
| case CONSTANT_FUNCTION: |
| // Only replace the function if necessary. |
| if (*value == result->GetConstantFunction()) return *value; |
| // Preserve the attributes of this existing property. |
| attributes = result->GetAttributes(); |
| - return self->ConvertDescriptorToField(*name, *value, attributes); |
| + ret = self->ConvertDescriptorToField(*name, *value, attributes); |
| + break; |
| case CALLBACKS: { |
| Object* callback_object = result->GetCallbackObject(); |
| return self->SetPropertyWithCallback(callback_object, |
| @@ -2892,10 +2936,11 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* result, |
| strict_mode); |
| } |
| case INTERCEPTOR: |
| - return self->SetPropertyWithInterceptor(*name, |
| - *value, |
| - attributes, |
| - strict_mode); |
| + ret = self->SetPropertyWithInterceptor(*name, |
| + *value, |
| + attributes, |
| + strict_mode); |
| + break; |
| case TRANSITION: { |
| Map* transition_map = result->GetTransitionTarget(); |
| int descriptor = transition_map->LastAdded(); |
| @@ -2906,37 +2951,43 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* result, |
| if (details.type() == FIELD) { |
| if (attributes == details.attributes()) { |
| int field_index = descriptors->GetFieldIndex(descriptor); |
| - return self->AddFastPropertyUsingMap(transition_map, |
| - *name, |
| - *value, |
| - field_index); |
| + ret = self->AddFastPropertyUsingMap(transition_map, |
| + *name, |
| + *value, |
| + field_index); |
| + } else { |
| + ret = self->ConvertDescriptorToField(*name, *value, attributes); |
| } |
| - return self->ConvertDescriptorToField(*name, *value, attributes); |
| } else if (details.type() == CALLBACKS) { |
| - return ConvertDescriptorToField(*name, *value, attributes); |
| - } |
| - |
| - ASSERT(details.type() == CONSTANT_FUNCTION); |
| - |
| - Object* constant_function = descriptors->GetValue(descriptor); |
| - // If the same constant function is being added we can simply |
| - // transition to the target map. |
| - if (constant_function == *value) { |
| - self->set_map(transition_map); |
| - return constant_function; |
| + ret = ConvertDescriptorToField(*name, *value, attributes); |
| + } else { |
| + ASSERT(details.type() == CONSTANT_FUNCTION); |
| + |
| + Object* constant_function = descriptors->GetValue(descriptor); |
| + if (constant_function == *value) { |
| + // If the same constant function is being added we can simply |
| + // transition to the target map. |
| + self->set_map(transition_map); |
| + ret = constant_function; |
| + } else { |
| + // Otherwise, replace with a map transition to a new map with a FIELD, |
| + // even if the value is a constant function. |
| + ret = ConvertTransitionToMapTransition( |
| + result->GetTransitionIndex(), *name, *value, attributes); |
| + } |
| } |
| - // Otherwise, replace with a map transition to a new map with a FIELD, |
| - // even if the value is a constant function. |
| - return ConvertTransitionToMapTransition( |
| - result->GetTransitionIndex(), *name, *value, attributes); |
| + break; |
| } |
| case HANDLER: |
| case NONEXISTENT: |
| UNREACHABLE(); |
| - return *value; |
| } |
| - UNREACHABLE(); // keep the compiler happy |
| - return *value; |
| + |
| + if (FLAG_harmony_observation && map()->is_observed() && !ret->IsFailure()) { |
| + this->NotifyObservers("updated", *name, oldValue); |
| + } |
| + |
| + return ret; |
|
rafaelw
2012/10/31 14:44:31
same question about returning MaybeObject after in
rossberg
2012/11/05 17:11:08
Done.
|
| } |
| @@ -2999,24 +3050,34 @@ MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes( |
| return AddProperty(name, value, attributes, kNonStrictMode); |
| } |
| + Object* oldValue = isolate->heap()->the_hole_value(); |
| + if (FLAG_harmony_observation && map()->is_observed()) { |
| + // TODO(observe): save oldValue |
| + } |
| + |
| // Check of IsReadOnly removed from here in clone. |
| + MaybeObject* ret = value; |
| switch (result.type()) { |
| case NORMAL: { |
| PropertyDetails details = PropertyDetails(attributes, NORMAL); |
| - return SetNormalizedProperty(name, value, details); |
| + ret = SetNormalizedProperty(name, value, details); |
| + break; |
| } |
| case FIELD: |
| - return FastPropertyAtPut(result.GetFieldIndex(), value); |
| + ret = FastPropertyAtPut(result.GetFieldIndex(), value); |
| + break; |
| case CONSTANT_FUNCTION: |
| // Only replace the function if necessary. |
| if (value == result.GetConstantFunction()) return value; |
| // Preserve the attributes of this existing property. |
| attributes = result.GetAttributes(); |
| - return ConvertDescriptorToField(name, value, attributes); |
| + ret = ConvertDescriptorToField(name, value, attributes); |
| + break; |
| case CALLBACKS: |
| case INTERCEPTOR: |
| // Override callback in clone |
| - return ConvertDescriptorToField(name, value, attributes); |
| + ret = ConvertDescriptorToField(name, value, attributes); |
| + break; |
| case TRANSITION: { |
| Map* transition_map = result.GetTransitionTarget(); |
| int descriptor = transition_map->LastAdded(); |
| @@ -3027,29 +3088,37 @@ MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes( |
| if (details.type() == FIELD) { |
| if (attributes == details.attributes()) { |
| int field_index = descriptors->GetFieldIndex(descriptor); |
| - return AddFastPropertyUsingMap(transition_map, |
| - name, |
| - value, |
| - field_index); |
| + ret = AddFastPropertyUsingMap(transition_map, |
| + name, |
| + value, |
| + field_index); |
| + } else { |
| + ret = ConvertDescriptorToField(name, value, attributes); |
| } |
| - return ConvertDescriptorToField(name, value, attributes); |
| } else if (details.type() == CALLBACKS) { |
| - return ConvertDescriptorToField(name, value, attributes); |
| - } |
| - |
| - ASSERT(details.type() == CONSTANT_FUNCTION); |
| + ret = ConvertDescriptorToField(name, value, attributes); |
| + } else { |
| + ASSERT(details.type() == CONSTANT_FUNCTION); |
| - // Replace transition to CONSTANT FUNCTION with a map transition to a new |
| - // map with a FIELD, even if the value is a function. |
| - return ConvertTransitionToMapTransition( |
| - result.GetTransitionIndex(), name, value, attributes); |
| + // Replace transition to CONSTANT FUNCTION with a map transition to a new |
| + // map with a FIELD, even if the value is a function. |
| + ret = ConvertTransitionToMapTransition( |
| + result.GetTransitionIndex(), name, value, attributes); |
| + } |
| + break; |
| } |
| case HANDLER: |
| case NONEXISTENT: |
| UNREACHABLE(); |
| } |
| - UNREACHABLE(); // keep the compiler happy |
| - return value; |
| + |
| + if (FLAG_harmony_observation && map()->is_observed() && !ret->IsFailure()) { |
| + const char* type = |
| + attributes == result.GetAttributes() ? "updated" : "reconfigured"; |
| + this->NotifyObservers(type, name, oldValue); |
| + } |
| + |
| + return ret; |
|
rafaelw
2012/10/31 14:44:31
same question about returning MaybeObject
rossberg
2012/11/05 17:11:08
Done.
|
| } |
| @@ -3953,38 +4022,51 @@ MaybeObject* JSObject::DeleteProperty(String* name, DeleteMode mode) { |
| uint32_t index = 0; |
| if (name->AsArrayIndex(&index)) { |
| return DeleteElement(index, mode); |
| - } else { |
| - LookupResult result(isolate); |
| - LocalLookup(name, &result); |
| - if (!result.IsFound()) return isolate->heap()->true_value(); |
| - // Ignore attributes if forcing a deletion. |
| - if (result.IsDontDelete() && mode != FORCE_DELETION) { |
| - if (mode == STRICT_DELETION) { |
| - // Deleting a non-configurable property in strict mode. |
| - HandleScope scope(isolate); |
| - Handle<Object> args[2] = { Handle<Object>(name), Handle<Object>(this) }; |
| - return isolate->Throw(*isolate->factory()->NewTypeError( |
| - "strict_delete_property", HandleVector(args, 2))); |
| - } |
| - return isolate->heap()->false_value(); |
| - } |
| - // Check for interceptor. |
| - if (result.IsInterceptor()) { |
| - // Skip interceptor if forcing a deletion. |
| - if (mode == FORCE_DELETION) { |
| - return DeletePropertyPostInterceptor(name, mode); |
| - } |
| - return DeletePropertyWithInterceptor(name); |
| + } |
| + |
| + LookupResult result(isolate); |
| + LocalLookup(name, &result); |
| + if (!result.IsFound()) return isolate->heap()->true_value(); |
| + // Ignore attributes if forcing a deletion. |
| + if (result.IsDontDelete() && mode != FORCE_DELETION) { |
| + if (mode == STRICT_DELETION) { |
| + // Deleting a non-configurable property in strict mode. |
| + HandleScope scope(isolate); |
| + Handle<Object> args[2] = { Handle<Object>(name), Handle<Object>(this) }; |
| + return isolate->Throw(*isolate->factory()->NewTypeError( |
| + "strict_delete_property", HandleVector(args, 2))); |
| } |
| + return isolate->heap()->false_value(); |
| + } |
| + |
| + Object* oldValue = isolate->heap()->the_hole_value(); |
| + if (FLAG_harmony_observation && map()->is_observed()) { |
| + // TODO(observe): save oldValue |
| + } |
| + MaybeObject* ret; |
| + |
| + // Check for interceptor. |
| + if (result.IsInterceptor()) { |
| + // Skip interceptor if forcing a deletion. |
| + if (mode == FORCE_DELETION) { |
| + ret = DeletePropertyPostInterceptor(name, mode); |
| + } else { |
| + ret = DeletePropertyWithInterceptor(name); |
| + } |
| + } else { |
| // Normalize object if needed. |
| Object* obj; |
| - { MaybeObject* maybe_obj = |
| - NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0); |
| - if (!maybe_obj->ToObject(&obj)) return maybe_obj; |
| - } |
| + ret = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0); |
| + if (!ret->ToObject(&obj)) return ret; |
| // Make sure the properties are normalized before removing the entry. |
| - return DeleteNormalizedProperty(name, mode); |
| + ret = DeleteNormalizedProperty(name, mode); |
| + } |
| + |
| + if (FLAG_harmony_observation && map()->is_observed() && !ret->IsFailure()) { |
| + this->NotifyObservers("deleted", name, oldValue); |
| } |
| + |
| + return ret; |
|
rafaelw
2012/10/31 14:44:31
ditto
rossberg
2012/11/05 17:11:08
Done.
|
| } |
| @@ -4612,10 +4694,26 @@ MaybeObject* JSObject::DefineAccessor(String* name, |
| if (!CanSetCallback(name)) return isolate->heap()->undefined_value(); |
| + Object* oldValue = isolate->heap()->the_hole_value(); |
| + bool preexists; |
| + if (FLAG_harmony_observation && map()->is_observed()) { |
| + LookupResult result(isolate); |
| + LocalLookup(name, &result); |
| + preexists = result.IsFound(); |
| + // TODO(observe): save oldValue |
| + } |
| + |
| uint32_t index = 0; |
| - return name->AsArrayIndex(&index) ? |
| + MaybeObject* ret = name->AsArrayIndex(&index) ? |
| DefineElementAccessor(index, getter, setter, attributes) : |
| DefinePropertyAccessor(name, getter, setter, attributes); |
| + |
| + if (FLAG_harmony_observation && map()->is_observed() && !ret->IsFailure()) { |
| + const char* type = preexists ? "reconfigured" : "new"; |
| + this->NotifyObservers(type, name, oldValue); |
| + } |
| + |
| + return ret; |
|
rafaelw
2012/10/31 14:44:31
ditto
rossberg
2012/11/05 17:11:08
Done.
|
| } |
| @@ -4896,7 +4994,6 @@ MaybeObject* Map::RawCopy(int instance_size) { |
| result->set_constructor(constructor()); |
| result->set_bit_field(bit_field()); |
| result->set_bit_field2(bit_field2()); |
| - result->set_bit_field3(bit_field3()); |
| int new_bit_field3 = bit_field3(); |
| new_bit_field3 = OwnsDescriptors::update(new_bit_field3, true); |
| new_bit_field3 = NumberOfOwnDescriptorsBits::update(new_bit_field3, 0); |
| @@ -7519,6 +7616,8 @@ bool Map::EquivalentToForNormalization(Map* other, |
| instance_type() == other->instance_type() && |
| bit_field() == other->bit_field() && |
| bit_field2() == other->bit_field2() && |
| + (bit_field3() & IsObserved::kMask) == |
| + (other->bit_field3() & IsObserved::kMask) && |
|
Toon Verwaest
2012/11/05 13:33:22
Can we just use is_observed() == other->is_observe
rossberg
2012/11/05 17:11:08
Done.
|
| function_with_prototype() == other->function_with_prototype(); |
| } |