Rewriting SetOwnPropertyIgnoreAttributes using the LookupIterator
BUG= R=jkummerow@chromium.org Review URL: https://codereview.chromium.org/468493002 git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@23163 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
d0fc03b5ce
commit
e61f727705
@ -126,6 +126,31 @@ void LookupIterator::PrepareForDataProperty(Handle<Object> value) {
|
||||
}
|
||||
|
||||
|
||||
void LookupIterator::ReconfigureDataProperty(Handle<Object> value,
|
||||
PropertyAttributes attributes) {
|
||||
DCHECK(has_property_);
|
||||
DCHECK(HolderIsReceiverOrHiddenPrototype());
|
||||
Handle<JSObject> holder = GetHolder<JSObject>();
|
||||
if (property_encoding_ != DICTIONARY) {
|
||||
holder_map_ = Map::ReconfigureDataProperty(holder_map_, descriptor_number(),
|
||||
attributes);
|
||||
JSObject::MigrateToMap(holder, holder_map_);
|
||||
}
|
||||
|
||||
// Reload property information and update the descriptor if in dictionary
|
||||
// mode.
|
||||
if (holder_map_->is_dictionary_map()) {
|
||||
property_encoding_ = DICTIONARY;
|
||||
PropertyDetails details(attributes, NORMAL, 0);
|
||||
JSObject::SetNormalizedProperty(holder, name(), value, details);
|
||||
} else {
|
||||
property_encoding_ = DESCRIPTOR;
|
||||
}
|
||||
|
||||
CHECK(HasProperty());
|
||||
}
|
||||
|
||||
|
||||
void LookupIterator::TransitionToDataProperty(
|
||||
Handle<Object> value, PropertyAttributes attributes,
|
||||
Object::StoreFromKeyed store_mode) {
|
||||
@ -136,10 +161,6 @@ void LookupIterator::TransitionToDataProperty(
|
||||
// observable.
|
||||
Handle<JSObject> receiver = Handle<JSObject>::cast(GetReceiver());
|
||||
|
||||
// Properties have to be added to context extension objects through
|
||||
// SetOwnPropertyIgnoreAttributes.
|
||||
DCHECK(!receiver->IsJSContextExtensionObject());
|
||||
|
||||
if (receiver->IsJSGlobalProxy()) {
|
||||
PrototypeIterator iter(isolate(), receiver);
|
||||
receiver =
|
||||
@ -162,6 +183,8 @@ void LookupIterator::TransitionToDataProperty(
|
||||
|
||||
bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const {
|
||||
DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY);
|
||||
// Optimization that only works if configuration_ is not mutable.
|
||||
if (!check_derived()) return true;
|
||||
DisallowHeapAllocation no_gc;
|
||||
Handle<Object> receiver = GetReceiver();
|
||||
if (!receiver->IsJSReceiver()) return false;
|
||||
@ -182,6 +205,16 @@ bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const {
|
||||
}
|
||||
|
||||
|
||||
bool LookupIterator::HolderIsNonGlobalHiddenPrototype() const {
|
||||
if (!HolderIsReceiverOrHiddenPrototype()) return false;
|
||||
Handle<Object> receiver = GetReceiver();
|
||||
Handle<JSReceiver> holder = GetHolder<JSReceiver>();
|
||||
if (receiver.is_identical_to(holder)) return false;
|
||||
if (receiver->IsJSGlobalProxy()) return !holder->IsJSGlobalObject();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Handle<Object> LookupIterator::FetchValue() const {
|
||||
Object* result = NULL;
|
||||
Handle<JSObject> holder = GetHolder<JSObject>();
|
||||
|
33
src/lookup.h
33
src/lookup.h
@ -15,15 +15,15 @@ namespace internal {
|
||||
class LookupIterator V8_FINAL BASE_EMBEDDED {
|
||||
public:
|
||||
enum Configuration {
|
||||
CHECK_OWN_REAL = 0,
|
||||
CHECK_HIDDEN = 1 << 0,
|
||||
CHECK_DERIVED = 1 << 1,
|
||||
CHECK_INTERCEPTOR = 1 << 2,
|
||||
CHECK_OWN_REAL = 0,
|
||||
CHECK_HIDDEN = 1 << 0,
|
||||
CHECK_DERIVED = 1 << 1,
|
||||
CHECK_INTERCEPTOR = 1 << 2,
|
||||
CHECK_ACCESS_CHECK = 1 << 3,
|
||||
CHECK_ALL = CHECK_HIDDEN | CHECK_DERIVED |
|
||||
CHECK_INTERCEPTOR | CHECK_ACCESS_CHECK,
|
||||
SKIP_INTERCEPTOR = CHECK_ALL ^ CHECK_INTERCEPTOR,
|
||||
CHECK_OWN = CHECK_ALL ^ CHECK_DERIVED
|
||||
CHECK_HIDDEN_ACCESS = CHECK_HIDDEN | CHECK_ACCESS_CHECK,
|
||||
SKIP_INTERCEPTOR = CHECK_HIDDEN_ACCESS | CHECK_DERIVED,
|
||||
CHECK_ALL = SKIP_INTERCEPTOR | CHECK_INTERCEPTOR,
|
||||
CHECK_OWN = CHECK_HIDDEN_ACCESS | CHECK_INTERCEPTOR
|
||||
};
|
||||
|
||||
enum State {
|
||||
@ -90,7 +90,7 @@ class LookupIterator V8_FINAL BASE_EMBEDDED {
|
||||
Heap* heap() const { return isolate_->heap(); }
|
||||
Factory* factory() const { return isolate_->factory(); }
|
||||
Handle<Object> GetReceiver() const {
|
||||
return Handle<Object>::cast(maybe_receiver_.ToHandleChecked());
|
||||
return maybe_receiver_.ToHandleChecked();
|
||||
}
|
||||
Handle<Map> holder_map() const { return holder_map_; }
|
||||
template <class T>
|
||||
@ -100,16 +100,7 @@ class LookupIterator V8_FINAL BASE_EMBEDDED {
|
||||
}
|
||||
Handle<JSReceiver> GetRoot() const;
|
||||
bool HolderIsReceiverOrHiddenPrototype() const;
|
||||
|
||||
/* Dynamically reduce the trapped types. */
|
||||
void skip_interceptor() {
|
||||
configuration_ = static_cast<Configuration>(
|
||||
configuration_ & ~CHECK_INTERCEPTOR);
|
||||
}
|
||||
void skip_access_check() {
|
||||
configuration_ = static_cast<Configuration>(
|
||||
configuration_ & ~CHECK_ACCESS_CHECK);
|
||||
}
|
||||
bool HolderIsNonGlobalHiddenPrototype() const;
|
||||
|
||||
/* ACCESS_CHECK */
|
||||
bool HasAccess(v8::AccessType access_type) const;
|
||||
@ -123,6 +114,8 @@ class LookupIterator V8_FINAL BASE_EMBEDDED {
|
||||
void TransitionToDataProperty(Handle<Object> value,
|
||||
PropertyAttributes attributes,
|
||||
Object::StoreFromKeyed store_mode);
|
||||
void ReconfigureDataProperty(Handle<Object> value,
|
||||
PropertyAttributes attributes);
|
||||
PropertyKind property_kind() const {
|
||||
DCHECK(has_property_);
|
||||
return property_kind_;
|
||||
@ -196,6 +189,8 @@ class LookupIterator V8_FINAL BASE_EMBEDDED {
|
||||
}
|
||||
}
|
||||
|
||||
// If configuration_ becomes mutable, update
|
||||
// HolderIsReceiverOrHiddenPrototype.
|
||||
Configuration configuration_;
|
||||
State state_;
|
||||
bool has_property_;
|
||||
|
464
src/objects.cc
464
src/objects.cc
@ -575,8 +575,6 @@ MaybeHandle<Object> Object::SetPropertyWithDefinedSetter(
|
||||
|
||||
|
||||
static bool FindAllCanReadHolder(LookupIterator* it) {
|
||||
it->skip_interceptor();
|
||||
it->skip_access_check();
|
||||
for (; it->IsFound(); it->Next()) {
|
||||
if (it->state() == LookupIterator::PROPERTY &&
|
||||
it->HasProperty() &&
|
||||
@ -618,8 +616,6 @@ Maybe<PropertyAttributes> JSObject::GetPropertyAttributesWithFailedAccessCheck(
|
||||
|
||||
|
||||
static bool FindAllCanWriteHolder(LookupIterator* it) {
|
||||
it->skip_interceptor();
|
||||
it->skip_access_check();
|
||||
for (; it->IsFound(); it->Next()) {
|
||||
if (it->state() == LookupIterator::PROPERTY && it->HasProperty() &&
|
||||
it->property_kind() == LookupIterator::ACCESSOR) {
|
||||
@ -674,21 +670,6 @@ Handle<Object> JSObject::GetNormalizedProperty(Handle<JSObject> object,
|
||||
}
|
||||
|
||||
|
||||
void JSObject::SetNormalizedProperty(Handle<JSObject> object,
|
||||
const LookupResult* result,
|
||||
Handle<Object> value) {
|
||||
DCHECK(!object->HasFastProperties());
|
||||
NameDictionary* property_dictionary = object->property_dictionary();
|
||||
if (object->IsGlobalObject()) {
|
||||
Handle<PropertyCell> cell(PropertyCell::cast(
|
||||
property_dictionary->ValueAt(result->GetDictionaryEntry())));
|
||||
PropertyCell::SetValueInferType(cell, value);
|
||||
} else {
|
||||
property_dictionary->ValueAtPut(result->GetDictionaryEntry(), *value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void JSObject::SetNormalizedProperty(Handle<JSObject> object,
|
||||
Handle<Name> name,
|
||||
Handle<Object> value,
|
||||
@ -1823,37 +1804,6 @@ MaybeHandle<Map> Map::CopyWithConstant(Handle<Map> map,
|
||||
}
|
||||
|
||||
|
||||
void JSObject::AddFastProperty(Handle<JSObject> object,
|
||||
Handle<Name> name,
|
||||
Handle<Object> value,
|
||||
PropertyAttributes attributes,
|
||||
StoreFromKeyed store_mode,
|
||||
TransitionFlag flag) {
|
||||
DCHECK(!object->IsJSGlobalProxy());
|
||||
|
||||
MaybeHandle<Map> maybe_map;
|
||||
if (value->IsJSFunction()) {
|
||||
maybe_map = Map::CopyWithConstant(
|
||||
handle(object->map()), name, value, attributes, flag);
|
||||
} else if (!object->map()->TooManyFastProperties(store_mode)) {
|
||||
Isolate* isolate = object->GetIsolate();
|
||||
Representation representation = value->OptimalRepresentation();
|
||||
maybe_map = Map::CopyWithField(
|
||||
handle(object->map(), isolate), name,
|
||||
value->OptimalType(isolate, representation),
|
||||
attributes, representation, flag);
|
||||
}
|
||||
|
||||
Handle<Map> new_map;
|
||||
if (!maybe_map.ToHandle(&new_map)) {
|
||||
NormalizeProperties(object, CLEAR_INOBJECT_PROPERTIES, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
JSObject::MigrateToNewProperty(object, new_map, value);
|
||||
}
|
||||
|
||||
|
||||
void JSObject::AddSlowProperty(Handle<JSObject> object,
|
||||
Handle<Name> name,
|
||||
Handle<Object> value,
|
||||
@ -1886,45 +1836,6 @@ void JSObject::AddSlowProperty(Handle<JSObject> object,
|
||||
}
|
||||
|
||||
|
||||
MaybeHandle<Object> JSObject::AddPropertyInternal(
|
||||
Handle<JSObject> object, Handle<Name> name, Handle<Object> value,
|
||||
PropertyAttributes attributes, JSReceiver::StoreFromKeyed store_mode,
|
||||
ExtensibilityCheck extensibility_check, TransitionFlag transition_flag) {
|
||||
DCHECK(!object->IsJSGlobalProxy());
|
||||
Isolate* isolate = object->GetIsolate();
|
||||
|
||||
if (!name->IsUniqueName()) {
|
||||
name = isolate->factory()->InternalizeString(
|
||||
Handle<String>::cast(name));
|
||||
}
|
||||
|
||||
if (extensibility_check == PERFORM_EXTENSIBILITY_CHECK &&
|
||||
!object->map()->is_extensible()) {
|
||||
Handle<Object> args[1] = {name};
|
||||
Handle<Object> error = isolate->factory()->NewTypeError(
|
||||
"object_not_extensible", HandleVector(args, ARRAY_SIZE(args)));
|
||||
return isolate->Throw<Object>(error);
|
||||
}
|
||||
|
||||
if (object->HasFastProperties()) {
|
||||
AddFastProperty(object, name, value, attributes, store_mode,
|
||||
transition_flag);
|
||||
}
|
||||
|
||||
if (!object->HasFastProperties()) {
|
||||
AddSlowProperty(object, name, value, attributes);
|
||||
}
|
||||
|
||||
if (object->map()->is_observed() &&
|
||||
*name != isolate->heap()->hidden_string()) {
|
||||
Handle<Object> old_value = isolate->factory()->the_hole_value();
|
||||
EnqueueChangeRecord(object, "add", name, old_value);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
Context* JSObject::GetCreationContext() {
|
||||
Object* constructor = this->map()->constructor();
|
||||
JSFunction* function;
|
||||
@ -1959,23 +1870,6 @@ void JSObject::EnqueueChangeRecord(Handle<JSObject> object,
|
||||
}
|
||||
|
||||
|
||||
static void ReplaceSlowProperty(Handle<JSObject> object,
|
||||
Handle<Name> name,
|
||||
Handle<Object> value,
|
||||
PropertyAttributes attributes) {
|
||||
NameDictionary* dictionary = object->property_dictionary();
|
||||
int old_index = dictionary->FindEntry(name);
|
||||
int new_enumeration_index = 0; // 0 means "Use the next available index."
|
||||
if (old_index != -1) {
|
||||
// All calls to ReplaceSlowProperty have had all transitions removed.
|
||||
new_enumeration_index = dictionary->DetailsAt(old_index).dictionary_index();
|
||||
}
|
||||
|
||||
PropertyDetails new_details(attributes, NORMAL, new_enumeration_index);
|
||||
JSObject::SetNormalizedProperty(object, name, value, new_details);
|
||||
}
|
||||
|
||||
|
||||
const char* Representation::Mnemonic() const {
|
||||
switch (kind_) {
|
||||
case kNone: return "v";
|
||||
@ -3029,7 +2923,8 @@ MaybeHandle<Object> Object::SetProperty(LookupIterator* it,
|
||||
if (done) break;
|
||||
}
|
||||
|
||||
return AddDataProperty(it, value, NONE, strict_mode, store_mode);
|
||||
return AddDataProperty(it, value, NONE, strict_mode, store_mode,
|
||||
PERFORM_EXTENSIBILITY_CHECK);
|
||||
}
|
||||
|
||||
|
||||
@ -3084,7 +2979,8 @@ MaybeHandle<Object> Object::AddDataProperty(LookupIterator* it,
|
||||
Handle<Object> value,
|
||||
PropertyAttributes attributes,
|
||||
StrictMode strict_mode,
|
||||
StoreFromKeyed store_mode) {
|
||||
StoreFromKeyed store_mode,
|
||||
ExtensibilityCheck check) {
|
||||
DCHECK(!it->GetReceiver()->IsJSProxy());
|
||||
if (!it->GetReceiver()->IsJSObject()) {
|
||||
// TODO(verwaest): Throw a TypeError with a more specific message.
|
||||
@ -3102,7 +2998,8 @@ MaybeHandle<Object> Object::AddDataProperty(LookupIterator* it,
|
||||
Handle<JSGlobalObject>::cast(PrototypeIterator::GetCurrent(iter));
|
||||
}
|
||||
|
||||
if (!receiver->map()->is_extensible()) {
|
||||
if (check == PERFORM_EXTENSIBILITY_CHECK &&
|
||||
!receiver->map()->is_extensible()) {
|
||||
if (strict_mode == SLOPPY) return value;
|
||||
|
||||
Handle<Object> args[1] = {it->name()};
|
||||
@ -3943,45 +3840,6 @@ bool JSObject::TryMigrateInstance(Handle<JSObject> object) {
|
||||
}
|
||||
|
||||
|
||||
MaybeHandle<Object> JSObject::SetPropertyUsingTransition(
|
||||
Handle<JSObject> object,
|
||||
LookupResult* lookup,
|
||||
Handle<Name> name,
|
||||
Handle<Object> value,
|
||||
PropertyAttributes attributes) {
|
||||
Handle<Map> transition_map(lookup->GetTransitionTarget());
|
||||
int descriptor = transition_map->LastAdded();
|
||||
|
||||
Handle<DescriptorArray> descriptors(transition_map->instance_descriptors());
|
||||
PropertyDetails details = descriptors->GetDetails(descriptor);
|
||||
|
||||
if (details.type() == CALLBACKS || attributes != details.attributes()) {
|
||||
// AddPropertyInternal will either normalize the object, or create a new
|
||||
// fast copy of the map. If we get a fast copy of the map, all field
|
||||
// representations will be tagged since the transition is omitted.
|
||||
return JSObject::AddPropertyInternal(
|
||||
object, name, value, attributes,
|
||||
JSReceiver::CERTAINLY_NOT_STORE_FROM_KEYED,
|
||||
JSReceiver::OMIT_EXTENSIBILITY_CHECK, OMIT_TRANSITION);
|
||||
}
|
||||
|
||||
// Keep the target CONSTANT if the same value is stored.
|
||||
// TODO(verwaest): Also support keeping the placeholder
|
||||
// (value->IsUninitialized) as constant.
|
||||
if (!lookup->CanHoldValue(value)) {
|
||||
Representation field_representation = value->OptimalRepresentation();
|
||||
Handle<HeapType> field_type = value->OptimalType(
|
||||
lookup->isolate(), field_representation);
|
||||
transition_map = Map::GeneralizeRepresentation(
|
||||
transition_map, descriptor,
|
||||
field_representation, field_type, FORCE_FIELD);
|
||||
}
|
||||
|
||||
JSObject::MigrateToNewProperty(object, transition_map, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
void JSObject::MigrateToNewProperty(Handle<JSObject> object,
|
||||
Handle<Map> map,
|
||||
Handle<Object> value) {
|
||||
@ -4012,65 +3870,6 @@ void JSObject::WriteToField(int descriptor, Object* value) {
|
||||
}
|
||||
|
||||
|
||||
void JSObject::SetPropertyToField(LookupResult* lookup, Handle<Object> value) {
|
||||
if (lookup->type() == CONSTANT || !lookup->CanHoldValue(value)) {
|
||||
Representation field_representation = value->OptimalRepresentation();
|
||||
Handle<HeapType> field_type = value->OptimalType(
|
||||
lookup->isolate(), field_representation);
|
||||
JSObject::GeneralizeFieldRepresentation(handle(lookup->holder()),
|
||||
lookup->GetDescriptorIndex(),
|
||||
field_representation, field_type);
|
||||
}
|
||||
lookup->holder()->WriteToField(lookup->GetDescriptorIndex(), *value);
|
||||
}
|
||||
|
||||
|
||||
void JSObject::ConvertAndSetOwnProperty(LookupResult* lookup,
|
||||
Handle<Name> name,
|
||||
Handle<Object> value,
|
||||
PropertyAttributes attributes) {
|
||||
Handle<JSObject> object(lookup->holder());
|
||||
if (object->map()->TooManyFastProperties(Object::MAY_BE_STORE_FROM_KEYED)) {
|
||||
JSObject::NormalizeProperties(object, CLEAR_INOBJECT_PROPERTIES, 0);
|
||||
} else if (object->map()->is_prototype_map()) {
|
||||
JSObject::NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, 0);
|
||||
}
|
||||
|
||||
if (!object->HasFastProperties()) {
|
||||
ReplaceSlowProperty(object, name, value, attributes);
|
||||
ReoptimizeIfPrototype(object);
|
||||
return;
|
||||
}
|
||||
|
||||
int descriptor_index = lookup->GetDescriptorIndex();
|
||||
if (lookup->GetAttributes() == attributes) {
|
||||
JSObject::GeneralizeFieldRepresentation(object, descriptor_index,
|
||||
Representation::Tagged(),
|
||||
HeapType::Any(lookup->isolate()));
|
||||
} else {
|
||||
Handle<Map> old_map(object->map());
|
||||
Handle<Map> new_map = Map::CopyGeneralizeAllRepresentations(old_map,
|
||||
descriptor_index, FORCE_FIELD, attributes, "attributes mismatch");
|
||||
JSObject::MigrateToMap(object, new_map);
|
||||
}
|
||||
|
||||
object->WriteToField(descriptor_index, *value);
|
||||
}
|
||||
|
||||
|
||||
void JSObject::SetPropertyToFieldWithAttributes(LookupResult* lookup,
|
||||
Handle<Name> name,
|
||||
Handle<Object> value,
|
||||
PropertyAttributes attributes) {
|
||||
if (lookup->GetAttributes() == attributes) {
|
||||
if (value->IsUninitialized()) return;
|
||||
SetPropertyToField(lookup, value);
|
||||
} else {
|
||||
ConvertAndSetOwnProperty(lookup, name, value, attributes);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void JSObject::AddProperty(Handle<JSObject> object, Handle<Name> name,
|
||||
Handle<Object> value,
|
||||
PropertyAttributes attributes) {
|
||||
@ -4100,156 +3899,123 @@ MaybeHandle<Object> JSObject::SetOwnPropertyIgnoreAttributes(
|
||||
StoreFromKeyed store_from_keyed,
|
||||
ExecutableAccessorInfoHandling handling) {
|
||||
DCHECK(!value->IsTheHole());
|
||||
Isolate* isolate = object->GetIsolate();
|
||||
|
||||
// Make sure that the top context does not change when doing callbacks or
|
||||
// interceptor calls.
|
||||
AssertNoContextChange ncc(isolate);
|
||||
|
||||
LookupResult lookup(isolate);
|
||||
object->LookupOwn(name, &lookup, true);
|
||||
if (!lookup.IsFound()) {
|
||||
object->map()->LookupTransition(*object, *name, &lookup);
|
||||
}
|
||||
|
||||
// Check access rights if needed.
|
||||
if (object->IsAccessCheckNeeded()) {
|
||||
if (!isolate->MayNamedAccess(object, name, v8::ACCESS_SET)) {
|
||||
LookupIterator it(object, name, LookupIterator::CHECK_OWN);
|
||||
return SetPropertyWithFailedAccessCheck(&it, value, SLOPPY);
|
||||
}
|
||||
}
|
||||
|
||||
if (object->IsJSGlobalProxy()) {
|
||||
PrototypeIterator iter(isolate, object);
|
||||
if (iter.IsAtEnd()) return value;
|
||||
DCHECK(PrototypeIterator::GetCurrent(iter)->IsJSGlobalObject());
|
||||
return SetOwnPropertyIgnoreAttributes(
|
||||
Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)), name,
|
||||
value, attributes, extensibility_check);
|
||||
}
|
||||
|
||||
if (lookup.IsInterceptor() ||
|
||||
(lookup.IsDescriptorOrDictionary() && lookup.type() == CALLBACKS)) {
|
||||
object->LookupOwnRealNamedProperty(name, &lookup);
|
||||
}
|
||||
|
||||
// Check for accessor in prototype chain removed here in clone.
|
||||
if (!lookup.IsFound()) {
|
||||
object->map()->LookupTransition(*object, *name, &lookup);
|
||||
TransitionFlag flag = lookup.IsFound()
|
||||
? OMIT_TRANSITION : INSERT_TRANSITION;
|
||||
// Neither properties nor transitions found.
|
||||
return AddPropertyInternal(object, name, value, attributes,
|
||||
store_from_keyed, extensibility_check, flag);
|
||||
}
|
||||
|
||||
Handle<Object> old_value = isolate->factory()->the_hole_value();
|
||||
PropertyAttributes old_attributes = ABSENT;
|
||||
LookupIterator it(object, name, LookupIterator::CHECK_HIDDEN_ACCESS);
|
||||
bool is_observed = object->map()->is_observed() &&
|
||||
*name != isolate->heap()->hidden_string();
|
||||
if (is_observed && lookup.IsProperty()) {
|
||||
if (lookup.IsDataProperty()) {
|
||||
old_value = Object::GetPropertyOrElement(object, name).ToHandleChecked();
|
||||
}
|
||||
old_attributes = lookup.GetAttributes();
|
||||
}
|
||||
*name != it.isolate()->heap()->hidden_string();
|
||||
for (; it.IsFound(); it.Next()) {
|
||||
switch (it.state()) {
|
||||
case LookupIterator::NOT_FOUND:
|
||||
case LookupIterator::JSPROXY:
|
||||
case LookupIterator::INTERCEPTOR:
|
||||
UNREACHABLE();
|
||||
|
||||
bool executed_set_prototype = false;
|
||||
|
||||
// Check of IsReadOnly removed from here in clone.
|
||||
if (lookup.IsTransition()) {
|
||||
Handle<Object> result;
|
||||
ASSIGN_RETURN_ON_EXCEPTION(
|
||||
isolate, result,
|
||||
SetPropertyUsingTransition(
|
||||
handle(lookup.holder()), &lookup, name, value, attributes),
|
||||
Object);
|
||||
} else {
|
||||
switch (lookup.type()) {
|
||||
case NORMAL:
|
||||
ReplaceSlowProperty(object, name, value, attributes);
|
||||
break;
|
||||
case FIELD:
|
||||
SetPropertyToFieldWithAttributes(&lookup, name, value, attributes);
|
||||
break;
|
||||
case CONSTANT:
|
||||
// Only replace the constant if necessary.
|
||||
if (lookup.GetAttributes() != attributes ||
|
||||
*value != lookup.GetConstant()) {
|
||||
SetPropertyToFieldWithAttributes(&lookup, name, value, attributes);
|
||||
case LookupIterator::ACCESS_CHECK:
|
||||
if (!it.isolate()->MayNamedAccess(object, name, v8::ACCESS_SET)) {
|
||||
return SetPropertyWithFailedAccessCheck(&it, value, SLOPPY);
|
||||
}
|
||||
break;
|
||||
case CALLBACKS:
|
||||
{
|
||||
Handle<Object> callback(lookup.GetCallbackObject(), isolate);
|
||||
if (callback->IsExecutableAccessorInfo() &&
|
||||
handling == DONT_FORCE_FIELD) {
|
||||
Handle<Object> result;
|
||||
ASSIGN_RETURN_ON_EXCEPTION(
|
||||
isolate, result, JSObject::SetPropertyWithAccessor(
|
||||
object, name, value, handle(lookup.holder()),
|
||||
callback, STRICT),
|
||||
Object);
|
||||
|
||||
if (attributes != lookup.GetAttributes()) {
|
||||
Handle<ExecutableAccessorInfo> new_data =
|
||||
Accessors::CloneAccessor(
|
||||
isolate, Handle<ExecutableAccessorInfo>::cast(callback));
|
||||
new_data->set_property_attributes(attributes);
|
||||
if (attributes & READ_ONLY) {
|
||||
// This way we don't have to introduce a lookup to the setter,
|
||||
// simply make it unavailable to reflect the attributes.
|
||||
new_data->clear_setter();
|
||||
case LookupIterator::PROPERTY: {
|
||||
if (!it.HasProperty()) break;
|
||||
if (it.HolderIsNonGlobalHiddenPrototype()) break;
|
||||
PropertyDetails details = it.property_details();
|
||||
Handle<Object> old_value = it.isolate()->factory()->the_hole_value();
|
||||
switch (it.property_kind()) {
|
||||
case LookupIterator::ACCESSOR: {
|
||||
// Ensure the context isn't changed after calling into accessors.
|
||||
AssertNoContextChange ncc(it.isolate());
|
||||
|
||||
Handle<Object> accessors = it.GetAccessors();
|
||||
|
||||
if (is_observed && accessors->IsAccessorInfo()) {
|
||||
ASSIGN_RETURN_ON_EXCEPTION(
|
||||
it.isolate(), old_value,
|
||||
GetPropertyWithAccessor(it.GetReceiver(), it.name(),
|
||||
it.GetHolder<JSObject>(), accessors),
|
||||
Object);
|
||||
}
|
||||
|
||||
SetPropertyCallback(object, name, new_data, attributes);
|
||||
// Special handling for ExecutableAccessorInfo, which behaves like a
|
||||
// data property.
|
||||
if (handling == DONT_FORCE_FIELD &&
|
||||
accessors->IsExecutableAccessorInfo()) {
|
||||
Handle<Object> result;
|
||||
ASSIGN_RETURN_ON_EXCEPTION(
|
||||
it.isolate(), result,
|
||||
JSObject::SetPropertyWithAccessor(
|
||||
it.GetReceiver(), it.name(), value,
|
||||
it.GetHolder<JSObject>(), accessors, STRICT),
|
||||
Object);
|
||||
DCHECK(result->SameValue(*value));
|
||||
|
||||
if (details.attributes() == attributes) {
|
||||
// Regular property update if the attributes match.
|
||||
if (is_observed && !old_value->SameValue(*value)) {
|
||||
// If we are setting the prototype of a function and are
|
||||
// observed, don't send change records because the prototype
|
||||
// handles that itself.
|
||||
if (!object->IsJSFunction() ||
|
||||
!Name::Equals(it.isolate()->factory()->prototype_string(),
|
||||
name) ||
|
||||
!Handle<JSFunction>::cast(object)
|
||||
->should_have_prototype()) {
|
||||
EnqueueChangeRecord(object, "update", name, old_value);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
// Reconfigure the accessor if attributes mismatch.
|
||||
Handle<ExecutableAccessorInfo> new_data =
|
||||
Accessors::CloneAccessor(
|
||||
it.isolate(),
|
||||
Handle<ExecutableAccessorInfo>::cast(accessors));
|
||||
new_data->set_property_attributes(attributes);
|
||||
// By clearing the setter we don't have to introduce a lookup to
|
||||
// the setter, simply make it unavailable to reflect the
|
||||
// attributes.
|
||||
if (attributes & READ_ONLY) new_data->clear_setter();
|
||||
SetPropertyCallback(object, name, new_data, attributes);
|
||||
if (is_observed) {
|
||||
if (old_value->SameValue(*value)) {
|
||||
old_value = it.isolate()->factory()->the_hole_value();
|
||||
}
|
||||
EnqueueChangeRecord(object, "reconfigure", name, old_value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
// Regular accessor. Reconfigure to data property.
|
||||
break;
|
||||
}
|
||||
if (is_observed) {
|
||||
// If we are setting the prototype of a function and are observed,
|
||||
// don't send change records because the prototype handles that
|
||||
// itself.
|
||||
executed_set_prototype = object->IsJSFunction() &&
|
||||
String::Equals(isolate->factory()->prototype_string(),
|
||||
Handle<String>::cast(name)) &&
|
||||
Handle<JSFunction>::cast(object)->should_have_prototype();
|
||||
}
|
||||
} else {
|
||||
ConvertAndSetOwnProperty(&lookup, name, value, attributes);
|
||||
|
||||
case LookupIterator::DATA:
|
||||
// Regular property update if the attributes match.
|
||||
if (details.attributes() == attributes) {
|
||||
return SetDataProperty(&it, value);
|
||||
}
|
||||
// Reconfigure the data property if the attributes mismatch.
|
||||
if (is_observed) old_value = it.GetDataValue();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NONEXISTENT:
|
||||
case HANDLER:
|
||||
case INTERCEPTOR:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
if (is_observed && !executed_set_prototype) {
|
||||
if (lookup.IsTransition()) {
|
||||
EnqueueChangeRecord(object, "add", name, old_value);
|
||||
} else if (old_value->IsTheHole()) {
|
||||
EnqueueChangeRecord(object, "reconfigure", name, old_value);
|
||||
} else {
|
||||
LookupResult new_lookup(isolate);
|
||||
object->LookupOwn(name, &new_lookup, true);
|
||||
bool value_changed = false;
|
||||
if (new_lookup.IsDataProperty()) {
|
||||
Handle<Object> new_value =
|
||||
Object::GetPropertyOrElement(object, name).ToHandleChecked();
|
||||
value_changed = !old_value->SameValue(*new_value);
|
||||
}
|
||||
if (new_lookup.GetAttributes() != old_attributes) {
|
||||
if (!value_changed) old_value = isolate->factory()->the_hole_value();
|
||||
EnqueueChangeRecord(object, "reconfigure", name, old_value);
|
||||
} else if (value_changed) {
|
||||
EnqueueChangeRecord(object, "update", name, old_value);
|
||||
it.ReconfigureDataProperty(value, attributes);
|
||||
it.PrepareForDataProperty(value);
|
||||
it.WriteDataValue(value);
|
||||
|
||||
if (is_observed) {
|
||||
if (old_value->SameValue(*value)) {
|
||||
old_value = it.isolate()->factory()->the_hole_value();
|
||||
}
|
||||
EnqueueChangeRecord(object, "reconfigure", name, old_value);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
return AddDataProperty(&it, value, attributes, STRICT, store_from_keyed,
|
||||
extensibility_check);
|
||||
}
|
||||
|
||||
|
||||
@ -7411,6 +7177,18 @@ Handle<Map> Map::TransitionToDataProperty(Handle<Map> map, Handle<Name> name,
|
||||
}
|
||||
|
||||
|
||||
Handle<Map> Map::ReconfigureDataProperty(Handle<Map> map, int descriptor,
|
||||
PropertyAttributes attributes) {
|
||||
// Dictionaries have to be reconfigured in-place.
|
||||
DCHECK(!map->is_dictionary_map());
|
||||
|
||||
// For now, give up on transitioning and just create a unique map.
|
||||
// TODO(verwaest/ishell): Cache transitions with different attributes.
|
||||
return CopyGeneralizeAllRepresentations(map, descriptor, FORCE_FIELD,
|
||||
attributes, "attributes mismatch");
|
||||
}
|
||||
|
||||
|
||||
Handle<Map> Map::CopyAddDescriptor(Handle<Map> map,
|
||||
Descriptor* descriptor,
|
||||
TransitionFlag flag) {
|
||||
|
@ -247,6 +247,14 @@ enum PropertyNormalizationMode {
|
||||
};
|
||||
|
||||
|
||||
// Internal properties (e.g. the hidden properties dictionary) might
|
||||
// be added even though the receiver is non-extensible.
|
||||
enum ExtensibilityCheck {
|
||||
PERFORM_EXTENSIBILITY_CHECK,
|
||||
OMIT_EXTENSIBILITY_CHECK
|
||||
};
|
||||
|
||||
|
||||
// Indicates how aggressively the prototype should be optimized. FAST_PROTOTYPE
|
||||
// will give the fastest result by tailoring the map to the prototype, but that
|
||||
// will cause polymorphism with other objects. REGULAR_PROTOTYPE is to be used
|
||||
@ -1492,7 +1500,8 @@ class Object {
|
||||
LookupIterator* it, Handle<Object> value);
|
||||
MUST_USE_RESULT static MaybeHandle<Object> AddDataProperty(
|
||||
LookupIterator* it, Handle<Object> value, PropertyAttributes attributes,
|
||||
StrictMode strict_mode, StoreFromKeyed store_mode);
|
||||
StrictMode strict_mode, StoreFromKeyed store_mode,
|
||||
ExtensibilityCheck check);
|
||||
MUST_USE_RESULT static inline MaybeHandle<Object> GetPropertyOrElement(
|
||||
Handle<Object> object,
|
||||
Handle<Name> key);
|
||||
@ -1936,13 +1945,6 @@ class JSReceiver: public HeapObject {
|
||||
FORCE_DELETION
|
||||
};
|
||||
|
||||
// Internal properties (e.g. the hidden properties dictionary) might
|
||||
// be added even though the receiver is non-extensible.
|
||||
enum ExtensibilityCheck {
|
||||
PERFORM_EXTENSIBILITY_CHECK,
|
||||
OMIT_EXTENSIBILITY_CHECK
|
||||
};
|
||||
|
||||
DECLARE_CAST(JSReceiver)
|
||||
|
||||
MUST_USE_RESULT static MaybeHandle<Object> SetElement(
|
||||
@ -2172,12 +2174,6 @@ class JSObject: public JSReceiver {
|
||||
static Handle<Object> GetNormalizedProperty(Handle<JSObject> object,
|
||||
const LookupResult* result);
|
||||
|
||||
// Sets the property value in a normalized object given a lookup result.
|
||||
// Handles the special representation of JS global objects.
|
||||
static void SetNormalizedProperty(Handle<JSObject> object,
|
||||
const LookupResult* result,
|
||||
Handle<Object> value);
|
||||
|
||||
// Sets the property value in a normalized object given (key, value, details).
|
||||
// Handles the special representation of JS global objects.
|
||||
static void SetNormalizedProperty(Handle<JSObject> object,
|
||||
@ -2621,17 +2617,6 @@ class JSObject: public JSReceiver {
|
||||
Handle<Map> new_map,
|
||||
int expected_additional_properties);
|
||||
|
||||
static void SetPropertyToField(LookupResult* lookup, Handle<Object> value);
|
||||
|
||||
static void ConvertAndSetOwnProperty(LookupResult* lookup,
|
||||
Handle<Name> name,
|
||||
Handle<Object> value,
|
||||
PropertyAttributes attributes);
|
||||
|
||||
static void SetPropertyToFieldWithAttributes(LookupResult* lookup,
|
||||
Handle<Name> name,
|
||||
Handle<Object> value,
|
||||
PropertyAttributes attributes);
|
||||
static void GeneralizeFieldRepresentation(Handle<JSObject> object,
|
||||
int modify_index,
|
||||
Representation new_representation,
|
||||
@ -2705,29 +2690,9 @@ class JSObject: public JSReceiver {
|
||||
StrictMode strict_mode,
|
||||
bool check_prototype = true);
|
||||
|
||||
MUST_USE_RESULT static MaybeHandle<Object> SetPropertyUsingTransition(
|
||||
Handle<JSObject> object,
|
||||
LookupResult* lookup,
|
||||
Handle<Name> name,
|
||||
Handle<Object> value,
|
||||
PropertyAttributes attributes);
|
||||
MUST_USE_RESULT static MaybeHandle<Object> SetPropertyWithFailedAccessCheck(
|
||||
LookupIterator* it, Handle<Object> value, StrictMode strict_mode);
|
||||
|
||||
// Add a property to an object.
|
||||
MUST_USE_RESULT static MaybeHandle<Object> AddPropertyInternal(
|
||||
Handle<JSObject> object, Handle<Name> name, Handle<Object> value,
|
||||
PropertyAttributes attributes, StoreFromKeyed store_mode,
|
||||
ExtensibilityCheck extensibility_check, TransitionFlag flag);
|
||||
|
||||
// Add a property to a fast-case object.
|
||||
static void AddFastProperty(Handle<JSObject> object,
|
||||
Handle<Name> name,
|
||||
Handle<Object> value,
|
||||
PropertyAttributes attributes,
|
||||
StoreFromKeyed store_mode,
|
||||
TransitionFlag flag);
|
||||
|
||||
// Add a property to a slow-case object.
|
||||
static void AddSlowProperty(Handle<JSObject> object,
|
||||
Handle<Name> name,
|
||||
@ -6517,6 +6482,8 @@ class Map: public HeapObject {
|
||||
Handle<Object> value,
|
||||
PropertyAttributes attributes,
|
||||
StoreFromKeyed store_mode);
|
||||
static Handle<Map> ReconfigureDataProperty(Handle<Map> map, int descriptor,
|
||||
PropertyAttributes attributes);
|
||||
|
||||
inline void AppendDescriptor(Descriptor* desc);
|
||||
|
||||
|
@ -5041,10 +5041,8 @@ RUNTIME_FUNCTION(Runtime_DefineDataPropertyUnchecked) {
|
||||
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
|
||||
isolate, result,
|
||||
JSObject::SetOwnPropertyIgnoreAttributes(
|
||||
js_object, name, obj_value, attr,
|
||||
JSReceiver::PERFORM_EXTENSIBILITY_CHECK,
|
||||
JSReceiver::MAY_BE_STORE_FROM_KEYED,
|
||||
JSObject::DONT_FORCE_FIELD));
|
||||
js_object, name, obj_value, attr, PERFORM_EXTENSIBILITY_CHECK,
|
||||
JSReceiver::MAY_BE_STORE_FROM_KEYED, JSObject::DONT_FORCE_FIELD));
|
||||
return *result;
|
||||
}
|
||||
|
||||
@ -5198,7 +5196,7 @@ MaybeHandle<Object> Runtime::DefineObjectProperty(
|
||||
} else {
|
||||
if (name->IsString()) name = String::Flatten(Handle<String>::cast(name));
|
||||
return JSObject::SetOwnPropertyIgnoreAttributes(
|
||||
js_object, name, value, attr, JSReceiver::PERFORM_EXTENSIBILITY_CHECK,
|
||||
js_object, name, value, attr, PERFORM_EXTENSIBILITY_CHECK,
|
||||
store_from_keyed);
|
||||
}
|
||||
}
|
||||
@ -5214,7 +5212,7 @@ MaybeHandle<Object> Runtime::DefineObjectProperty(
|
||||
SLOPPY, false, DEFINE_PROPERTY);
|
||||
} else {
|
||||
return JSObject::SetOwnPropertyIgnoreAttributes(
|
||||
js_object, name, value, attr, JSReceiver::PERFORM_EXTENSIBILITY_CHECK,
|
||||
js_object, name, value, attr, PERFORM_EXTENSIBILITY_CHECK,
|
||||
store_from_keyed);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user