| Index: src/objects.cc
|
| diff --git a/src/objects.cc b/src/objects.cc
|
| index 3ecbf22e4ded3636444716ddf76332023bc2e122..c435917834f9825996ef170f7c0b0a1a3bba7da5 100644
|
| --- a/src/objects.cc
|
| +++ b/src/objects.cc
|
| @@ -2761,12 +2761,14 @@ MUST_USE_RESULT PropertyAttributes JSProxy::GetPropertyAttributeWithHandler(
|
|
|
|
|
| MUST_USE_RESULT PropertyAttributes JSProxy::GetElementAttributeWithHandler(
|
| - JSReceiver* receiver,
|
| + JSReceiver* receiver_raw,
|
| uint32_t index) {
|
| Isolate* isolate = GetIsolate();
|
| HandleScope scope(isolate);
|
| + Handle<JSProxy> proxy(this);
|
| + Handle<JSReceiver> receiver(receiver_raw);
|
| Handle<String> name = isolate->factory()->Uint32ToString(index);
|
| - return GetPropertyAttributeWithHandler(receiver, *name);
|
| + return proxy->GetPropertyAttributeWithHandler(*receiver, *name);
|
| }
|
|
|
|
|
| @@ -2900,8 +2902,21 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* lookup,
|
| }
|
|
|
| Handle<Object> old_value(heap->the_hole_value());
|
| + PropertyAttributes old_attributes;
|
| if (FLAG_harmony_observation && map()->is_observed()) {
|
| - // TODO(observe): save oldValue
|
| + switch (lookup->type()) {
|
| + case NORMAL:
|
| + case FIELD:
|
| + case CONSTANT_FUNCTION:
|
| + case INTERCEPTOR:
|
| + old_value =
|
| + Object::GetProperty(self, self, lookup, name, &old_attributes);
|
| + case CALLBACKS:
|
| + case TRANSITION:
|
| + case HANDLER:
|
| + case NONEXISTENT:
|
| + break;
|
| + }
|
| }
|
|
|
| // This is a real property that is not read-only, or it is a
|
| @@ -2981,7 +2996,18 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* lookup,
|
| if (!result->ToHandle(&hresult)) return result;
|
|
|
| if (FLAG_harmony_observation && map()->is_observed()) {
|
| - this->EnqueueChangeRecord("updated", name, old_value);
|
| + PropertyAttributes new_attributes = self->GetLocalPropertyAttribute(*name);
|
| + if (lookup->IsTransition()) {
|
| + self->EnqueueChangeRecord("new", name, old_value);
|
| + } else if (new_attributes != old_attributes) {
|
| + self->EnqueueChangeRecord("reconfigured", name, old_value);
|
| + } else {
|
| + PropertyAttributes attributes;
|
| + Handle<Object> new_value =
|
| + Object::GetProperty(self, self, lookup, name, &attributes);
|
| + if (!new_value->SameValue(*old_value))
|
| + self->EnqueueChangeRecord("updated", name, old_value);
|
| + }
|
| }
|
|
|
| return *hresult;
|
| @@ -3010,22 +3036,22 @@ Handle<Object> JSObject::SetLocalPropertyIgnoreAttributes(
|
|
|
|
|
| MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes(
|
| - String* name,
|
| - Object* value,
|
| + String* name_raw,
|
| + Object* value_raw,
|
| PropertyAttributes attributes) {
|
| // Make sure that the top context does not change when doing callbacks or
|
| // interceptor calls.
|
| AssertNoContextChange ncc;
|
| Isolate* isolate = GetIsolate();
|
| LookupResult lookup(isolate);
|
| - LocalLookup(name, &lookup);
|
| - if (!lookup.IsFound()) map()->LookupTransition(this, name, &lookup);
|
| + LocalLookup(name_raw, &lookup);
|
| + if (!lookup.IsFound()) map()->LookupTransition(this, name_raw, &lookup);
|
| // Check access rights if needed.
|
| if (IsAccessCheckNeeded()) {
|
| - if (!isolate->MayNamedAccess(this, name, v8::ACCESS_SET)) {
|
| + if (!isolate->MayNamedAccess(this, name_raw, v8::ACCESS_SET)) {
|
| return SetPropertyWithFailedAccessCheck(&lookup,
|
| - name,
|
| - value,
|
| + name_raw,
|
| + value_raw,
|
| false,
|
| kNonStrictMode);
|
| }
|
| @@ -3033,47 +3059,67 @@ MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes(
|
|
|
| if (IsJSGlobalProxy()) {
|
| Object* proto = GetPrototype();
|
| - if (proto->IsNull()) return value;
|
| + if (proto->IsNull()) return value_raw;
|
| ASSERT(proto->IsJSGlobalObject());
|
| return JSObject::cast(proto)->SetLocalPropertyIgnoreAttributes(
|
| - name,
|
| - value,
|
| + name_raw,
|
| + value_raw,
|
| attributes);
|
| }
|
|
|
| // Check for accessor in prototype chain removed here in clone.
|
| if (!lookup.IsFound()) {
|
| // Neither properties nor transitions found.
|
| - return AddProperty(name, value, attributes, kNonStrictMode);
|
| + return AddProperty(name_raw, value_raw, attributes, kNonStrictMode);
|
| }
|
|
|
| + // From this point on everything needs to be handlified.
|
| + HandleScope scope(GetIsolate());
|
| + Handle<JSObject> self(this);
|
| + Handle<String> name(name_raw);
|
| + Handle<Object> value(value_raw);
|
| +
|
| Handle<Object> old_value(isolate->heap()->the_hole_value());
|
| + PropertyAttributes old_attributes;
|
| if (FLAG_harmony_observation && map()->is_observed()) {
|
| - // TODO(observe): save oldValue
|
| + switch (lookup.type()) {
|
| + case NORMAL:
|
| + case FIELD:
|
| + case CONSTANT_FUNCTION:
|
| + case INTERCEPTOR:
|
| + old_value =
|
| + Object::GetProperty(self, self, &lookup, name, &old_attributes);
|
| + case CALLBACKS:
|
| + case TRANSITION:
|
| + case HANDLER:
|
| + case NONEXISTENT:
|
| + break;
|
| + }
|
| }
|
|
|
| // Check of IsReadOnly removed from here in clone.
|
| - MaybeObject* result = value;
|
| + MaybeObject* result = *value;
|
| switch (lookup.type()) {
|
| case NORMAL: {
|
| PropertyDetails details = PropertyDetails(attributes, NORMAL);
|
| - result = SetNormalizedProperty(name, value, details);
|
| + result = self->SetNormalizedProperty(*name, *value, details);
|
| break;
|
| }
|
| case FIELD:
|
| - result = FastPropertyAtPut(lookup.GetFieldIndex(), value);
|
| + result = self->FastPropertyAtPut(lookup.GetFieldIndex(), *value);
|
| break;
|
| case CONSTANT_FUNCTION:
|
| // Only replace the function if necessary.
|
| - if (value == lookup.GetConstantFunction()) return value;
|
| - // Preserve the attributes of this existing property.
|
| - attributes = lookup.GetAttributes();
|
| - result = ConvertDescriptorToField(name, value, attributes);
|
| + if (*value != lookup.GetConstantFunction()) {
|
| + // Preserve the attributes of this existing property.
|
| + attributes = lookup.GetAttributes();
|
| + result = self->ConvertDescriptorToField(*name, *value, attributes);
|
| + }
|
| break;
|
| case CALLBACKS:
|
| case INTERCEPTOR:
|
| // Override callback in clone
|
| - result = ConvertDescriptorToField(name, value, attributes);
|
| + result = self->ConvertDescriptorToField(*name, *value, attributes);
|
| break;
|
| case TRANSITION: {
|
| Map* transition_map = lookup.GetTransitionTarget();
|
| @@ -3085,22 +3131,20 @@ MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes(
|
| if (details.type() == FIELD) {
|
| if (attributes == details.attributes()) {
|
| int field_index = descriptors->GetFieldIndex(descriptor);
|
| - result = AddFastPropertyUsingMap(transition_map,
|
| - name,
|
| - value,
|
| - field_index);
|
| + result = self->AddFastPropertyUsingMap(
|
| + transition_map, *name, *value, field_index);
|
| } else {
|
| - result = ConvertDescriptorToField(name, value, attributes);
|
| + result = self->ConvertDescriptorToField(*name, *value, attributes);
|
| }
|
| } else if (details.type() == CALLBACKS) {
|
| - result = ConvertDescriptorToField(name, value, attributes);
|
| + result = self->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.
|
| - result = ConvertTransitionToMapTransition(
|
| - lookup.GetTransitionIndex(), name, value, attributes);
|
| + result = self->ConvertTransitionToMapTransition(
|
| + lookup.GetTransitionIndex(), *name, *value, attributes);
|
| }
|
| break;
|
| }
|
| @@ -3113,9 +3157,17 @@ MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes(
|
| 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);
|
| + PropertyAttributes new_attributes = self->GetLocalPropertyAttribute(*name);
|
| + if (lookup.IsTransition()) {
|
| + self->EnqueueChangeRecord("new", name, old_value);
|
| + } else if (new_attributes != old_attributes) {
|
| + self->EnqueueChangeRecord("reconfigured", name, old_value);
|
| + } else {
|
| + Handle<Object> new_value =
|
| + Object::GetProperty(self, self, &lookup, name, &old_attributes);
|
| + if (!new_value->SameValue(*old_value))
|
| + self->EnqueueChangeRecord("updated", name, old_value);
|
| + }
|
| }
|
|
|
| return *hresult;
|
| @@ -3199,42 +3251,43 @@ PropertyAttributes JSReceiver::GetPropertyAttributeWithReceiver(
|
| String* key) {
|
| uint32_t index = 0;
|
| if (IsJSObject() && key->AsArrayIndex(&index)) {
|
| - return JSObject::cast(this)->HasElementWithReceiver(receiver, index)
|
| - ? NONE : ABSENT;
|
| + return JSObject::cast(this)->GetElementAttributeWithReceiver(
|
| + receiver, index, true);
|
| }
|
| // Named property.
|
| - LookupResult result(GetIsolate());
|
| - Lookup(key, &result);
|
| - return GetPropertyAttribute(receiver, &result, key, true);
|
| + LookupResult lookup(GetIsolate());
|
| + Lookup(key, &lookup);
|
| + return GetPropertyAttributeForResult(receiver, &lookup, key, true);
|
| }
|
|
|
|
|
| -PropertyAttributes JSReceiver::GetPropertyAttribute(JSReceiver* receiver,
|
| - LookupResult* result,
|
| - String* name,
|
| - bool continue_search) {
|
| +PropertyAttributes JSReceiver::GetPropertyAttributeForResult(
|
| + JSReceiver* receiver,
|
| + LookupResult* lookup,
|
| + String* name,
|
| + bool continue_search) {
|
| // Check access rights if needed.
|
| if (IsAccessCheckNeeded()) {
|
| JSObject* this_obj = JSObject::cast(this);
|
| Heap* heap = GetHeap();
|
| if (!heap->isolate()->MayNamedAccess(this_obj, name, v8::ACCESS_HAS)) {
|
| return this_obj->GetPropertyAttributeWithFailedAccessCheck(
|
| - receiver, result, name, continue_search);
|
| + receiver, lookup, name, continue_search);
|
| }
|
| }
|
| - if (result->IsFound()) {
|
| - switch (result->type()) {
|
| + if (lookup->IsFound()) {
|
| + switch (lookup->type()) {
|
| case NORMAL: // fall through
|
| case FIELD:
|
| case CONSTANT_FUNCTION:
|
| case CALLBACKS:
|
| - return result->GetAttributes();
|
| + return lookup->GetAttributes();
|
| case HANDLER: {
|
| - return JSProxy::cast(result->proxy())->GetPropertyAttributeWithHandler(
|
| + return JSProxy::cast(lookup->proxy())->GetPropertyAttributeWithHandler(
|
| receiver, name);
|
| }
|
| case INTERCEPTOR:
|
| - return result->holder()->GetPropertyAttributeWithInterceptor(
|
| + return lookup->holder()->GetPropertyAttributeWithInterceptor(
|
| JSObject::cast(receiver), name, continue_search);
|
| case TRANSITION:
|
| case NONEXISTENT:
|
| @@ -3249,13 +3302,113 @@ PropertyAttributes JSReceiver::GetLocalPropertyAttribute(String* name) {
|
| // Check whether the name is an array index.
|
| uint32_t index = 0;
|
| if (IsJSObject() && name->AsArrayIndex(&index)) {
|
| - if (JSObject::cast(this)->HasLocalElement(index)) return NONE;
|
| - return ABSENT;
|
| + return GetLocalElementAttribute(index);
|
| }
|
| // Named property.
|
| - LookupResult result(GetIsolate());
|
| - LocalLookup(name, &result);
|
| - return GetPropertyAttribute(this, &result, name, false);
|
| + LookupResult lookup(GetIsolate());
|
| + LocalLookup(name, &lookup);
|
| + return GetPropertyAttributeForResult(this, &lookup, name, false);
|
| +}
|
| +
|
| +
|
| +PropertyAttributes JSObject::GetElementAttributeWithReceiver(
|
| + JSReceiver* receiver, uint32_t index, bool continue_search) {
|
| + Isolate* isolate = GetIsolate();
|
| +
|
| + // Check access rights if needed.
|
| + if (IsAccessCheckNeeded()) {
|
| + if (!isolate->MayIndexedAccess(this, index, v8::ACCESS_HAS)) {
|
| + isolate->ReportFailedAccessCheck(this, v8::ACCESS_HAS);
|
| + return ABSENT;
|
| + }
|
| + }
|
| +
|
| + if (IsJSGlobalProxy()) {
|
| + Object* proto = GetPrototype();
|
| + if (proto->IsNull()) return ABSENT;
|
| + ASSERT(proto->IsJSGlobalObject());
|
| + return JSReceiver::cast(proto)->GetElementAttributeWithReceiver(
|
| + receiver, index, continue_search);
|
| + }
|
| +
|
| + // Check for lookup interceptor except when bootstrapping.
|
| + if (HasIndexedInterceptor() && !isolate->bootstrapper()->IsActive()) {
|
| + return GetElementAttributeWithInterceptor(receiver, index, continue_search);
|
| + }
|
| +
|
| + return GetElementAttributeWithoutInterceptor(
|
| + receiver, index, continue_search);
|
| +}
|
| +
|
| +
|
| +PropertyAttributes JSObject::GetElementAttributeWithInterceptor(
|
| + JSReceiver* receiver, uint32_t index, bool continue_search) {
|
| + Isolate* isolate = GetIsolate();
|
| + // Make sure that the top context does not change when doing
|
| + // callbacks or interceptor calls.
|
| + AssertNoContextChange ncc;
|
| + HandleScope scope(isolate);
|
| + Handle<InterceptorInfo> interceptor(GetIndexedInterceptor());
|
| + Handle<JSReceiver> hreceiver(receiver);
|
| + Handle<JSObject> holder(this);
|
| + CustomArguments args(isolate, interceptor->data(), receiver, this);
|
| + v8::AccessorInfo info(args.end());
|
| + if (!interceptor->query()->IsUndefined()) {
|
| + v8::IndexedPropertyQuery query =
|
| + v8::ToCData<v8::IndexedPropertyQuery>(interceptor->query());
|
| + LOG(isolate,
|
| + ApiIndexedPropertyAccess("interceptor-indexed-has", this, index));
|
| + v8::Handle<v8::Integer> result;
|
| + {
|
| + // Leaving JavaScript.
|
| + VMState state(isolate, EXTERNAL);
|
| + result = query(index, info);
|
| + }
|
| + if (!result.IsEmpty())
|
| + return static_cast<PropertyAttributes>(result->Int32Value());
|
| + } else if (!interceptor->getter()->IsUndefined()) {
|
| + v8::IndexedPropertyGetter getter =
|
| + v8::ToCData<v8::IndexedPropertyGetter>(interceptor->getter());
|
| + LOG(isolate,
|
| + ApiIndexedPropertyAccess("interceptor-indexed-get-has", this, index));
|
| + v8::Handle<v8::Value> result;
|
| + {
|
| + // Leaving JavaScript.
|
| + VMState state(isolate, EXTERNAL);
|
| + result = getter(index, info);
|
| + }
|
| + if (!result.IsEmpty()) return DONT_ENUM;
|
| + }
|
| +
|
| + return holder->GetElementAttributeWithoutInterceptor(
|
| + *hreceiver, index, continue_search);
|
| +}
|
| +
|
| +
|
| +PropertyAttributes JSObject::GetElementAttributeWithoutInterceptor(
|
| + JSReceiver* receiver, uint32_t index, bool continue_search) {
|
| + Isolate* isolate = GetIsolate();
|
| + HandleScope scope(isolate);
|
| + Handle<JSReceiver> hreceiver(receiver);
|
| + Handle<JSObject> holder(this);
|
| + PropertyAttributes attr = holder->GetElementsAccessor()->GetAttributes(
|
| + *hreceiver, *holder, index);
|
| + if (attr != ABSENT) return attr;
|
| +
|
| + if (holder->IsStringObjectWithCharacterAt(index)) {
|
| + return static_cast<PropertyAttributes>(READ_ONLY | DONT_DELETE);
|
| + }
|
| +
|
| + if (!continue_search) return ABSENT;
|
| +
|
| + Object* pt = holder->GetPrototype();
|
| + if (pt->IsJSProxy()) {
|
| + // We need to follow the spec and simulate a call to [[GetOwnProperty]].
|
| + return JSProxy::cast(pt)->GetElementAttributeWithHandler(*hreceiver, index);
|
| + }
|
| + if (pt->IsNull()) return ABSENT;
|
| + return JSObject::cast(pt)->GetElementAttributeWithReceiver(
|
| + *hreceiver, index, true);
|
| }
|
|
|
|
|
| @@ -3980,15 +4133,39 @@ MaybeObject* JSObject::DeleteElement(uint32_t index, DeleteMode mode) {
|
| return JSGlobalObject::cast(proto)->DeleteElement(index, mode);
|
| }
|
|
|
| - if (HasIndexedInterceptor()) {
|
| - // Skip interceptor if forcing deletion.
|
| - if (mode != FORCE_DELETION) {
|
| - return DeleteElementWithInterceptor(index);
|
| + // From this point on everything needs to be handlified.
|
| + HandleScope scope(isolate);
|
| + Handle<JSObject> self(this);
|
| +
|
| + Handle<String> name;
|
| + Handle<Object> old_value(isolate->heap()->the_hole_value());
|
| + bool preexists = false;
|
| + if (FLAG_harmony_observation && map()->is_observed()) {
|
| + name = isolate->factory()->Uint32ToString(index);
|
| + preexists = self->HasLocalElement(index);
|
| + if (preexists) {
|
| + // TODO(observe): only read & set old_value if it's not an accessor
|
| + old_value = Object::GetElement(self, index);
|
| }
|
| - mode = JSReceiver::FORCE_DELETION;
|
| }
|
|
|
| - return GetElementsAccessor()->Delete(this, index, mode);
|
| + MaybeObject* result;
|
| + // Skip interceptor if forcing deletion.
|
| + if (self->HasIndexedInterceptor() && mode != FORCE_DELETION) {
|
| + result = self->DeleteElementWithInterceptor(index);
|
| + } else {
|
| + result = self->GetElementsAccessor()->Delete(*self, index, mode);
|
| + }
|
| +
|
| + Handle<Object> hresult;
|
| + if (!result->ToHandle(&hresult)) return result;
|
| +
|
| + if (FLAG_harmony_observation && map()->is_observed()) {
|
| + if (preexists && !self->HasLocalElement(index))
|
| + self->EnqueueChangeRecord("deleted", name, old_value);
|
| + }
|
| +
|
| + return *hresult;
|
| }
|
|
|
|
|
| @@ -4039,10 +4216,28 @@ MaybeObject* JSObject::DeleteProperty(String* name, DeleteMode mode) {
|
| return isolate->heap()->false_value();
|
| }
|
|
|
| + // From this point on everything needs to be handlified.
|
| HandleScope scope(isolate);
|
| + Handle<JSObject> self(this);
|
| + Handle<String> hname(name);
|
| +
|
| Handle<Object> old_value(isolate->heap()->the_hole_value());
|
| if (FLAG_harmony_observation && map()->is_observed()) {
|
| - // TODO(observe): save oldValue
|
| + switch (lookup.type()) {
|
| + case NORMAL:
|
| + case FIELD:
|
| + case CONSTANT_FUNCTION:
|
| + case INTERCEPTOR: {
|
| + PropertyAttributes old_attributes;
|
| + old_value =
|
| + Object::GetProperty(self, self, &lookup, hname, &old_attributes);
|
| + }
|
| + case CALLBACKS:
|
| + case TRANSITION:
|
| + case HANDLER:
|
| + case NONEXISTENT:
|
| + break;
|
| + }
|
| }
|
| MaybeObject* result;
|
|
|
| @@ -4050,24 +4245,25 @@ MaybeObject* JSObject::DeleteProperty(String* name, DeleteMode mode) {
|
| if (lookup.IsInterceptor()) {
|
| // Skip interceptor if forcing a deletion.
|
| if (mode == FORCE_DELETION) {
|
| - result = DeletePropertyPostInterceptor(name, mode);
|
| + result = self->DeletePropertyPostInterceptor(*hname, mode);
|
| } else {
|
| - result = DeletePropertyWithInterceptor(name);
|
| + result = self->DeletePropertyWithInterceptor(*hname);
|
| }
|
| } else {
|
| // Normalize object if needed.
|
| Object* obj;
|
| - result = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
|
| - if (!result->ToObject(&obj)) return result;
|
| + result = self->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
|
| + if (!result->To(&obj)) return result;
|
| // Make sure the properties are normalized before removing the entry.
|
| - result = DeleteNormalizedProperty(name, mode);
|
| + result = self->DeleteNormalizedProperty(*hname, mode);
|
| }
|
|
|
| Handle<Object> hresult;
|
| if (!result->ToHandle(&hresult)) return result;
|
|
|
| if (FLAG_harmony_observation && map()->is_observed()) {
|
| - this->EnqueueChangeRecord("deleted", handle(name), old_value);
|
| + if (!self->HasLocalProperty(*hname))
|
| + self->EnqueueChangeRecord("deleted", hname, old_value);
|
| }
|
|
|
| return *hresult;
|
| @@ -4669,14 +4865,14 @@ void JSObject::DefineAccessor(Handle<JSObject> object,
|
| object->DefineAccessor(*name, *getter, *setter, attributes));
|
| }
|
|
|
| -MaybeObject* JSObject::DefineAccessor(String* name,
|
| - Object* getter,
|
| - Object* setter,
|
| +MaybeObject* JSObject::DefineAccessor(String* name_raw,
|
| + Object* getter_raw,
|
| + Object* setter_raw,
|
| PropertyAttributes attributes) {
|
| Isolate* isolate = GetIsolate();
|
| // Check access rights if needed.
|
| if (IsAccessCheckNeeded() &&
|
| - !isolate->MayNamedAccess(this, name, v8::ACCESS_SET)) {
|
| + !isolate->MayNamedAccess(this, name_raw, v8::ACCESS_SET)) {
|
| isolate->ReportFailedAccessCheck(this, v8::ACCESS_SET);
|
| return isolate->heap()->undefined_value();
|
| }
|
| @@ -4686,7 +4882,7 @@ MaybeObject* JSObject::DefineAccessor(String* name,
|
| if (proto->IsNull()) return this;
|
| ASSERT(proto->IsJSGlobalObject());
|
| return JSObject::cast(proto)->DefineAccessor(
|
| - name, getter, setter, attributes);
|
| + name_raw, getter_raw, setter_raw, attributes);
|
| }
|
|
|
| // Make sure that the top context does not change when doing callbacks or
|
| @@ -4694,30 +4890,63 @@ MaybeObject* JSObject::DefineAccessor(String* name,
|
| AssertNoContextChange ncc;
|
|
|
| // Try to flatten before operating on the string.
|
| - name->TryFlatten();
|
| + name_raw->TryFlatten();
|
|
|
| - if (!CanSetCallback(name)) return isolate->heap()->undefined_value();
|
| + if (!CanSetCallback(name_raw)) return isolate->heap()->undefined_value();
|
| +
|
| + // From this point on everything needs to be handlified.
|
| + HandleScope scope(GetIsolate());
|
| + Handle<JSObject> self(this);
|
| + Handle<String> name(name_raw);
|
| + Handle<Object> getter(getter_raw);
|
| + Handle<Object> setter(setter_raw);
|
| +
|
| + uint32_t index = 0;
|
| + bool is_element = name->AsArrayIndex(&index);
|
|
|
| Handle<Object> old_value(isolate->heap()->the_hole_value());
|
| bool preexists = false;
|
| if (FLAG_harmony_observation && map()->is_observed()) {
|
| - LookupResult result(isolate);
|
| - LocalLookup(name, &result);
|
| - preexists = result.IsFound();
|
| - // TODO(observe): save oldValue
|
| + if (is_element) {
|
| + preexists = HasLocalElement(index);
|
| + if (preexists) {
|
| + // TODO(observe): distinguish the case where it's an accessor
|
| + old_value = Object::GetElement(self, index);
|
| + }
|
| + } else {
|
| + LookupResult lookup(isolate);
|
| + LocalLookup(*name, &lookup);
|
| + preexists = lookup.IsProperty();
|
| + if (preexists) {
|
| + switch (lookup.type()) {
|
| + case NORMAL:
|
| + case FIELD:
|
| + case CONSTANT_FUNCTION:
|
| + case INTERCEPTOR: {
|
| + PropertyAttributes old_attributes;
|
| + old_value =
|
| + Object::GetProperty(self, self, &lookup, name, &old_attributes);
|
| + }
|
| + case CALLBACKS:
|
| + case TRANSITION:
|
| + case HANDLER:
|
| + case NONEXISTENT:
|
| + break;
|
| + }
|
| + }
|
| + }
|
| }
|
|
|
| - uint32_t index = 0;
|
| - MaybeObject* result = name->AsArrayIndex(&index)
|
| - ? DefineElementAccessor(index, getter, setter, attributes)
|
| - : DefinePropertyAccessor(name, getter, setter, attributes);
|
| + MaybeObject* result = is_element ?
|
| + self->DefineElementAccessor(index, *getter, *setter, attributes) :
|
| + self->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);
|
| + self->EnqueueChangeRecord(type, name, old_value);
|
| }
|
|
|
| return *hresult;
|
| @@ -9212,64 +9441,7 @@ MaybeObject* JSObject::EnsureCanContainElements(Arguments* args,
|
| }
|
|
|
|
|
| -bool JSObject::HasElementWithInterceptor(JSReceiver* receiver, uint32_t index) {
|
| - Isolate* isolate = GetIsolate();
|
| - // Make sure that the top context does not change when doing
|
| - // callbacks or interceptor calls.
|
| - AssertNoContextChange ncc;
|
| - HandleScope scope(isolate);
|
| - Handle<InterceptorInfo> interceptor(GetIndexedInterceptor());
|
| - Handle<JSReceiver> receiver_handle(receiver);
|
| - Handle<JSObject> holder_handle(this);
|
| - CustomArguments args(isolate, interceptor->data(), receiver, this);
|
| - v8::AccessorInfo info(args.end());
|
| - if (!interceptor->query()->IsUndefined()) {
|
| - v8::IndexedPropertyQuery query =
|
| - v8::ToCData<v8::IndexedPropertyQuery>(interceptor->query());
|
| - LOG(isolate,
|
| - ApiIndexedPropertyAccess("interceptor-indexed-has", this, index));
|
| - v8::Handle<v8::Integer> result;
|
| - {
|
| - // Leaving JavaScript.
|
| - VMState state(isolate, EXTERNAL);
|
| - result = query(index, info);
|
| - }
|
| - if (!result.IsEmpty()) {
|
| - ASSERT(result->IsInt32());
|
| - return true; // absence of property is signaled by empty handle.
|
| - }
|
| - } else if (!interceptor->getter()->IsUndefined()) {
|
| - v8::IndexedPropertyGetter getter =
|
| - v8::ToCData<v8::IndexedPropertyGetter>(interceptor->getter());
|
| - LOG(isolate,
|
| - ApiIndexedPropertyAccess("interceptor-indexed-has-get", this, index));
|
| - v8::Handle<v8::Value> result;
|
| - {
|
| - // Leaving JavaScript.
|
| - VMState state(isolate, EXTERNAL);
|
| - result = getter(index, info);
|
| - }
|
| - if (!result.IsEmpty()) return true;
|
| - }
|
| -
|
| - if (holder_handle->GetElementsAccessor()->HasElement(
|
| - *receiver_handle, *holder_handle, index)) {
|
| - return true;
|
| - }
|
| -
|
| - if (holder_handle->IsStringObjectWithCharacterAt(index)) return true;
|
| - Object* pt = holder_handle->GetPrototype();
|
| - if (pt->IsJSProxy()) {
|
| - // We need to follow the spec and simulate a call to [[GetOwnProperty]].
|
| - return JSProxy::cast(pt)->GetElementAttributeWithHandler(
|
| - receiver, index) != ABSENT;
|
| - }
|
| - if (pt->IsNull()) return false;
|
| - return JSObject::cast(pt)->HasElementWithReceiver(*receiver_handle, index);
|
| -}
|
| -
|
| -
|
| -JSObject::LocalElementType JSObject::HasLocalElement(uint32_t index) {
|
| +JSObject::LocalElementType JSObject::GetLocalElementType(uint32_t index) {
|
| // Check access rights if needed.
|
| if (IsAccessCheckNeeded()) {
|
| Heap* heap = GetHeap();
|
| @@ -9283,13 +9455,13 @@ JSObject::LocalElementType JSObject::HasLocalElement(uint32_t index) {
|
| Object* proto = GetPrototype();
|
| if (proto->IsNull()) return UNDEFINED_ELEMENT;
|
| ASSERT(proto->IsJSGlobalObject());
|
| - return JSObject::cast(proto)->HasLocalElement(index);
|
| + return JSObject::cast(proto)->GetLocalElementType(index);
|
| }
|
|
|
| // Check for lookup interceptor
|
| if (HasIndexedInterceptor()) {
|
| - return HasElementWithInterceptor(this, index) ? INTERCEPTED_ELEMENT
|
| - : UNDEFINED_ELEMENT;
|
| + return GetElementAttributeWithInterceptor(this, index, false) != ABSENT
|
| + ? INTERCEPTED_ELEMENT : UNDEFINED_ELEMENT;
|
| }
|
|
|
| // Handle [] on String objects.
|
| @@ -9378,40 +9550,6 @@ JSObject::LocalElementType JSObject::HasLocalElement(uint32_t index) {
|
| }
|
|
|
|
|
| -bool JSObject::HasElementWithReceiver(JSReceiver* receiver, uint32_t index) {
|
| - // Check access rights if needed.
|
| - if (IsAccessCheckNeeded()) {
|
| - Heap* heap = GetHeap();
|
| - if (!heap->isolate()->MayIndexedAccess(this, index, v8::ACCESS_HAS)) {
|
| - heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS);
|
| - return false;
|
| - }
|
| - }
|
| -
|
| - // Check for lookup interceptor
|
| - if (HasIndexedInterceptor()) {
|
| - return HasElementWithInterceptor(receiver, index);
|
| - }
|
| -
|
| - ElementsAccessor* accessor = GetElementsAccessor();
|
| - if (accessor->HasElement(receiver, this, index)) {
|
| - return true;
|
| - }
|
| -
|
| - // Handle [] on String objects.
|
| - if (this->IsStringObjectWithCharacterAt(index)) return true;
|
| -
|
| - Object* pt = GetPrototype();
|
| - if (pt->IsNull()) return false;
|
| - if (pt->IsJSProxy()) {
|
| - // We need to follow the spec and simulate a call to [[GetOwnProperty]].
|
| - return JSProxy::cast(pt)->GetElementAttributeWithHandler(
|
| - receiver, index) != ABSENT;
|
| - }
|
| - return JSObject::cast(pt)->HasElementWithReceiver(receiver, index);
|
| -}
|
| -
|
| -
|
| MaybeObject* JSObject::SetElementWithInterceptor(uint32_t index,
|
| Object* value,
|
| PropertyAttributes attributes,
|
| @@ -10009,28 +10147,31 @@ Handle<Object> JSObject::SetElement(Handle<JSObject> object,
|
|
|
|
|
| MaybeObject* JSObject::SetElement(uint32_t index,
|
| - Object* value,
|
| + Object* value_raw,
|
| PropertyAttributes attributes,
|
| StrictModeFlag strict_mode,
|
| bool check_prototype,
|
| SetPropertyMode set_mode) {
|
| + Isolate* isolate = GetIsolate();
|
| + HandleScope scope(isolate);
|
| + Handle<JSObject> self(this);
|
| + Handle<Object> value(value_raw);
|
| +
|
| // Check access rights if needed.
|
| if (IsAccessCheckNeeded()) {
|
| Heap* heap = GetHeap();
|
| - if (!heap->isolate()->MayIndexedAccess(this, index, v8::ACCESS_SET)) {
|
| - HandleScope scope(heap->isolate());
|
| - Handle<Object> value_handle(value);
|
| - heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_SET);
|
| - return *value_handle;
|
| + if (!heap->isolate()->MayIndexedAccess(*self, index, v8::ACCESS_SET)) {
|
| + heap->isolate()->ReportFailedAccessCheck(*self, v8::ACCESS_SET);
|
| + return *value;
|
| }
|
| }
|
|
|
| if (IsJSGlobalProxy()) {
|
| Object* proto = GetPrototype();
|
| - if (proto->IsNull()) return value;
|
| + if (proto->IsNull()) return *value;
|
| ASSERT(proto->IsJSGlobalObject());
|
| return JSObject::cast(proto)->SetElement(index,
|
| - value,
|
| + *value,
|
| attributes,
|
| strict_mode,
|
| check_prototype,
|
| @@ -10039,10 +10180,8 @@ MaybeObject* JSObject::SetElement(uint32_t index,
|
|
|
| // Don't allow element properties to be redefined for external arrays.
|
| if (HasExternalArrayElements() && set_mode == DEFINE_PROPERTY) {
|
| - Isolate* isolate = GetHeap()->isolate();
|
| - Handle<Object> receiver(this);
|
| Handle<Object> number = isolate->factory()->NewNumberFromUint(index);
|
| - Handle<Object> args[] = { receiver, number };
|
| + Handle<Object> args[] = { self, number };
|
| Handle<Object> error = isolate->factory()->NewTypeError(
|
| "redef_external_array_element", HandleVector(args, ARRAY_SIZE(args)));
|
| return isolate->Throw(*error);
|
| @@ -10057,22 +10196,45 @@ MaybeObject* JSObject::SetElement(uint32_t index,
|
| dictionary->set_requires_slow_elements();
|
| }
|
|
|
| + // From here on, everything has to be handlified.
|
| + Handle<String> name;
|
| + Handle<Object> old_value(isolate->heap()->the_hole_value());
|
| + PropertyAttributes old_attributes;
|
| + bool preexists = false;
|
| + if (FLAG_harmony_observation && map()->is_observed()) {
|
| + name = isolate->factory()->Uint32ToString(index);
|
| + preexists = self->HasLocalElement(index);
|
| + if (preexists) {
|
| + old_attributes = self->GetLocalPropertyAttribute(*name);
|
| + // TODO(observe): only read & set old_value if we have a data property
|
| + old_value = Object::GetElement(self, index);
|
| + }
|
| + }
|
| +
|
| // Check for lookup interceptor
|
| - if (HasIndexedInterceptor()) {
|
| - return SetElementWithInterceptor(index,
|
| - value,
|
| - attributes,
|
| - strict_mode,
|
| - check_prototype,
|
| - set_mode);
|
| + MaybeObject* result = self->HasIndexedInterceptor()
|
| + ? self->SetElementWithInterceptor(
|
| + index, *value, attributes, strict_mode, check_prototype, set_mode)
|
| + : self->SetElementWithoutInterceptor(
|
| + index, *value, attributes, strict_mode, check_prototype, set_mode);
|
| +
|
| + Handle<Object> hresult;
|
| + if (!result->ToHandle(&hresult)) return result;
|
| +
|
| + if (FLAG_harmony_observation && map()->is_observed()) {
|
| + PropertyAttributes new_attributes = self->GetLocalPropertyAttribute(*name);
|
| + if (!preexists) {
|
| + self->EnqueueChangeRecord("new", name, old_value);
|
| + } else if (new_attributes != old_attributes || old_value->IsTheHole()) {
|
| + self->EnqueueChangeRecord("reconfigured", name, old_value);
|
| + } else {
|
| + Handle<Object> newValue = Object::GetElement(self, index);
|
| + if (!newValue->SameValue(*old_value))
|
| + self->EnqueueChangeRecord("updated", name, old_value);
|
| + }
|
| }
|
|
|
| - return SetElementWithoutInterceptor(index,
|
| - value,
|
| - attributes,
|
| - strict_mode,
|
| - check_prototype,
|
| - set_mode);
|
| + return *hresult;
|
| }
|
|
|
|
|
|
|