Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(540)

Unified Diff: src/objects.cc

Issue 11347037: Object.observe: generate change records for named properties. (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Addressing comments. Created 8 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/objects.h ('k') | src/objects-inl.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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();
}
« no previous file with comments | « src/objects.h ('k') | src/objects-inl.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698