| 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 |