| Index: src/objects.cc
|
| diff --git a/src/objects.cc b/src/objects.cc
|
| index 906f7feef6bb30f5bf9f3787e568f1b7f9405a30..28c6063110f22ae824cfad0601a0a403f513cf03 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* result;
|
| if (extensibility_check == PERFORM_EXTENSIBILITY_CHECK &&
|
| !map_of_this->is_extensible()) {
|
| if (strict_mode == kNonStrictMode) {
|
| @@ -1688,28 +1689,55 @@ 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);
|
| + result = AddConstantFunctionProperty(name,
|
| + JSFunction::cast(value),
|
| + attributes);
|
| } else {
|
| - return AddFastProperty(name, value, attributes, store_mode);
|
| + result = 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;
|
| - }
|
| + MaybeObject* maybe = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
|
| + if (!maybe->To(&obj)) return maybe;
|
| + result = AddSlowProperty(name, value, attributes);
|
| }
|
| + } else {
|
| + result = AddSlowProperty(name, value, attributes);
|
| + }
|
| +
|
| + Handle<Object> hresult;
|
| + if (!result->ToHandle(&hresult)) return result;
|
| +
|
| + if (FLAG_harmony_observation && map()->is_observed()) {
|
| + this->EnqueueChangeRecord(
|
| + "new", handle(name), handle(heap->the_hole_value()));
|
| }
|
| - return AddSlowProperty(name, value, attributes);
|
| +
|
| + return *hresult;
|
| +}
|
| +
|
| +
|
| +void JSObject::EnqueueChangeRecord(
|
| + const char* type_str, Handle<String> name, Handle<Object> old_value) {
|
| + Isolate* isolate = GetIsolate();
|
| + HandleScope scope;
|
| + Handle<String> type = isolate->factory()->LookupAsciiSymbol(type_str);
|
| + Handle<JSObject> object(this);
|
| + Handle<Object> args[] = { type, object, name, old_value };
|
| + bool threw;
|
| + Execution::Call(Handle<JSFunction>(isolate->observers_notify_change()),
|
| + Handle<Object>(isolate->heap()->undefined_value()),
|
| + old_value->IsTheHole() ? 3 : 4, args,
|
| + &threw);
|
| + ASSERT(!threw);
|
| }
|
|
|
|
|
| @@ -2802,7 +2830,7 @@ void JSObject::AddFastPropertyUsingMap(Handle<JSObject> object,
|
| }
|
|
|
|
|
| -MaybeObject* JSObject::SetPropertyForResult(LookupResult* result,
|
| +MaybeObject* JSObject::SetPropertyForResult(LookupResult* lookup,
|
| String* name_raw,
|
| Object* value_raw,
|
| PropertyAttributes attributes,
|
| @@ -2829,7 +2857,7 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* result,
|
| if (IsAccessCheckNeeded()) {
|
| if (!heap->isolate()->MayNamedAccess(this, name_raw, v8::ACCESS_SET)) {
|
| return SetPropertyWithFailedAccessCheck(
|
| - result, name_raw, value_raw, true, strict_mode);
|
| + lookup, name_raw, value_raw, true, strict_mode);
|
| }
|
| }
|
|
|
| @@ -2838,7 +2866,7 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* result,
|
| if (proto->IsNull()) return value_raw;
|
| ASSERT(proto->IsJSGlobalObject());
|
| return JSObject::cast(proto)->SetPropertyForResult(
|
| - result, name_raw, value_raw, attributes, strict_mode, store_mode);
|
| + lookup, name_raw, value_raw, attributes, strict_mode, store_mode);
|
| }
|
|
|
| // From this point on everything needs to be handlified, because
|
| @@ -2848,19 +2876,20 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* result,
|
| Handle<String> name(name_raw);
|
| Handle<Object> value(value_raw);
|
|
|
| - if (!result->IsProperty() && !self->IsJSContextExtensionObject()) {
|
| + if (!lookup->IsProperty() && !self->IsJSContextExtensionObject()) {
|
| bool done = false;
|
| MaybeObject* result_object = self->SetPropertyViaPrototypes(
|
| *name, *value, attributes, strict_mode, &done);
|
| if (done) return result_object;
|
| }
|
|
|
| - if (!result->IsFound()) {
|
| + if (!lookup->IsFound()) {
|
| // Neither properties nor transitions found.
|
| return self->AddProperty(
|
| *name, *value, attributes, strict_mode, store_mode);
|
| }
|
| - if (result->IsProperty() && result->IsReadOnly()) {
|
| +
|
| + if (lookup->IsProperty() && lookup->IsReadOnly()) {
|
| if (strict_mode == kStrictMode) {
|
| Handle<Object> args[] = { name, self };
|
| return heap->isolate()->Throw(*heap->isolate()->factory()->NewTypeError(
|
| @@ -2870,34 +2899,44 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* result,
|
| }
|
| }
|
|
|
| + Handle<Object> old_value(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.
|
| - switch (result->type()) {
|
| + MaybeObject* result = *value;
|
| + switch (lookup->type()) {
|
| case NORMAL:
|
| - return self->SetNormalizedProperty(result, *value);
|
| + result = self->SetNormalizedProperty(lookup, *value);
|
| + break;
|
| case FIELD:
|
| - return self->FastPropertyAtPut(result->GetFieldIndex(), *value);
|
| + result = self->FastPropertyAtPut(lookup->GetFieldIndex(), *value);
|
| + break;
|
| case CONSTANT_FUNCTION:
|
| // Only replace the function if necessary.
|
| - if (*value == result->GetConstantFunction()) return *value;
|
| + if (*value == lookup->GetConstantFunction()) return *value;
|
| // Preserve the attributes of this existing property.
|
| - attributes = result->GetAttributes();
|
| - return self->ConvertDescriptorToField(*name, *value, attributes);
|
| + attributes = lookup->GetAttributes();
|
| + result = self->ConvertDescriptorToField(*name, *value, attributes);
|
| + break;
|
| case CALLBACKS: {
|
| - Object* callback_object = result->GetCallbackObject();
|
| + Object* callback_object = lookup->GetCallbackObject();
|
| return self->SetPropertyWithCallback(callback_object,
|
| *name,
|
| *value,
|
| - result->holder(),
|
| + lookup->holder(),
|
| strict_mode);
|
| }
|
| case INTERCEPTOR:
|
| - return self->SetPropertyWithInterceptor(*name,
|
| - *value,
|
| - attributes,
|
| - strict_mode);
|
| + result = self->SetPropertyWithInterceptor(*name,
|
| + *value,
|
| + attributes,
|
| + strict_mode);
|
| + break;
|
| case TRANSITION: {
|
| - Map* transition_map = result->GetTransitionTarget();
|
| + Map* transition_map = lookup->GetTransitionTarget();
|
| int descriptor = transition_map->LastAdded();
|
|
|
| DescriptorArray* descriptors = transition_map->instance_descriptors();
|
| @@ -2906,37 +2945,46 @@ 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);
|
| + result = self->AddFastPropertyUsingMap(transition_map,
|
| + *name,
|
| + *value,
|
| + field_index);
|
| + } else {
|
| + result = 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;
|
| + result = 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);
|
| + result = constant_function;
|
| + } else {
|
| + // Otherwise, replace with a map transition to a new map with a FIELD,
|
| + // even if the value is a constant function.
|
| + result = ConvertTransitionToMapTransition(
|
| + lookup->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;
|
| +
|
| + Handle<Object> hresult;
|
| + if (!result->ToHandle(&hresult)) return result;
|
| +
|
| + if (FLAG_harmony_observation && map()->is_observed()) {
|
| + this->EnqueueChangeRecord("updated", name, old_value);
|
| + }
|
| +
|
| + return *hresult;
|
| }
|
|
|
|
|
| @@ -2969,13 +3017,13 @@ MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes(
|
| // interceptor calls.
|
| AssertNoContextChange ncc;
|
| Isolate* isolate = GetIsolate();
|
| - LookupResult result(isolate);
|
| - LocalLookup(name, &result);
|
| - if (!result.IsFound()) map()->LookupTransition(this, name, &result);
|
| + LookupResult lookup(isolate);
|
| + LocalLookup(name, &lookup);
|
| + if (!lookup.IsFound()) map()->LookupTransition(this, name, &lookup);
|
| // Check access rights if needed.
|
| if (IsAccessCheckNeeded()) {
|
| if (!isolate->MayNamedAccess(this, name, v8::ACCESS_SET)) {
|
| - return SetPropertyWithFailedAccessCheck(&result,
|
| + return SetPropertyWithFailedAccessCheck(&lookup,
|
| name,
|
| value,
|
| false,
|
| @@ -2994,31 +3042,41 @@ MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes(
|
| }
|
|
|
| // Check for accessor in prototype chain removed here in clone.
|
| - if (!result.IsFound()) {
|
| + if (!lookup.IsFound()) {
|
| // Neither properties nor transitions found.
|
| return AddProperty(name, value, attributes, kNonStrictMode);
|
| }
|
|
|
| + Handle<Object> old_value(isolate->heap()->the_hole_value());
|
| + if (FLAG_harmony_observation && map()->is_observed()) {
|
| + // TODO(observe): save oldValue
|
| + }
|
| +
|
| // Check of IsReadOnly removed from here in clone.
|
| - switch (result.type()) {
|
| + MaybeObject* result = value;
|
| + switch (lookup.type()) {
|
| case NORMAL: {
|
| PropertyDetails details = PropertyDetails(attributes, NORMAL);
|
| - return SetNormalizedProperty(name, value, details);
|
| + result = SetNormalizedProperty(name, value, details);
|
| + break;
|
| }
|
| case FIELD:
|
| - return FastPropertyAtPut(result.GetFieldIndex(), value);
|
| + result = FastPropertyAtPut(lookup.GetFieldIndex(), value);
|
| + break;
|
| case CONSTANT_FUNCTION:
|
| // Only replace the function if necessary.
|
| - if (value == result.GetConstantFunction()) return value;
|
| + if (value == lookup.GetConstantFunction()) return value;
|
| // Preserve the attributes of this existing property.
|
| - attributes = result.GetAttributes();
|
| - return ConvertDescriptorToField(name, value, attributes);
|
| + attributes = lookup.GetAttributes();
|
| + result = ConvertDescriptorToField(name, value, attributes);
|
| + break;
|
| case CALLBACKS:
|
| case INTERCEPTOR:
|
| // Override callback in clone
|
| - return ConvertDescriptorToField(name, value, attributes);
|
| + result = ConvertDescriptorToField(name, value, attributes);
|
| + break;
|
| case TRANSITION: {
|
| - Map* transition_map = result.GetTransitionTarget();
|
| + Map* transition_map = lookup.GetTransitionTarget();
|
| int descriptor = transition_map->LastAdded();
|
|
|
| DescriptorArray* descriptors = transition_map->instance_descriptors();
|
| @@ -3027,29 +3085,40 @@ 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);
|
| + result = AddFastPropertyUsingMap(transition_map,
|
| + name,
|
| + value,
|
| + field_index);
|
| + } else {
|
| + result = ConvertDescriptorToField(name, value, attributes);
|
| }
|
| - return ConvertDescriptorToField(name, value, attributes);
|
| } else if (details.type() == CALLBACKS) {
|
| - return ConvertDescriptorToField(name, value, attributes);
|
| - }
|
| -
|
| - ASSERT(details.type() == CONSTANT_FUNCTION);
|
| + result = 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.
|
| + result = ConvertTransitionToMapTransition(
|
| + lookup.GetTransitionIndex(), name, value, attributes);
|
| + }
|
| + break;
|
| }
|
| case HANDLER:
|
| case NONEXISTENT:
|
| UNREACHABLE();
|
| }
|
| - UNREACHABLE(); // keep the compiler happy
|
| - return value;
|
| +
|
| + Handle<Object> hresult;
|
| + if (!result->ToHandle(&hresult)) return result;
|
| +
|
| + if (FLAG_harmony_observation && map()->is_observed()) {
|
| + const char* type =
|
| + attributes == lookup.GetAttributes() ? "updated" : "reconfigured";
|
| + this->EnqueueChangeRecord(type, handle(name), old_value);
|
| + }
|
| +
|
| + return *hresult;
|
| }
|
|
|
|
|
| @@ -3953,38 +4022,55 @@ 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 lookup(isolate);
|
| + LocalLookup(name, &lookup);
|
| + if (!lookup.IsFound()) return isolate->heap()->true_value();
|
| + // Ignore attributes if forcing a deletion.
|
| + if (lookup.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();
|
| + }
|
| +
|
| + HandleScope scope(isolate);
|
| + Handle<Object> old_value(isolate->heap()->the_hole_value());
|
| + if (FLAG_harmony_observation && map()->is_observed()) {
|
| + // TODO(observe): save oldValue
|
| + }
|
| + MaybeObject* result;
|
| +
|
| + // Check for interceptor.
|
| + if (lookup.IsInterceptor()) {
|
| + // Skip interceptor if forcing a deletion.
|
| + if (mode == FORCE_DELETION) {
|
| + result = DeletePropertyPostInterceptor(name, mode);
|
| + } else {
|
| + result = 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;
|
| - }
|
| + result = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
|
| + if (!result->ToObject(&obj)) return result;
|
| // Make sure the properties are normalized before removing the entry.
|
| - return DeleteNormalizedProperty(name, mode);
|
| + result = DeleteNormalizedProperty(name, mode);
|
| }
|
| +
|
| + Handle<Object> hresult;
|
| + if (!result->ToHandle(&hresult)) return result;
|
| +
|
| + if (FLAG_harmony_observation && map()->is_observed()) {
|
| + this->EnqueueChangeRecord("deleted", handle(name), old_value);
|
| + }
|
| +
|
| + return *hresult;
|
| }
|
|
|
|
|
| @@ -4612,10 +4698,29 @@ MaybeObject* JSObject::DefineAccessor(String* name,
|
|
|
| if (!CanSetCallback(name)) return isolate->heap()->undefined_value();
|
|
|
| + Handle<Object> old_value(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) ?
|
| - DefineElementAccessor(index, getter, setter, attributes) :
|
| - DefinePropertyAccessor(name, getter, setter, attributes);
|
| + MaybeObject* result = name->AsArrayIndex(&index)
|
| + ? DefineElementAccessor(index, getter, setter, attributes)
|
| + : DefinePropertyAccessor(name, getter, setter, attributes);
|
| +
|
| + Handle<Object> hresult;
|
| + if (!result->ToHandle(&hresult)) return result;
|
| +
|
| + if (FLAG_harmony_observation && map()->is_observed()) {
|
| + const char* type = preexists ? "reconfigured" : "new";
|
| + this->EnqueueChangeRecord(type, handle(name), old_value);
|
| + }
|
| +
|
| + return *hresult;
|
| }
|
|
|
|
|
| @@ -4896,7 +5001,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 +7623,7 @@ bool Map::EquivalentToForNormalization(Map* other,
|
| instance_type() == other->instance_type() &&
|
| bit_field() == other->bit_field() &&
|
| bit_field2() == other->bit_field2() &&
|
| + is_observed() == other->is_observed() &&
|
| function_with_prototype() == other->function_with_prototype();
|
| }
|
|
|
|
|