| Index: src/objects.cc
|
| diff --git a/src/objects.cc b/src/objects.cc
|
| index c8acb4707181a7c7f8ab1ad7e2a32d92ef4eb8e1..0df922efa05d40df799b3956daa7ead10eb0d0c2 100644
|
| --- a/src/objects.cc
|
| +++ b/src/objects.cc
|
| @@ -189,7 +189,7 @@ Object* Object::GetPropertyWithCallback(Object* receiver,
|
| }
|
|
|
| UNREACHABLE();
|
| - return 0;
|
| + return NULL;
|
| }
|
|
|
|
|
| @@ -1613,7 +1613,7 @@ Object* JSObject::SetPropertyWithCallback(Object* structure,
|
| }
|
|
|
| UNREACHABLE();
|
| - return 0;
|
| + return NULL;
|
| }
|
|
|
|
|
| @@ -1657,7 +1657,8 @@ void JSObject::LookupCallbackSetterInPrototypes(String* name,
|
| }
|
|
|
|
|
| -Object* JSObject::LookupCallbackSetterInPrototypes(uint32_t index) {
|
| +bool JSObject::SetElementWithCallbackSetterInPrototypes(uint32_t index,
|
| + Object* value) {
|
| for (Object* pt = GetPrototype();
|
| pt != Heap::null_value();
|
| pt = pt->GetPrototype()) {
|
| @@ -1670,12 +1671,12 @@ Object* JSObject::LookupCallbackSetterInPrototypes(uint32_t index) {
|
| Object* element = dictionary->ValueAt(entry);
|
| PropertyDetails details = dictionary->DetailsAt(entry);
|
| if (details.type() == CALLBACKS) {
|
| - // Only accessors allowed as elements.
|
| - return FixedArray::cast(element)->get(kSetterIndex);
|
| + SetElementWithCallback(element, index, value, JSObject::cast(pt));
|
| + return true;
|
| }
|
| }
|
| }
|
| - return Heap::undefined_value();
|
| + return false;
|
| }
|
|
|
|
|
| @@ -2692,30 +2693,11 @@ Object* JSObject::DefineGetterSetter(String* name,
|
| // interceptor calls.
|
| AssertNoContextChange ncc;
|
|
|
| - // Check access rights if needed.
|
| - if (IsAccessCheckNeeded() &&
|
| - !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) {
|
| - Top::ReportFailedAccessCheck(this, v8::ACCESS_SET);
|
| - return Heap::undefined_value();
|
| - }
|
| -
|
| // Try to flatten before operating on the string.
|
| name->TryFlatten();
|
|
|
| - // Check if there is an API defined callback object which prohibits
|
| - // callback overwriting in this object or it's prototype chain.
|
| - // This mechanism is needed for instance in a browser setting, where
|
| - // certain accessors such as window.location should not be allowed
|
| - // to be overwritten because allowing overwriting could potentially
|
| - // cause security problems.
|
| - LookupResult callback_result;
|
| - LookupCallback(name, &callback_result);
|
| - if (callback_result.IsFound()) {
|
| - Object* obj = callback_result.GetCallbackObject();
|
| - if (obj->IsAccessorInfo() &&
|
| - AccessorInfo::cast(obj)->prohibits_overwriting()) {
|
| - return Heap::undefined_value();
|
| - }
|
| + if (!CanSetCallback(name)) {
|
| + return Heap::undefined_value();
|
| }
|
|
|
| uint32_t index;
|
| @@ -2746,9 +2728,10 @@ Object* JSObject::DefineGetterSetter(String* name,
|
| PropertyDetails details = dictionary->DetailsAt(entry);
|
| if (details.IsReadOnly()) return Heap::undefined_value();
|
| if (details.type() == CALLBACKS) {
|
| - // Only accessors allowed as elements.
|
| - ASSERT(result->IsFixedArray());
|
| - return result;
|
| + if (result->IsFixedArray()) {
|
| + return result;
|
| + }
|
| + // Otherwise allow to override it.
|
| }
|
| }
|
| break;
|
| @@ -2765,15 +2748,10 @@ Object* JSObject::DefineGetterSetter(String* name,
|
| if (result.IsReadOnly()) return Heap::undefined_value();
|
| if (result.type() == CALLBACKS) {
|
| Object* obj = result.GetCallbackObject();
|
| + // Need to preserve old getters/setters.
|
| if (obj->IsFixedArray()) {
|
| - // The object might be in fast mode even though it has
|
| - // a getter/setter.
|
| - Object* ok = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
|
| - if (ok->IsFailure()) return ok;
|
| -
|
| - PropertyDetails details = PropertyDetails(attributes, CALLBACKS);
|
| - SetNormalizedProperty(name, obj, details);
|
| - return obj;
|
| + // Use set to update attributes.
|
| + return SetPropertyCallback(name, obj, attributes);
|
| }
|
| }
|
| }
|
| @@ -2782,50 +2760,100 @@ Object* JSObject::DefineGetterSetter(String* name,
|
| // Allocate the fixed array to hold getter and setter.
|
| Object* structure = Heap::AllocateFixedArray(2, TENURED);
|
| if (structure->IsFailure()) return structure;
|
| - PropertyDetails details = PropertyDetails(attributes, CALLBACKS);
|
|
|
| if (is_element) {
|
| - // Normalize object to make this operation simple.
|
| - Object* ok = NormalizeElements();
|
| - if (ok->IsFailure()) return ok;
|
| + return SetElementCallback(index, structure, attributes);
|
| + } else {
|
| + return SetPropertyCallback(name, structure, attributes);
|
| + }
|
| +}
|
|
|
| - // Update the dictionary with the new CALLBACKS property.
|
| - Object* dict =
|
| - element_dictionary()->Set(index, structure, details);
|
| - if (dict->IsFailure()) return dict;
|
|
|
| - // If name is an index we need to stay in slow case.
|
| - NumberDictionary* elements = NumberDictionary::cast(dict);
|
| - elements->set_requires_slow_elements();
|
| - // Set the potential new dictionary on the object.
|
| - set_elements(NumberDictionary::cast(dict));
|
| - } else {
|
| - // Normalize object to make this operation simple.
|
| - Object* ok = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
|
| - if (ok->IsFailure()) return ok;
|
| +bool JSObject::CanSetCallback(String* name) {
|
| + ASSERT(!IsAccessCheckNeeded()
|
| + || Top::MayNamedAccess(this, name, v8::ACCESS_SET));
|
|
|
| - // For the global object allocate a new map to invalidate the global inline
|
| - // caches which have a global property cell reference directly in the code.
|
| - if (IsGlobalObject()) {
|
| - Object* new_map = map()->CopyDropDescriptors();
|
| - if (new_map->IsFailure()) return new_map;
|
| - set_map(Map::cast(new_map));
|
| + // Check if there is an API defined callback object which prohibits
|
| + // callback overwriting in this object or it's prototype chain.
|
| + // This mechanism is needed for instance in a browser setting, where
|
| + // certain accessors such as window.location should not be allowed
|
| + // to be overwritten because allowing overwriting could potentially
|
| + // cause security problems.
|
| + LookupResult callback_result;
|
| + LookupCallback(name, &callback_result);
|
| + if (callback_result.IsProperty()) {
|
| + Object* obj = callback_result.GetCallbackObject();
|
| + if (obj->IsAccessorInfo() &&
|
| + AccessorInfo::cast(obj)->prohibits_overwriting()) {
|
| + return false;
|
| }
|
| -
|
| - // Update the dictionary with the new CALLBACKS property.
|
| - return SetNormalizedProperty(name, structure, details);
|
| }
|
|
|
| + return true;
|
| +}
|
| +
|
| +
|
| +Object* JSObject::SetElementCallback(uint32_t index,
|
| + Object* structure,
|
| + PropertyAttributes attributes) {
|
| + PropertyDetails details = PropertyDetails(attributes, CALLBACKS);
|
| +
|
| + // Normalize elements to make this operation simple.
|
| + Object* ok = NormalizeElements();
|
| + if (ok->IsFailure()) return ok;
|
| +
|
| + // Update the dictionary with the new CALLBACKS property.
|
| + Object* dict =
|
| + element_dictionary()->Set(index, structure, details);
|
| + if (dict->IsFailure()) return dict;
|
| +
|
| + NumberDictionary* elements = NumberDictionary::cast(dict);
|
| + elements->set_requires_slow_elements();
|
| + // Set the potential new dictionary on the object.
|
| + set_elements(elements);
|
| +
|
| return structure;
|
| }
|
|
|
|
|
| +Object* JSObject::SetPropertyCallback(String* name,
|
| + Object* structure,
|
| + PropertyAttributes attributes) {
|
| + PropertyDetails details = PropertyDetails(attributes, CALLBACKS);
|
| +
|
| + bool convert_back_to_fast = HasFastProperties() &&
|
| + (map()->instance_descriptors()->number_of_descriptors()
|
| + < DescriptorArray::kMaxNumberOfDescriptors);
|
| +
|
| + // Normalize object to make this operation simple.
|
| + Object* ok = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
|
| + if (ok->IsFailure()) return ok;
|
| +
|
| + // For the global object allocate a new map to invalidate the global inline
|
| + // caches which have a global property cell reference directly in the code.
|
| + if (IsGlobalObject()) {
|
| + Object* new_map = map()->CopyDropDescriptors();
|
| + if (new_map->IsFailure()) return new_map;
|
| + set_map(Map::cast(new_map));
|
| + }
|
| +
|
| + // Update the dictionary with the new CALLBACKS property.
|
| + Object* result = SetNormalizedProperty(name, structure, details);
|
| + if (result->IsFailure()) return result;
|
| +
|
| + if (convert_back_to_fast) {
|
| + ok = TransformToFastProperties(0);
|
| + if (ok->IsFailure()) return ok;
|
| + }
|
| + return result;
|
| +}
|
| +
|
| Object* JSObject::DefineAccessor(String* name, bool is_getter, JSFunction* fun,
|
| PropertyAttributes attributes) {
|
| // Check access rights if needed.
|
| if (IsAccessCheckNeeded() &&
|
| - !Top::MayNamedAccess(this, name, v8::ACCESS_HAS)) {
|
| - Top::ReportFailedAccessCheck(this, v8::ACCESS_HAS);
|
| + !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) {
|
| + Top::ReportFailedAccessCheck(this, v8::ACCESS_SET);
|
| return Heap::undefined_value();
|
| }
|
|
|
| @@ -2844,6 +2872,78 @@ Object* JSObject::DefineAccessor(String* name, bool is_getter, JSFunction* fun,
|
| }
|
|
|
|
|
| +Object* JSObject::DefineAccessor(AccessorInfo* info) {
|
| + String* name = String::cast(info->name());
|
| + // Check access rights if needed.
|
| + if (IsAccessCheckNeeded() &&
|
| + !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) {
|
| + Top::ReportFailedAccessCheck(this, v8::ACCESS_SET);
|
| + return Heap::undefined_value();
|
| + }
|
| +
|
| + if (IsJSGlobalProxy()) {
|
| + Object* proto = GetPrototype();
|
| + if (proto->IsNull()) return this;
|
| + ASSERT(proto->IsJSGlobalObject());
|
| + return JSObject::cast(proto)->DefineAccessor(info);
|
| + }
|
| +
|
| + // Make sure that the top context does not change when doing callbacks or
|
| + // interceptor calls.
|
| + AssertNoContextChange ncc;
|
| +
|
| + // Try to flatten before operating on the string.
|
| + name->TryFlatten();
|
| +
|
| + if (!CanSetCallback(name)) {
|
| + return Heap::undefined_value();
|
| + }
|
| +
|
| + uint32_t index = 0;
|
| + bool is_element = name->AsArrayIndex(&index);
|
| +
|
| + if (is_element) {
|
| + if (IsJSArray()) return Heap::undefined_value();
|
| +
|
| + // Accessors overwrite previous callbacks (cf. with getters/setters).
|
| + switch (GetElementsKind()) {
|
| + case FAST_ELEMENTS:
|
| + break;
|
| + case PIXEL_ELEMENTS:
|
| + case EXTERNAL_BYTE_ELEMENTS:
|
| + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
|
| + case EXTERNAL_SHORT_ELEMENTS:
|
| + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
|
| + case EXTERNAL_INT_ELEMENTS:
|
| + case EXTERNAL_UNSIGNED_INT_ELEMENTS:
|
| + case EXTERNAL_FLOAT_ELEMENTS:
|
| + // Ignore getters and setters on pixel and external array
|
| + // elements.
|
| + return Heap::undefined_value();
|
| + case DICTIONARY_ELEMENTS:
|
| + break;
|
| + default:
|
| + UNREACHABLE();
|
| + break;
|
| + }
|
| +
|
| + SetElementCallback(index, info, info->property_attributes());
|
| + } else {
|
| + // Lookup the name.
|
| + LookupResult result;
|
| + LocalLookup(name, &result);
|
| + // ES5 forbids turning a property into an accessor if it's not
|
| + // configurable (that is IsDontDelete in ES3 and v8), see 8.6.1 (Table 5).
|
| + if (result.IsProperty() && (result.IsReadOnly() || result.IsDontDelete())) {
|
| + return Heap::undefined_value();
|
| + }
|
| + SetPropertyCallback(name, info, info->property_attributes());
|
| + }
|
| +
|
| + return this;
|
| +}
|
| +
|
| +
|
| Object* JSObject::LookupAccessor(String* name, bool is_getter) {
|
| // Make sure that the top context does not change when doing callbacks or
|
| // interceptor calls.
|
| @@ -2871,8 +2971,9 @@ Object* JSObject::LookupAccessor(String* name, bool is_getter) {
|
| Object* element = dictionary->ValueAt(entry);
|
| PropertyDetails details = dictionary->DetailsAt(entry);
|
| if (details.type() == CALLBACKS) {
|
| - // Only accessors allowed as elements.
|
| - return FixedArray::cast(element)->get(accessor_index);
|
| + if (element->IsFixedArray()) {
|
| + return FixedArray::cast(element)->get(accessor_index);
|
| + }
|
| }
|
| }
|
| }
|
| @@ -5847,6 +5948,108 @@ Object* JSObject::SetElementWithInterceptor(uint32_t index, Object* value) {
|
| }
|
|
|
|
|
| +Object* JSObject::GetElementWithCallback(Object* receiver,
|
| + Object* structure,
|
| + uint32_t index,
|
| + Object* holder) {
|
| + ASSERT(!structure->IsProxy());
|
| +
|
| + // api style callbacks.
|
| + if (structure->IsAccessorInfo()) {
|
| + AccessorInfo* data = AccessorInfo::cast(structure);
|
| + Object* fun_obj = data->getter();
|
| + v8::AccessorGetter call_fun = v8::ToCData<v8::AccessorGetter>(fun_obj);
|
| + HandleScope scope;
|
| + Handle<JSObject> self(JSObject::cast(receiver));
|
| + Handle<JSObject> holder_handle(JSObject::cast(holder));
|
| + Handle<Object> number = Factory::NewNumberFromUint(index);
|
| + Handle<String> key(Factory::NumberToString(number));
|
| + LOG(ApiNamedPropertyAccess("load", *self, *key));
|
| + CustomArguments args(data->data(), *self, *holder_handle);
|
| + v8::AccessorInfo info(args.end());
|
| + v8::Handle<v8::Value> result;
|
| + {
|
| + // Leaving JavaScript.
|
| + VMState state(EXTERNAL);
|
| + result = call_fun(v8::Utils::ToLocal(key), info);
|
| + }
|
| + RETURN_IF_SCHEDULED_EXCEPTION();
|
| + if (result.IsEmpty()) return Heap::undefined_value();
|
| + return *v8::Utils::OpenHandle(*result);
|
| + }
|
| +
|
| + // __defineGetter__ callback
|
| + if (structure->IsFixedArray()) {
|
| + Object* getter = FixedArray::cast(structure)->get(kGetterIndex);
|
| + if (getter->IsJSFunction()) {
|
| + return Object::GetPropertyWithDefinedGetter(receiver,
|
| + JSFunction::cast(getter));
|
| + }
|
| + // Getter is not a function.
|
| + return Heap::undefined_value();
|
| + }
|
| +
|
| + UNREACHABLE();
|
| + return NULL;
|
| +}
|
| +
|
| +
|
| +Object* JSObject::SetElementWithCallback(Object* structure,
|
| + uint32_t index,
|
| + Object* value,
|
| + JSObject* holder) {
|
| + HandleScope scope;
|
| +
|
| + // We should never get here to initialize a const with the hole
|
| + // value since a const declaration would conflict with the setter.
|
| + ASSERT(!value->IsTheHole());
|
| + Handle<Object> value_handle(value);
|
| +
|
| + // To accommodate both the old and the new api we switch on the
|
| + // data structure used to store the callbacks. Eventually proxy
|
| + // callbacks should be phased out.
|
| + ASSERT(!structure->IsProxy());
|
| +
|
| + if (structure->IsAccessorInfo()) {
|
| + // api style callbacks
|
| + AccessorInfo* data = AccessorInfo::cast(structure);
|
| + Object* call_obj = data->setter();
|
| + v8::AccessorSetter call_fun = v8::ToCData<v8::AccessorSetter>(call_obj);
|
| + if (call_fun == NULL) return value;
|
| + Handle<Object> number = Factory::NewNumberFromUint(index);
|
| + Handle<String> key(Factory::NumberToString(number));
|
| + LOG(ApiNamedPropertyAccess("store", this, *key));
|
| + CustomArguments args(data->data(), this, JSObject::cast(holder));
|
| + v8::AccessorInfo info(args.end());
|
| + {
|
| + // Leaving JavaScript.
|
| + VMState state(EXTERNAL);
|
| + call_fun(v8::Utils::ToLocal(key),
|
| + v8::Utils::ToLocal(value_handle),
|
| + info);
|
| + }
|
| + RETURN_IF_SCHEDULED_EXCEPTION();
|
| + return *value_handle;
|
| + }
|
| +
|
| + if (structure->IsFixedArray()) {
|
| + Object* setter = FixedArray::cast(structure)->get(kSetterIndex);
|
| + if (setter->IsJSFunction()) {
|
| + return SetPropertyWithDefinedSetter(JSFunction::cast(setter), value);
|
| + } else {
|
| + Handle<Object> holder_handle(holder);
|
| + Handle<Object> key(Factory::NewNumberFromUint(index));
|
| + Handle<Object> args[2] = { key, holder_handle };
|
| + return Top::Throw(*Factory::NewTypeError("no_setter_in_callback",
|
| + HandleVector(args, 2)));
|
| + }
|
| + }
|
| +
|
| + UNREACHABLE();
|
| + return NULL;
|
| +}
|
| +
|
| +
|
| // Adding n elements in fast case is O(n*n).
|
| // Note: revisit design to have dual undefined values to capture absent
|
| // elements.
|
| @@ -5857,9 +6060,8 @@ Object* JSObject::SetFastElement(uint32_t index, Object* value) {
|
| uint32_t elms_length = static_cast<uint32_t>(elms->length());
|
|
|
| if (!IsJSArray() && (index >= elms_length || elms->get(index)->IsTheHole())) {
|
| - Object* setter = LookupCallbackSetterInPrototypes(index);
|
| - if (setter->IsJSFunction()) {
|
| - return SetPropertyWithDefinedSetter(JSFunction::cast(setter), value);
|
| + if (SetElementWithCallbackSetterInPrototypes(index, value)) {
|
| + return value;
|
| }
|
| }
|
|
|
| @@ -5977,18 +6179,7 @@ Object* JSObject::SetElementWithoutInterceptor(uint32_t index, Object* value) {
|
| Object* element = dictionary->ValueAt(entry);
|
| PropertyDetails details = dictionary->DetailsAt(entry);
|
| if (details.type() == CALLBACKS) {
|
| - // Only accessors allowed as elements.
|
| - FixedArray* structure = FixedArray::cast(element);
|
| - if (structure->get(kSetterIndex)->IsJSFunction()) {
|
| - JSFunction* setter = JSFunction::cast(structure->get(kSetterIndex));
|
| - return SetPropertyWithDefinedSetter(setter, value);
|
| - } else {
|
| - Handle<Object> self(this);
|
| - Handle<Object> key(Factory::NewNumberFromUint(index));
|
| - Handle<Object> args[2] = { key, self };
|
| - return Top::Throw(*Factory::NewTypeError("no_setter_in_callback",
|
| - HandleVector(args, 2)));
|
| - }
|
| + return SetElementWithCallback(element, index, value, this);
|
| } else {
|
| dictionary->UpdateMaxNumberKey(index);
|
| dictionary->ValueAtPut(entry, value);
|
| @@ -5996,10 +6187,8 @@ Object* JSObject::SetElementWithoutInterceptor(uint32_t index, Object* value) {
|
| } else {
|
| // Index not already used. Look for an accessor in the prototype chain.
|
| if (!IsJSArray()) {
|
| - Object* setter = LookupCallbackSetterInPrototypes(index);
|
| - if (setter->IsJSFunction()) {
|
| - return SetPropertyWithDefinedSetter(JSFunction::cast(setter),
|
| - value);
|
| + if (SetElementWithCallbackSetterInPrototypes(index, value)) {
|
| + return value;
|
| }
|
| }
|
| Object* result = dictionary->AtNumberPut(index, value);
|
| @@ -6102,16 +6291,10 @@ Object* JSObject::GetElementPostInterceptor(JSObject* receiver,
|
| Object* element = dictionary->ValueAt(entry);
|
| PropertyDetails details = dictionary->DetailsAt(entry);
|
| if (details.type() == CALLBACKS) {
|
| - // Only accessors allowed as elements.
|
| - FixedArray* structure = FixedArray::cast(element);
|
| - Object* getter = structure->get(kGetterIndex);
|
| - if (getter->IsJSFunction()) {
|
| - return GetPropertyWithDefinedGetter(receiver,
|
| - JSFunction::cast(getter));
|
| - } else {
|
| - // Getter is not a function.
|
| - return Heap::undefined_value();
|
| - }
|
| + return GetElementWithCallback(receiver,
|
| + element,
|
| + index,
|
| + this);
|
| }
|
| return element;
|
| }
|
| @@ -6259,16 +6442,10 @@ Object* JSObject::GetElementWithReceiver(JSObject* receiver, uint32_t index) {
|
| Object* element = dictionary->ValueAt(entry);
|
| PropertyDetails details = dictionary->DetailsAt(entry);
|
| if (details.type() == CALLBACKS) {
|
| - // Only accessors allowed as elements.
|
| - FixedArray* structure = FixedArray::cast(element);
|
| - Object* getter = structure->get(kGetterIndex);
|
| - if (getter->IsJSFunction()) {
|
| - return GetPropertyWithDefinedGetter(receiver,
|
| - JSFunction::cast(getter));
|
| - } else {
|
| - // Getter is not a function.
|
| - return Heap::undefined_value();
|
| - }
|
| + return GetElementWithCallback(receiver,
|
| + element,
|
| + index,
|
| + this);
|
| }
|
| return element;
|
| }
|
|
|