OLD | NEW |
1 // Copyright 2013 the V8 project authors. All rights reserved. | 1 // Copyright 2013 the V8 project authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "src/v8.h" | 5 #include "src/v8.h" |
6 | 6 |
7 #include "src/accessors.h" | 7 #include "src/accessors.h" |
8 #include "src/allocation-site-scopes.h" | 8 #include "src/allocation-site-scopes.h" |
9 #include "src/api.h" | 9 #include "src/api.h" |
10 #include "src/arguments.h" | 10 #include "src/arguments.h" |
(...skipping 6084 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
6095 } | 6095 } |
6096 | 6096 |
6097 Isolate* isolate = object->GetIsolate(); | 6097 Isolate* isolate = object->GetIsolate(); |
6098 Handle<AccessorPair> accessors = isolate->factory()->NewAccessorPair(); | 6098 Handle<AccessorPair> accessors = isolate->factory()->NewAccessorPair(); |
6099 accessors->SetComponents(*getter, *setter); | 6099 accessors->SetComponents(*getter, *setter); |
6100 | 6100 |
6101 SetElementCallback(object, index, accessors, attributes); | 6101 SetElementCallback(object, index, accessors, attributes); |
6102 } | 6102 } |
6103 | 6103 |
6104 | 6104 |
6105 Handle<AccessorPair> JSObject::CreateAccessorPairFor(Handle<JSObject> object, | |
6106 Handle<Name> name) { | |
6107 Isolate* isolate = object->GetIsolate(); | |
6108 LookupResult result(isolate); | |
6109 object->LookupOwnRealNamedProperty(name, &result); | |
6110 if (result.IsPropertyCallbacks()) { | |
6111 // Note that the result can actually have IsDontDelete() == true when we | |
6112 // e.g. have to fall back to the slow case while adding a setter after | |
6113 // successfully reusing a map transition for a getter. Nevertheless, this is | |
6114 // OK, because the assertion only holds for the whole addition of both | |
6115 // accessors, not for the addition of each part. See first comment in | |
6116 // DefinePropertyAccessor below. | |
6117 Object* obj = result.GetCallbackObject(); | |
6118 if (obj->IsAccessorPair()) { | |
6119 return AccessorPair::Copy(handle(AccessorPair::cast(obj), isolate)); | |
6120 } | |
6121 } | |
6122 return isolate->factory()->NewAccessorPair(); | |
6123 } | |
6124 | |
6125 | |
6126 void JSObject::DefinePropertyAccessor(Handle<JSObject> object, | |
6127 Handle<Name> name, | |
6128 Handle<Object> getter, | |
6129 Handle<Object> setter, | |
6130 PropertyAttributes attributes) { | |
6131 // We could assert that the property is configurable here, but we would need | |
6132 // to do a lookup, which seems to be a bit of overkill. | |
6133 bool only_attribute_changes = getter->IsNull() && setter->IsNull(); | |
6134 if (object->HasFastProperties() && !only_attribute_changes && | |
6135 (object->map()->NumberOfOwnDescriptors() <= kMaxNumberOfDescriptors)) { | |
6136 bool getterOk = getter->IsNull() || | |
6137 DefineFastAccessor(object, name, ACCESSOR_GETTER, getter, attributes); | |
6138 bool setterOk = !getterOk || setter->IsNull() || | |
6139 DefineFastAccessor(object, name, ACCESSOR_SETTER, setter, attributes); | |
6140 if (getterOk && setterOk) return; | |
6141 } | |
6142 | |
6143 Handle<AccessorPair> accessors = CreateAccessorPairFor(object, name); | |
6144 accessors->SetComponents(*getter, *setter); | |
6145 | |
6146 SetPropertyCallback(object, name, accessors, attributes); | |
6147 } | |
6148 | |
6149 | |
6150 bool Map::DictionaryElementsInPrototypeChainOnly() { | 6105 bool Map::DictionaryElementsInPrototypeChainOnly() { |
6151 if (IsDictionaryElementsKind(elements_kind())) { | 6106 if (IsDictionaryElementsKind(elements_kind())) { |
6152 return false; | 6107 return false; |
6153 } | 6108 } |
6154 | 6109 |
6155 for (PrototypeIterator iter(this); !iter.IsAtEnd(); iter.Advance()) { | 6110 for (PrototypeIterator iter(this); !iter.IsAtEnd(); iter.Advance()) { |
6156 if (iter.GetCurrent()->IsJSProxy()) { | 6111 if (iter.GetCurrent()->IsJSProxy()) { |
6157 // Be conservative, don't walk into proxies. | 6112 // Be conservative, don't walk into proxies. |
6158 return true; | 6113 return true; |
6159 } | 6114 } |
(...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
6298 if (preexists && (it.property_kind() == LookupIterator::DATA || | 6253 if (preexists && (it.property_kind() == LookupIterator::DATA || |
6299 it.GetAccessors()->IsAccessorInfo())) { | 6254 it.GetAccessors()->IsAccessorInfo())) { |
6300 old_value = GetProperty(&it).ToHandleChecked(); | 6255 old_value = GetProperty(&it).ToHandleChecked(); |
6301 } | 6256 } |
6302 } | 6257 } |
6303 } | 6258 } |
6304 | 6259 |
6305 if (is_element) { | 6260 if (is_element) { |
6306 DefineElementAccessor(object, index, getter, setter, attributes); | 6261 DefineElementAccessor(object, index, getter, setter, attributes); |
6307 } else { | 6262 } else { |
6308 DefinePropertyAccessor(object, name, getter, setter, attributes); | 6263 DCHECK(getter->IsSpecFunction() || getter->IsUndefined() || |
| 6264 getter->IsNull()); |
| 6265 DCHECK(setter->IsSpecFunction() || setter->IsUndefined() || |
| 6266 setter->IsNull()); |
| 6267 // At least one of the accessors needs to be a new value. |
| 6268 DCHECK(!getter->IsNull() || !setter->IsNull()); |
| 6269 LookupIterator it(object, name, LookupIterator::CHECK_PROPERTY); |
| 6270 if (!getter->IsNull()) { |
| 6271 it.TransitionToAccessorProperty(ACCESSOR_GETTER, getter, attributes); |
| 6272 } |
| 6273 if (!setter->IsNull()) { |
| 6274 it.TransitionToAccessorProperty(ACCESSOR_SETTER, setter, attributes); |
| 6275 } |
6309 } | 6276 } |
6310 | 6277 |
6311 if (is_observed) { | 6278 if (is_observed) { |
6312 const char* type = preexists ? "reconfigure" : "add"; | 6279 const char* type = preexists ? "reconfigure" : "add"; |
6313 EnqueueChangeRecord(object, type, name, old_value); | 6280 EnqueueChangeRecord(object, type, name, old_value); |
6314 } | 6281 } |
6315 | 6282 |
6316 return isolate->factory()->undefined_value(); | 6283 return isolate->factory()->undefined_value(); |
6317 } | 6284 } |
6318 | 6285 |
6319 | 6286 |
6320 static bool TryAccessorTransition(Handle<JSObject> self, | |
6321 Handle<Map> transitioned_map, | |
6322 int target_descriptor, | |
6323 AccessorComponent component, | |
6324 Handle<Object> accessor, | |
6325 PropertyAttributes attributes) { | |
6326 DescriptorArray* descs = transitioned_map->instance_descriptors(); | |
6327 PropertyDetails details = descs->GetDetails(target_descriptor); | |
6328 | |
6329 // If the transition target was not callbacks, fall back to the slow case. | |
6330 if (details.type() != CALLBACKS) return false; | |
6331 Object* descriptor = descs->GetCallbacksObject(target_descriptor); | |
6332 if (!descriptor->IsAccessorPair()) return false; | |
6333 | |
6334 Object* target_accessor = AccessorPair::cast(descriptor)->get(component); | |
6335 PropertyAttributes target_attributes = details.attributes(); | |
6336 | |
6337 // Reuse transition if adding same accessor with same attributes. | |
6338 if (target_accessor == *accessor && target_attributes == attributes) { | |
6339 JSObject::MigrateToMap(self, transitioned_map); | |
6340 return true; | |
6341 } | |
6342 | |
6343 // If either not the same accessor, or not the same attributes, fall back to | |
6344 // the slow case. | |
6345 return false; | |
6346 } | |
6347 | |
6348 | |
6349 bool JSObject::DefineFastAccessor(Handle<JSObject> object, | |
6350 Handle<Name> name, | |
6351 AccessorComponent component, | |
6352 Handle<Object> accessor, | |
6353 PropertyAttributes attributes) { | |
6354 DCHECK(accessor->IsSpecFunction() || accessor->IsUndefined()); | |
6355 Isolate* isolate = object->GetIsolate(); | |
6356 LookupResult result(isolate); | |
6357 object->LookupOwn(name, &result); | |
6358 | |
6359 if (result.IsFound() && !result.IsPropertyCallbacks()) { | |
6360 return false; | |
6361 } | |
6362 | |
6363 // Return success if the same accessor with the same attributes already exist. | |
6364 AccessorPair* source_accessors = NULL; | |
6365 if (result.IsPropertyCallbacks()) { | |
6366 Object* callback_value = result.GetCallbackObject(); | |
6367 if (callback_value->IsAccessorPair()) { | |
6368 source_accessors = AccessorPair::cast(callback_value); | |
6369 Object* entry = source_accessors->get(component); | |
6370 if (entry == *accessor && result.GetAttributes() == attributes) { | |
6371 return true; | |
6372 } | |
6373 } else { | |
6374 return false; | |
6375 } | |
6376 | |
6377 int descriptor_number = result.GetDescriptorIndex(); | |
6378 | |
6379 object->map()->LookupTransition(*object, *name, &result); | |
6380 | |
6381 if (result.IsFound()) { | |
6382 Handle<Map> target(result.GetTransitionTarget()); | |
6383 DCHECK(target->NumberOfOwnDescriptors() == | |
6384 object->map()->NumberOfOwnDescriptors()); | |
6385 // This works since descriptors are sorted in order of addition. | |
6386 DCHECK(Name::Equals( | |
6387 handle(object->map()->instance_descriptors()->GetKey( | |
6388 descriptor_number)), | |
6389 name)); | |
6390 return TryAccessorTransition(object, target, descriptor_number, | |
6391 component, accessor, attributes); | |
6392 } | |
6393 } else { | |
6394 // If not, lookup a transition. | |
6395 object->map()->LookupTransition(*object, *name, &result); | |
6396 | |
6397 // If there is a transition, try to follow it. | |
6398 if (result.IsFound()) { | |
6399 Handle<Map> target(result.GetTransitionTarget()); | |
6400 int descriptor_number = target->LastAdded(); | |
6401 DCHECK(Name::Equals(name, | |
6402 handle(target->instance_descriptors()->GetKey(descriptor_number)))); | |
6403 return TryAccessorTransition(object, target, descriptor_number, | |
6404 component, accessor, attributes); | |
6405 } | |
6406 } | |
6407 | |
6408 // If there is no transition yet, add a transition to the a new accessor pair | |
6409 // containing the accessor. Allocate a new pair if there were no source | |
6410 // accessors. Otherwise, copy the pair and modify the accessor. | |
6411 Handle<AccessorPair> accessors = source_accessors != NULL | |
6412 ? AccessorPair::Copy(Handle<AccessorPair>(source_accessors)) | |
6413 : isolate->factory()->NewAccessorPair(); | |
6414 accessors->set(component, *accessor); | |
6415 | |
6416 CallbacksDescriptor new_accessors_desc(name, accessors, attributes); | |
6417 Handle<Map> new_map = Map::CopyInsertDescriptor( | |
6418 handle(object->map()), &new_accessors_desc, INSERT_TRANSITION); | |
6419 | |
6420 JSObject::MigrateToMap(object, new_map); | |
6421 return true; | |
6422 } | |
6423 | |
6424 | |
6425 MaybeHandle<Object> JSObject::SetAccessor(Handle<JSObject> object, | 6287 MaybeHandle<Object> JSObject::SetAccessor(Handle<JSObject> object, |
6426 Handle<AccessorInfo> info) { | 6288 Handle<AccessorInfo> info) { |
6427 Isolate* isolate = object->GetIsolate(); | 6289 Isolate* isolate = object->GetIsolate(); |
6428 Factory* factory = isolate->factory(); | 6290 Factory* factory = isolate->factory(); |
6429 Handle<Name> name(Name::cast(info->name())); | 6291 Handle<Name> name(Name::cast(info->name())); |
6430 | 6292 |
6431 // Check access rights if needed. | 6293 // Check access rights if needed. |
6432 if (object->IsAccessCheckNeeded() && | 6294 if (object->IsAccessCheckNeeded() && |
6433 !isolate->MayNamedAccess(object, name, v8::ACCESS_SET)) { | 6295 !isolate->MayNamedAccess(object, name, v8::ACCESS_SET)) { |
6434 isolate->ReportFailedAccessCheck(object, v8::ACCESS_SET); | 6296 isolate->ReportFailedAccessCheck(object, v8::ACCESS_SET); |
(...skipping 620 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
7055 // Dictionaries have to be reconfigured in-place. | 6917 // Dictionaries have to be reconfigured in-place. |
7056 DCHECK(!map->is_dictionary_map()); | 6918 DCHECK(!map->is_dictionary_map()); |
7057 | 6919 |
7058 // For now, give up on transitioning and just create a unique map. | 6920 // For now, give up on transitioning and just create a unique map. |
7059 // TODO(verwaest/ishell): Cache transitions with different attributes. | 6921 // TODO(verwaest/ishell): Cache transitions with different attributes. |
7060 return CopyGeneralizeAllRepresentations(map, descriptor, FORCE_FIELD, | 6922 return CopyGeneralizeAllRepresentations(map, descriptor, FORCE_FIELD, |
7061 attributes, "attributes mismatch"); | 6923 attributes, "attributes mismatch"); |
7062 } | 6924 } |
7063 | 6925 |
7064 | 6926 |
| 6927 Handle<Map> Map::TransitionToAccessorProperty(Handle<Map> map, |
| 6928 Handle<Name> name, |
| 6929 AccessorComponent component, |
| 6930 Handle<Object> accessor, |
| 6931 PropertyAttributes attributes) { |
| 6932 Isolate* isolate = name->GetIsolate(); |
| 6933 |
| 6934 // Dictionary maps can always have additional data properties. |
| 6935 if (map->is_dictionary_map()) { |
| 6936 // For global objects, property cells are inlined. We need to change the |
| 6937 // map. |
| 6938 if (map->IsGlobalObjectMap()) return Copy(map); |
| 6939 return map; |
| 6940 } |
| 6941 |
| 6942 // Migrate to the newest map before transitioning to the new property. |
| 6943 if (map->is_deprecated()) map = Update(map); |
| 6944 |
| 6945 PropertyNormalizationMode mode = map->is_prototype_map() |
| 6946 ? KEEP_INOBJECT_PROPERTIES |
| 6947 : CLEAR_INOBJECT_PROPERTIES; |
| 6948 |
| 6949 int index = map->SearchTransition(*name); |
| 6950 if (index != TransitionArray::kNotFound) { |
| 6951 Handle<Map> transition(map->GetTransition(index)); |
| 6952 DescriptorArray* descriptors = transition->instance_descriptors(); |
| 6953 // Fast path, assume that we're modifying the last added descriptor. |
| 6954 int descriptor = transition->LastAdded(); |
| 6955 if (descriptors->GetKey(descriptor) != *name) { |
| 6956 // If not, search for the descriptor. |
| 6957 descriptor = descriptors->SearchWithCache(*name, *transition); |
| 6958 } |
| 6959 |
| 6960 if (descriptors->GetDetails(descriptor).type() != CALLBACKS) { |
| 6961 return Map::Normalize(map, mode); |
| 6962 } |
| 6963 |
| 6964 // TODO(verwaest): Handle attributes better. |
| 6965 if (descriptors->GetDetails(descriptor).attributes() != attributes) { |
| 6966 return Map::Normalize(map, mode); |
| 6967 } |
| 6968 |
| 6969 Handle<Object> maybe_pair(descriptors->GetValue(descriptor), isolate); |
| 6970 if (!maybe_pair->IsAccessorPair()) { |
| 6971 return Map::Normalize(map, mode); |
| 6972 } |
| 6973 |
| 6974 Handle<AccessorPair> pair = Handle<AccessorPair>::cast(maybe_pair); |
| 6975 if (pair->get(component) != *accessor) { |
| 6976 return Map::Normalize(map, mode); |
| 6977 } |
| 6978 |
| 6979 return transition; |
| 6980 } |
| 6981 |
| 6982 Handle<AccessorPair> pair; |
| 6983 DescriptorArray* old_descriptors = map->instance_descriptors(); |
| 6984 int descriptor = old_descriptors->SearchWithCache(*name, *map); |
| 6985 if (descriptor != DescriptorArray::kNotFound) { |
| 6986 PropertyDetails old_details = old_descriptors->GetDetails(descriptor); |
| 6987 if (old_details.type() != CALLBACKS) { |
| 6988 return Map::Normalize(map, mode); |
| 6989 } |
| 6990 |
| 6991 if (old_details.attributes() != attributes) { |
| 6992 return Map::Normalize(map, mode); |
| 6993 } |
| 6994 |
| 6995 Handle<Object> maybe_pair(old_descriptors->GetValue(descriptor), isolate); |
| 6996 if (!maybe_pair->IsAccessorPair()) { |
| 6997 return Map::Normalize(map, mode); |
| 6998 } |
| 6999 |
| 7000 Object* current = Handle<AccessorPair>::cast(maybe_pair)->get(component); |
| 7001 if (current == *accessor) return map; |
| 7002 |
| 7003 if (!current->IsTheHole()) { |
| 7004 return Map::Normalize(map, mode); |
| 7005 } |
| 7006 |
| 7007 pair = AccessorPair::Copy(Handle<AccessorPair>::cast(maybe_pair)); |
| 7008 } else if (map->NumberOfOwnDescriptors() >= kMaxNumberOfDescriptors || |
| 7009 map->TooManyFastProperties(CERTAINLY_NOT_STORE_FROM_KEYED)) { |
| 7010 return Map::Normalize(map, CLEAR_INOBJECT_PROPERTIES); |
| 7011 } else { |
| 7012 pair = isolate->factory()->NewAccessorPair(); |
| 7013 } |
| 7014 |
| 7015 pair->set(component, *accessor); |
| 7016 TransitionFlag flag = INSERT_TRANSITION; |
| 7017 CallbacksDescriptor new_desc(name, pair, attributes); |
| 7018 return Map::CopyInsertDescriptor(map, &new_desc, flag); |
| 7019 } |
| 7020 |
| 7021 |
7065 Handle<Map> Map::CopyAddDescriptor(Handle<Map> map, | 7022 Handle<Map> Map::CopyAddDescriptor(Handle<Map> map, |
7066 Descriptor* descriptor, | 7023 Descriptor* descriptor, |
7067 TransitionFlag flag) { | 7024 TransitionFlag flag) { |
7068 Handle<DescriptorArray> descriptors(map->instance_descriptors()); | 7025 Handle<DescriptorArray> descriptors(map->instance_descriptors()); |
7069 | 7026 |
7070 // Ensure the key is unique. | 7027 // Ensure the key is unique. |
7071 descriptor->KeyToUniqueName(); | 7028 descriptor->KeyToUniqueName(); |
7072 | 7029 |
7073 if (flag == INSERT_TRANSITION && | 7030 if (flag == INSERT_TRANSITION && |
7074 map->owns_descriptors() && | 7031 map->owns_descriptors() && |
(...skipping 9530 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
16605 #define ERROR_MESSAGES_TEXTS(C, T) T, | 16562 #define ERROR_MESSAGES_TEXTS(C, T) T, |
16606 static const char* error_messages_[] = { | 16563 static const char* error_messages_[] = { |
16607 ERROR_MESSAGES_LIST(ERROR_MESSAGES_TEXTS) | 16564 ERROR_MESSAGES_LIST(ERROR_MESSAGES_TEXTS) |
16608 }; | 16565 }; |
16609 #undef ERROR_MESSAGES_TEXTS | 16566 #undef ERROR_MESSAGES_TEXTS |
16610 return error_messages_[reason]; | 16567 return error_messages_[reason]; |
16611 } | 16568 } |
16612 | 16569 |
16613 | 16570 |
16614 } } // namespace v8::internal | 16571 } } // namespace v8::internal |
OLD | NEW |