|  | // Copyright 2019 the V8 project authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "src/objects/js-objects.h" | 
|  |  | 
|  | #include "src/api/api-arguments-inl.h" | 
|  | #include "src/common/globals.h" | 
|  | #include "src/date/date.h" | 
|  | #include "src/execution/arguments.h" | 
|  | #include "src/execution/frames.h" | 
|  | #include "src/execution/isolate.h" | 
|  | #include "src/handles/handles-inl.h" | 
|  | #include "src/handles/maybe-handles.h" | 
|  | #include "src/heap/factory-inl.h" | 
|  | #include "src/heap/heap-inl.h" | 
|  | #include "src/heap/memory-chunk.h" | 
|  | #include "src/init/bootstrapper.h" | 
|  | #include "src/logging/counters.h" | 
|  | #include "src/logging/log.h" | 
|  | #include "src/objects/allocation-site-inl.h" | 
|  | #include "src/objects/api-callbacks.h" | 
|  | #include "src/objects/arguments-inl.h" | 
|  | #include "src/objects/dictionary.h" | 
|  | #include "src/objects/elements.h" | 
|  | #include "src/objects/field-type.h" | 
|  | #include "src/objects/fixed-array.h" | 
|  | #include "src/objects/heap-number.h" | 
|  | #include "src/objects/js-array-buffer.h" | 
|  | #include "src/objects/js-array-inl.h" | 
|  | #include "src/objects/layout-descriptor.h" | 
|  | #include "src/objects/lookup.h" | 
|  | #include "src/objects/objects-inl.h" | 
|  | #ifdef V8_INTL_SUPPORT | 
|  | #include "src/objects/js-break-iterator.h" | 
|  | #include "src/objects/js-collator.h" | 
|  | #endif  // V8_INTL_SUPPORT | 
|  | #include "src/objects/js-collection.h" | 
|  | #ifdef V8_INTL_SUPPORT | 
|  | #include "src/objects/js-date-time-format.h" | 
|  | #include "src/objects/js-display-names.h" | 
|  | #endif  // V8_INTL_SUPPORT | 
|  | #include "src/objects/js-generator-inl.h" | 
|  | #ifdef V8_INTL_SUPPORT | 
|  | #include "src/objects/js-list-format.h" | 
|  | #include "src/objects/js-locale.h" | 
|  | #include "src/objects/js-number-format.h" | 
|  | #include "src/objects/js-plural-rules.h" | 
|  | #endif  // V8_INTL_SUPPORT | 
|  | #include "src/objects/js-promise.h" | 
|  | #include "src/objects/js-regexp-inl.h" | 
|  | #include "src/objects/js-regexp-string-iterator.h" | 
|  | #ifdef V8_INTL_SUPPORT | 
|  | #include "src/objects/js-relative-time-format.h" | 
|  | #include "src/objects/js-segment-iterator.h" | 
|  | #include "src/objects/js-segmenter.h" | 
|  | #include "src/objects/js-segments.h" | 
|  | #endif  // V8_INTL_SUPPORT | 
|  | #include "src/objects/js-weak-refs.h" | 
|  | #include "src/objects/map-inl.h" | 
|  | #include "src/objects/module.h" | 
|  | #include "src/objects/oddball.h" | 
|  | #include "src/objects/property-cell.h" | 
|  | #include "src/objects/property-descriptor.h" | 
|  | #include "src/objects/property.h" | 
|  | #include "src/objects/prototype-info.h" | 
|  | #include "src/objects/prototype.h" | 
|  | #include "src/objects/shared-function-info.h" | 
|  | #include "src/objects/transitions.h" | 
|  | #include "src/strings/string-builder-inl.h" | 
|  | #include "src/strings/string-stream.h" | 
|  | #include "src/utils/ostreams.h" | 
|  | #include "src/wasm/wasm-objects.h" | 
|  |  | 
|  | namespace v8 { | 
|  | namespace internal { | 
|  |  | 
|  | // static | 
|  | Maybe<bool> JSReceiver::HasProperty(LookupIterator* it) { | 
|  | for (; it->IsFound(); it->Next()) { | 
|  | switch (it->state()) { | 
|  | case LookupIterator::NOT_FOUND: | 
|  | case LookupIterator::TRANSITION: | 
|  | UNREACHABLE(); | 
|  | case LookupIterator::JSPROXY: | 
|  | return JSProxy::HasProperty(it->isolate(), it->GetHolder<JSProxy>(), | 
|  | it->GetName()); | 
|  | case LookupIterator::INTERCEPTOR: { | 
|  | Maybe<PropertyAttributes> result = | 
|  | JSObject::GetPropertyAttributesWithInterceptor(it); | 
|  | if (result.IsNothing()) return Nothing<bool>(); | 
|  | if (result.FromJust() != ABSENT) return Just(true); | 
|  | break; | 
|  | } | 
|  | case LookupIterator::ACCESS_CHECK: { | 
|  | if (it->HasAccess()) break; | 
|  | Maybe<PropertyAttributes> result = | 
|  | JSObject::GetPropertyAttributesWithFailedAccessCheck(it); | 
|  | if (result.IsNothing()) return Nothing<bool>(); | 
|  | return Just(result.FromJust() != ABSENT); | 
|  | } | 
|  | case LookupIterator::INTEGER_INDEXED_EXOTIC: | 
|  | // TypedArray out-of-bounds access. | 
|  | return Just(false); | 
|  | case LookupIterator::ACCESSOR: | 
|  | case LookupIterator::DATA: | 
|  | return Just(true); | 
|  | } | 
|  | } | 
|  | return Just(false); | 
|  | } | 
|  |  | 
|  | // static | 
|  | Maybe<bool> JSReceiver::HasOwnProperty(Handle<JSReceiver> object, | 
|  | Handle<Name> name) { | 
|  | if (object->IsJSModuleNamespace()) { | 
|  | PropertyDescriptor desc; | 
|  | return JSReceiver::GetOwnPropertyDescriptor(object->GetIsolate(), object, | 
|  | name, &desc); | 
|  | } | 
|  |  | 
|  | if (object->IsJSObject()) {  // Shortcut. | 
|  | Isolate* isolate = object->GetIsolate(); | 
|  | LookupIterator::Key key(isolate, name); | 
|  | LookupIterator it(isolate, object, key, LookupIterator::OWN); | 
|  | return HasProperty(&it); | 
|  | } | 
|  |  | 
|  | Maybe<PropertyAttributes> attributes = | 
|  | JSReceiver::GetOwnPropertyAttributes(object, name); | 
|  | MAYBE_RETURN(attributes, Nothing<bool>()); | 
|  | return Just(attributes.FromJust() != ABSENT); | 
|  | } | 
|  |  | 
|  | Handle<Object> JSReceiver::GetDataProperty(LookupIterator* it, | 
|  | AllocationPolicy allocation_policy) { | 
|  | for (; it->IsFound(); it->Next()) { | 
|  | switch (it->state()) { | 
|  | case LookupIterator::INTERCEPTOR: | 
|  | case LookupIterator::NOT_FOUND: | 
|  | case LookupIterator::TRANSITION: | 
|  | UNREACHABLE(); | 
|  | case LookupIterator::ACCESS_CHECK: | 
|  | // Support calling this method without an active context, but refuse | 
|  | // access to access-checked objects in that case. | 
|  | if (!it->isolate()->context().is_null() && it->HasAccess()) continue; | 
|  | V8_FALLTHROUGH; | 
|  | case LookupIterator::JSPROXY: | 
|  | it->NotFound(); | 
|  | return it->isolate()->factory()->undefined_value(); | 
|  | case LookupIterator::ACCESSOR: | 
|  | // TODO(verwaest): For now this doesn't call into AccessorInfo, since | 
|  | // clients don't need it. Update once relevant. | 
|  | it->NotFound(); | 
|  | return it->isolate()->factory()->undefined_value(); | 
|  | case LookupIterator::INTEGER_INDEXED_EXOTIC: | 
|  | return it->isolate()->factory()->undefined_value(); | 
|  | case LookupIterator::DATA: | 
|  | return it->GetDataValue(allocation_policy); | 
|  | } | 
|  | } | 
|  | return it->isolate()->factory()->undefined_value(); | 
|  | } | 
|  |  | 
|  | // static | 
|  | Maybe<bool> JSReceiver::HasInPrototypeChain(Isolate* isolate, | 
|  | Handle<JSReceiver> object, | 
|  | Handle<Object> proto) { | 
|  | PrototypeIterator iter(isolate, object, kStartAtReceiver); | 
|  | while (true) { | 
|  | if (!iter.AdvanceFollowingProxies()) return Nothing<bool>(); | 
|  | if (iter.IsAtEnd()) return Just(false); | 
|  | if (PrototypeIterator::GetCurrent(iter).is_identical_to(proto)) { | 
|  | return Just(true); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | bool HasExcludedProperty( | 
|  | const ScopedVector<Handle<Object>>* excluded_properties, | 
|  | Handle<Object> search_element) { | 
|  | // TODO(gsathya): Change this to be a hashtable. | 
|  | for (int i = 0; i < excluded_properties->length(); i++) { | 
|  | if (search_element->SameValue(*excluded_properties->at(i))) { | 
|  | return true; | 
|  | } | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | V8_WARN_UNUSED_RESULT Maybe<bool> FastAssign( | 
|  | Handle<JSReceiver> target, Handle<Object> source, | 
|  | const ScopedVector<Handle<Object>>* excluded_properties, bool use_set) { | 
|  | // Non-empty strings are the only non-JSReceivers that need to be handled | 
|  | // explicitly by Object.assign. | 
|  | if (!source->IsJSReceiver()) { | 
|  | return Just(!source->IsString() || String::cast(*source).length() == 0); | 
|  | } | 
|  |  | 
|  | Isolate* isolate = target->GetIsolate(); | 
|  |  | 
|  | // If the target is deprecated, the object will be updated on first store. If | 
|  | // the source for that store equals the target, this will invalidate the | 
|  | // cached representation of the source. Preventively upgrade the target. | 
|  | // Do this on each iteration since any property load could cause deprecation. | 
|  | if (target->map().is_deprecated()) { | 
|  | JSObject::MigrateInstance(isolate, Handle<JSObject>::cast(target)); | 
|  | } | 
|  |  | 
|  | Handle<Map> map(JSReceiver::cast(*source).map(), isolate); | 
|  |  | 
|  | if (!map->IsJSObjectMap()) return Just(false); | 
|  | if (!map->OnlyHasSimpleProperties()) return Just(false); | 
|  |  | 
|  | Handle<JSObject> from = Handle<JSObject>::cast(source); | 
|  | if (from->elements() != ReadOnlyRoots(isolate).empty_fixed_array()) { | 
|  | return Just(false); | 
|  | } | 
|  |  | 
|  | Handle<DescriptorArray> descriptors(map->instance_descriptors(kRelaxedLoad), | 
|  | isolate); | 
|  |  | 
|  | bool stable = true; | 
|  |  | 
|  | for (InternalIndex i : map->IterateOwnDescriptors()) { | 
|  | HandleScope inner_scope(isolate); | 
|  |  | 
|  | Handle<Name> next_key(descriptors->GetKey(i), isolate); | 
|  | Handle<Object> prop_value; | 
|  | // Directly decode from the descriptor array if |from| did not change shape. | 
|  | if (stable) { | 
|  | DCHECK_EQ(from->map(), *map); | 
|  | DCHECK_EQ(*descriptors, map->instance_descriptors(kRelaxedLoad)); | 
|  |  | 
|  | PropertyDetails details = descriptors->GetDetails(i); | 
|  | if (!details.IsEnumerable()) continue; | 
|  | if (details.kind() == kData) { | 
|  | if (details.location() == kDescriptor) { | 
|  | prop_value = handle(descriptors->GetStrongValue(i), isolate); | 
|  | } else { | 
|  | Representation representation = details.representation(); | 
|  | FieldIndex index = FieldIndex::ForPropertyIndex( | 
|  | *map, details.field_index(), representation); | 
|  | prop_value = JSObject::FastPropertyAt(from, representation, index); | 
|  | } | 
|  | } else { | 
|  | LookupIterator it(isolate, from, next_key, | 
|  | LookupIterator::OWN_SKIP_INTERCEPTOR); | 
|  | ASSIGN_RETURN_ON_EXCEPTION_VALUE( | 
|  | isolate, prop_value, Object::GetProperty(&it), Nothing<bool>()); | 
|  | stable = from->map() == *map; | 
|  | descriptors.PatchValue(map->instance_descriptors(kRelaxedLoad)); | 
|  | } | 
|  | } else { | 
|  | // If the map did change, do a slower lookup. We are still guaranteed that | 
|  | // the object has a simple shape, and that the key is a name. | 
|  | LookupIterator it(isolate, from, next_key, from, | 
|  | LookupIterator::OWN_SKIP_INTERCEPTOR); | 
|  | if (!it.IsFound()) continue; | 
|  | DCHECK(it.state() == LookupIterator::DATA || | 
|  | it.state() == LookupIterator::ACCESSOR); | 
|  | if (!it.IsEnumerable()) continue; | 
|  | ASSIGN_RETURN_ON_EXCEPTION_VALUE( | 
|  | isolate, prop_value, Object::GetProperty(&it), Nothing<bool>()); | 
|  | } | 
|  |  | 
|  | if (use_set) { | 
|  | // The lookup will walk the prototype chain, so we have to be careful | 
|  | // to treat any key correctly for any receiver/holder. | 
|  | LookupIterator::Key key(isolate, next_key); | 
|  | LookupIterator it(isolate, target, key); | 
|  | Maybe<bool> result = | 
|  | Object::SetProperty(&it, prop_value, StoreOrigin::kNamed, | 
|  | Just(ShouldThrow::kThrowOnError)); | 
|  | if (result.IsNothing()) return result; | 
|  | if (stable) { | 
|  | stable = from->map() == *map; | 
|  | descriptors.PatchValue(map->instance_descriptors(kRelaxedLoad)); | 
|  | } | 
|  | } else { | 
|  | if (excluded_properties != nullptr && | 
|  | HasExcludedProperty(excluded_properties, next_key)) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | // 4a ii 2. Perform ? CreateDataProperty(target, nextKey, propValue). | 
|  | // This is an OWN lookup, so constructing a named-mode LookupIterator | 
|  | // from {next_key} is safe. | 
|  | LookupIterator it(isolate, target, next_key, LookupIterator::OWN); | 
|  | CHECK(JSObject::CreateDataProperty(&it, prop_value, Just(kThrowOnError)) | 
|  | .FromJust()); | 
|  | } | 
|  | } | 
|  |  | 
|  | return Just(true); | 
|  | } | 
|  | }  // namespace | 
|  |  | 
|  | // static | 
|  | Maybe<bool> JSReceiver::SetOrCopyDataProperties( | 
|  | Isolate* isolate, Handle<JSReceiver> target, Handle<Object> source, | 
|  | const ScopedVector<Handle<Object>>* excluded_properties, bool use_set) { | 
|  | Maybe<bool> fast_assign = | 
|  | FastAssign(target, source, excluded_properties, use_set); | 
|  | if (fast_assign.IsNothing()) return Nothing<bool>(); | 
|  | if (fast_assign.FromJust()) return Just(true); | 
|  |  | 
|  | Handle<JSReceiver> from = Object::ToObject(isolate, source).ToHandleChecked(); | 
|  |  | 
|  | // 3b. Let keys be ? from.[[OwnPropertyKeys]](). | 
|  | Handle<FixedArray> keys; | 
|  | ASSIGN_RETURN_ON_EXCEPTION_VALUE( | 
|  | isolate, keys, | 
|  | KeyAccumulator::GetKeys(from, KeyCollectionMode::kOwnOnly, ALL_PROPERTIES, | 
|  | GetKeysConversion::kKeepNumbers), | 
|  | Nothing<bool>()); | 
|  |  | 
|  | if (!from->HasFastProperties() && target->HasFastProperties() && | 
|  | !target->IsJSGlobalProxy()) { | 
|  | // JSProxy is always in slow-mode. | 
|  | DCHECK(!target->IsJSProxy()); | 
|  | // Convert to slow properties if we're guaranteed to overflow the number of | 
|  | // descriptors. | 
|  | int source_length; | 
|  | if (from->IsJSGlobalObject()) { | 
|  | source_length = JSGlobalObject::cast(*from) | 
|  | .global_dictionary() | 
|  | .NumberOfEnumerableProperties(); | 
|  | } else if (V8_DICT_MODE_PROTOTYPES_BOOL) { | 
|  | source_length = | 
|  | from->property_dictionary_ordered().NumberOfEnumerableProperties(); | 
|  | } else { | 
|  | source_length = | 
|  | from->property_dictionary().NumberOfEnumerableProperties(); | 
|  | } | 
|  | if (source_length > kMaxNumberOfDescriptors) { | 
|  | JSObject::NormalizeProperties(isolate, Handle<JSObject>::cast(target), | 
|  | CLEAR_INOBJECT_PROPERTIES, source_length, | 
|  | "Copying data properties"); | 
|  | } | 
|  | } | 
|  |  | 
|  | // 4. Repeat for each element nextKey of keys in List order, | 
|  | for (int i = 0; i < keys->length(); ++i) { | 
|  | Handle<Object> next_key(keys->get(i), isolate); | 
|  | // 4a i. Let desc be ? from.[[GetOwnProperty]](nextKey). | 
|  | PropertyDescriptor desc; | 
|  | Maybe<bool> found = | 
|  | JSReceiver::GetOwnPropertyDescriptor(isolate, from, next_key, &desc); | 
|  | if (found.IsNothing()) return Nothing<bool>(); | 
|  | // 4a ii. If desc is not undefined and desc.[[Enumerable]] is true, then | 
|  | if (found.FromJust() && desc.enumerable()) { | 
|  | // 4a ii 1. Let propValue be ? Get(from, nextKey). | 
|  | Handle<Object> prop_value; | 
|  | ASSIGN_RETURN_ON_EXCEPTION_VALUE( | 
|  | isolate, prop_value, | 
|  | Runtime::GetObjectProperty(isolate, from, next_key), Nothing<bool>()); | 
|  |  | 
|  | if (use_set) { | 
|  | // 4c ii 2. Let status be ? Set(to, nextKey, propValue, true). | 
|  | Handle<Object> status; | 
|  | ASSIGN_RETURN_ON_EXCEPTION_VALUE( | 
|  | isolate, status, | 
|  | Runtime::SetObjectProperty(isolate, target, next_key, prop_value, | 
|  | StoreOrigin::kMaybeKeyed, | 
|  | Just(ShouldThrow::kThrowOnError)), | 
|  | Nothing<bool>()); | 
|  | } else { | 
|  | if (excluded_properties != nullptr && | 
|  | HasExcludedProperty(excluded_properties, next_key)) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | // 4a ii 2. Perform ! CreateDataProperty(target, nextKey, propValue). | 
|  | LookupIterator::Key key(isolate, next_key); | 
|  | LookupIterator it(isolate, target, key, LookupIterator::OWN); | 
|  | CHECK(JSObject::CreateDataProperty(&it, prop_value, Just(kThrowOnError)) | 
|  | .FromJust()); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return Just(true); | 
|  | } | 
|  |  | 
|  | String JSReceiver::class_name() { | 
|  | ReadOnlyRoots roots = GetReadOnlyRoots(); | 
|  | if (IsFunction()) return roots.Function_string(); | 
|  | if (IsJSArgumentsObject()) return roots.Arguments_string(); | 
|  | if (IsJSArray()) return roots.Array_string(); | 
|  | if (IsJSArrayBuffer()) { | 
|  | if (JSArrayBuffer::cast(*this).is_shared()) { | 
|  | return roots.SharedArrayBuffer_string(); | 
|  | } | 
|  | return roots.ArrayBuffer_string(); | 
|  | } | 
|  | if (IsJSArrayIterator()) return roots.ArrayIterator_string(); | 
|  | if (IsJSDate()) return roots.Date_string(); | 
|  | if (IsJSError()) return roots.Error_string(); | 
|  | if (IsJSGeneratorObject()) return roots.Generator_string(); | 
|  | if (IsJSMap()) return roots.Map_string(); | 
|  | if (IsJSMapIterator()) return roots.MapIterator_string(); | 
|  | if (IsJSProxy()) { | 
|  | return map().is_callable() ? roots.Function_string() | 
|  | : roots.Object_string(); | 
|  | } | 
|  | if (IsJSRegExp()) return roots.RegExp_string(); | 
|  | if (IsJSSet()) return roots.Set_string(); | 
|  | if (IsJSSetIterator()) return roots.SetIterator_string(); | 
|  | if (IsJSTypedArray()) { | 
|  | #define SWITCH_KIND(Type, type, TYPE, ctype)      \ | 
|  | if (map().elements_kind() == TYPE##_ELEMENTS) { \ | 
|  | return roots.Type##Array_string();            \ | 
|  | } | 
|  | TYPED_ARRAYS(SWITCH_KIND) | 
|  | #undef SWITCH_KIND | 
|  | } | 
|  | if (IsJSPrimitiveWrapper()) { | 
|  | Object value = JSPrimitiveWrapper::cast(*this).value(); | 
|  | if (value.IsBoolean()) return roots.Boolean_string(); | 
|  | if (value.IsString()) return roots.String_string(); | 
|  | if (value.IsNumber()) return roots.Number_string(); | 
|  | if (value.IsBigInt()) return roots.BigInt_string(); | 
|  | if (value.IsSymbol()) return roots.Symbol_string(); | 
|  | if (value.IsScript()) return roots.Script_string(); | 
|  | UNREACHABLE(); | 
|  | } | 
|  | if (IsJSWeakMap()) return roots.WeakMap_string(); | 
|  | if (IsJSWeakSet()) return roots.WeakSet_string(); | 
|  | if (IsJSGlobalProxy()) return roots.global_string(); | 
|  |  | 
|  | return roots.Object_string(); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  | std::pair<MaybeHandle<JSFunction>, Handle<String>> GetConstructorHelper( | 
|  | Handle<JSReceiver> receiver) { | 
|  | Isolate* isolate = receiver->GetIsolate(); | 
|  |  | 
|  | // If the object was instantiated simply with base == new.target, the | 
|  | // constructor on the map provides the most accurate name. | 
|  | // Don't provide the info for prototypes, since their constructors are | 
|  | // reclaimed and replaced by Object in OptimizeAsPrototype. | 
|  | if (!receiver->IsJSProxy() && receiver->map().new_target_is_base() && | 
|  | !receiver->map().is_prototype_map()) { | 
|  | Object maybe_constructor = receiver->map().GetConstructor(); | 
|  | if (maybe_constructor.IsJSFunction()) { | 
|  | JSFunction constructor = JSFunction::cast(maybe_constructor); | 
|  | String name = constructor.shared().DebugName(); | 
|  | if (name.length() != 0 && | 
|  | !name.Equals(ReadOnlyRoots(isolate).Object_string())) { | 
|  | return std::make_pair(handle(constructor, isolate), | 
|  | handle(name, isolate)); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | LookupIterator it_tag(isolate, receiver, | 
|  | isolate->factory()->to_string_tag_symbol(), receiver, | 
|  | LookupIterator::PROTOTYPE_CHAIN_SKIP_INTERCEPTOR); | 
|  | Handle<Object> maybe_tag = JSReceiver::GetDataProperty( | 
|  | &it_tag, AllocationPolicy::kAllocationDisallowed); | 
|  | if (maybe_tag->IsString()) { | 
|  | return std::make_pair(MaybeHandle<JSFunction>(), | 
|  | Handle<String>::cast(maybe_tag)); | 
|  | } | 
|  |  | 
|  | PrototypeIterator iter(isolate, receiver); | 
|  | if (iter.IsAtEnd()) { | 
|  | return std::make_pair(MaybeHandle<JSFunction>(), | 
|  | handle(receiver->class_name(), isolate)); | 
|  | } | 
|  |  | 
|  | Handle<JSReceiver> start = PrototypeIterator::GetCurrent<JSReceiver>(iter); | 
|  | LookupIterator it(isolate, receiver, isolate->factory()->constructor_string(), | 
|  | start, LookupIterator::PROTOTYPE_CHAIN_SKIP_INTERCEPTOR); | 
|  | Handle<Object> maybe_constructor = | 
|  | JSReceiver::GetDataProperty(&it, AllocationPolicy::kAllocationDisallowed); | 
|  | if (maybe_constructor->IsJSFunction()) { | 
|  | JSFunction constructor = JSFunction::cast(*maybe_constructor); | 
|  | String name = constructor.shared().DebugName(); | 
|  |  | 
|  | if (name.length() != 0 && | 
|  | !name.Equals(ReadOnlyRoots(isolate).Object_string())) { | 
|  | return std::make_pair(handle(constructor, isolate), | 
|  | handle(name, isolate)); | 
|  | } | 
|  | } | 
|  |  | 
|  | return std::make_pair(MaybeHandle<JSFunction>(), | 
|  | handle(receiver->class_name(), isolate)); | 
|  | } | 
|  | }  // anonymous namespace | 
|  |  | 
|  | // static | 
|  | MaybeHandle<JSFunction> JSReceiver::GetConstructor( | 
|  | Handle<JSReceiver> receiver) { | 
|  | return GetConstructorHelper(receiver).first; | 
|  | } | 
|  |  | 
|  | // static | 
|  | Handle<String> JSReceiver::GetConstructorName(Handle<JSReceiver> receiver) { | 
|  | return GetConstructorHelper(receiver).second; | 
|  | } | 
|  |  | 
|  | Handle<NativeContext> JSReceiver::GetCreationContext() { | 
|  | JSReceiver receiver = *this; | 
|  | // Externals are JSObjects with null as a constructor. | 
|  | DCHECK(!receiver.IsExternal(GetIsolate())); | 
|  | Object constructor = receiver.map().GetConstructor(); | 
|  | JSFunction function; | 
|  | if (constructor.IsJSFunction()) { | 
|  | function = JSFunction::cast(constructor); | 
|  | } else if (constructor.IsFunctionTemplateInfo()) { | 
|  | // Remote objects don't have a creation context. | 
|  | return Handle<NativeContext>::null(); | 
|  | } else if (receiver.IsJSGeneratorObject()) { | 
|  | function = JSGeneratorObject::cast(receiver).function(); | 
|  | } else { | 
|  | // Functions have null as a constructor, | 
|  | // but any JSFunction knows its context immediately. | 
|  | CHECK(receiver.IsJSFunction()); | 
|  | function = JSFunction::cast(receiver); | 
|  | } | 
|  |  | 
|  | return function.has_context() | 
|  | ? Handle<NativeContext>(function.context().native_context(), | 
|  | receiver.GetIsolate()) | 
|  | : Handle<NativeContext>::null(); | 
|  | } | 
|  |  | 
|  | // static | 
|  | MaybeHandle<NativeContext> JSReceiver::GetFunctionRealm( | 
|  | Handle<JSReceiver> receiver) { | 
|  | if (receiver->IsJSProxy()) { | 
|  | return JSProxy::GetFunctionRealm(Handle<JSProxy>::cast(receiver)); | 
|  | } | 
|  |  | 
|  | if (receiver->IsJSFunction()) { | 
|  | return JSFunction::GetFunctionRealm(Handle<JSFunction>::cast(receiver)); | 
|  | } | 
|  |  | 
|  | if (receiver->IsJSBoundFunction()) { | 
|  | return JSBoundFunction::GetFunctionRealm( | 
|  | Handle<JSBoundFunction>::cast(receiver)); | 
|  | } | 
|  |  | 
|  | return JSObject::GetFunctionRealm(Handle<JSObject>::cast(receiver)); | 
|  | } | 
|  |  | 
|  | // static | 
|  | MaybeHandle<NativeContext> JSReceiver::GetContextForMicrotask( | 
|  | Handle<JSReceiver> receiver) { | 
|  | Isolate* isolate = receiver->GetIsolate(); | 
|  | while (receiver->IsJSBoundFunction() || receiver->IsJSProxy()) { | 
|  | if (receiver->IsJSBoundFunction()) { | 
|  | receiver = handle( | 
|  | Handle<JSBoundFunction>::cast(receiver)->bound_target_function(), | 
|  | isolate); | 
|  | } else { | 
|  | DCHECK(receiver->IsJSProxy()); | 
|  | Handle<Object> target(Handle<JSProxy>::cast(receiver)->target(), isolate); | 
|  | if (!target->IsJSReceiver()) return MaybeHandle<NativeContext>(); | 
|  | receiver = Handle<JSReceiver>::cast(target); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!receiver->IsJSFunction()) return MaybeHandle<NativeContext>(); | 
|  | return handle(Handle<JSFunction>::cast(receiver)->native_context(), isolate); | 
|  | } | 
|  |  | 
|  | Maybe<PropertyAttributes> JSReceiver::GetPropertyAttributes( | 
|  | LookupIterator* it) { | 
|  | for (; it->IsFound(); it->Next()) { | 
|  | switch (it->state()) { | 
|  | case LookupIterator::NOT_FOUND: | 
|  | case LookupIterator::TRANSITION: | 
|  | UNREACHABLE(); | 
|  | case LookupIterator::JSPROXY: | 
|  | return JSProxy::GetPropertyAttributes(it); | 
|  | case LookupIterator::INTERCEPTOR: { | 
|  | Maybe<PropertyAttributes> result = | 
|  | JSObject::GetPropertyAttributesWithInterceptor(it); | 
|  | if (result.IsNothing()) return result; | 
|  | if (result.FromJust() != ABSENT) return result; | 
|  | break; | 
|  | } | 
|  | case LookupIterator::ACCESS_CHECK: | 
|  | if (it->HasAccess()) break; | 
|  | return JSObject::GetPropertyAttributesWithFailedAccessCheck(it); | 
|  | case LookupIterator::INTEGER_INDEXED_EXOTIC: | 
|  | return Just(ABSENT); | 
|  | case LookupIterator::ACCESSOR: | 
|  | if (it->GetHolder<Object>()->IsJSModuleNamespace()) { | 
|  | return JSModuleNamespace::GetPropertyAttributes(it); | 
|  | } else { | 
|  | return Just(it->property_attributes()); | 
|  | } | 
|  | case LookupIterator::DATA: | 
|  | return Just(it->property_attributes()); | 
|  | } | 
|  | } | 
|  | return Just(ABSENT); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | Object SetHashAndUpdateProperties(HeapObject properties, int hash) { | 
|  | DCHECK_NE(PropertyArray::kNoHashSentinel, hash); | 
|  | DCHECK(PropertyArray::HashField::is_valid(hash)); | 
|  |  | 
|  | ReadOnlyRoots roots = properties.GetReadOnlyRoots(); | 
|  | if (properties == roots.empty_fixed_array() || | 
|  | properties == roots.empty_property_array() || | 
|  | properties == roots.empty_property_dictionary() || | 
|  | properties == roots.empty_ordered_property_dictionary()) { | 
|  | return Smi::FromInt(hash); | 
|  | } | 
|  |  | 
|  | if (properties.IsPropertyArray()) { | 
|  | PropertyArray::cast(properties).SetHash(hash); | 
|  | DCHECK_LT(0, PropertyArray::cast(properties).length()); | 
|  | return properties; | 
|  | } | 
|  |  | 
|  | if (properties.IsGlobalDictionary()) { | 
|  | GlobalDictionary::cast(properties).SetHash(hash); | 
|  | return properties; | 
|  | } | 
|  |  | 
|  | if (V8_DICT_MODE_PROTOTYPES_BOOL) { | 
|  | DCHECK(properties.IsOrderedNameDictionary()); | 
|  | OrderedNameDictionary::cast(properties).SetHash(hash); | 
|  | } else { | 
|  | DCHECK(properties.IsNameDictionary()); | 
|  | NameDictionary::cast(properties).SetHash(hash); | 
|  | } | 
|  | return properties; | 
|  | } | 
|  |  | 
|  | int GetIdentityHashHelper(JSReceiver object) { | 
|  | DisallowHeapAllocation no_gc; | 
|  | Object properties = object.raw_properties_or_hash(); | 
|  | if (properties.IsSmi()) { | 
|  | return Smi::ToInt(properties); | 
|  | } | 
|  |  | 
|  | if (properties.IsPropertyArray()) { | 
|  | return PropertyArray::cast(properties).Hash(); | 
|  | } | 
|  | if (V8_DICT_MODE_PROTOTYPES_BOOL && properties.IsOrderedNameDictionary()) { | 
|  | return OrderedNameDictionary::cast(properties).Hash(); | 
|  | } | 
|  |  | 
|  | if (properties.IsNameDictionary()) { | 
|  | DCHECK(!V8_DICT_MODE_PROTOTYPES_BOOL); | 
|  | return NameDictionary::cast(properties).Hash(); | 
|  | } | 
|  |  | 
|  | if (properties.IsGlobalDictionary()) { | 
|  | return GlobalDictionary::cast(properties).Hash(); | 
|  | } | 
|  |  | 
|  | #ifdef DEBUG | 
|  | ReadOnlyRoots roots = object.GetReadOnlyRoots(); | 
|  | DCHECK(properties == roots.empty_fixed_array() || | 
|  | properties == roots.empty_property_dictionary() || | 
|  | properties == roots.empty_ordered_property_dictionary()); | 
|  | #endif | 
|  |  | 
|  | return PropertyArray::kNoHashSentinel; | 
|  | } | 
|  | }  // namespace | 
|  |  | 
|  | void JSReceiver::SetIdentityHash(int hash) { | 
|  | DisallowHeapAllocation no_gc; | 
|  | DCHECK_NE(PropertyArray::kNoHashSentinel, hash); | 
|  | DCHECK(PropertyArray::HashField::is_valid(hash)); | 
|  |  | 
|  | HeapObject existing_properties = HeapObject::cast(raw_properties_or_hash()); | 
|  | Object new_properties = SetHashAndUpdateProperties(existing_properties, hash); | 
|  | set_raw_properties_or_hash(new_properties); | 
|  | } | 
|  |  | 
|  | void JSReceiver::SetProperties(HeapObject properties) { | 
|  | DCHECK_IMPLIES(properties.IsPropertyArray() && | 
|  | PropertyArray::cast(properties).length() == 0, | 
|  | properties == GetReadOnlyRoots().empty_property_array()); | 
|  | DisallowHeapAllocation no_gc; | 
|  | int hash = GetIdentityHashHelper(*this); | 
|  | Object new_properties = properties; | 
|  |  | 
|  | // TODO(cbruni): Make GetIdentityHashHelper return a bool so that we | 
|  | // don't have to manually compare against kNoHashSentinel. | 
|  | if (hash != PropertyArray::kNoHashSentinel) { | 
|  | new_properties = SetHashAndUpdateProperties(properties, hash); | 
|  | } | 
|  |  | 
|  | set_raw_properties_or_hash(new_properties); | 
|  | } | 
|  |  | 
|  | Object JSReceiver::GetIdentityHash() { | 
|  | DisallowHeapAllocation no_gc; | 
|  |  | 
|  | int hash = GetIdentityHashHelper(*this); | 
|  | if (hash == PropertyArray::kNoHashSentinel) { | 
|  | return GetReadOnlyRoots().undefined_value(); | 
|  | } | 
|  |  | 
|  | return Smi::FromInt(hash); | 
|  | } | 
|  |  | 
|  | // static | 
|  | Smi JSReceiver::CreateIdentityHash(Isolate* isolate, JSReceiver key) { | 
|  | DisallowHeapAllocation no_gc; | 
|  | int hash = isolate->GenerateIdentityHash(PropertyArray::HashField::kMax); | 
|  | DCHECK_NE(PropertyArray::kNoHashSentinel, hash); | 
|  |  | 
|  | key.SetIdentityHash(hash); | 
|  | return Smi::FromInt(hash); | 
|  | } | 
|  |  | 
|  | Smi JSReceiver::GetOrCreateIdentityHash(Isolate* isolate) { | 
|  | DisallowHeapAllocation no_gc; | 
|  |  | 
|  | int hash = GetIdentityHashHelper(*this); | 
|  | if (hash != PropertyArray::kNoHashSentinel) { | 
|  | return Smi::FromInt(hash); | 
|  | } | 
|  |  | 
|  | return JSReceiver::CreateIdentityHash(isolate, *this); | 
|  | } | 
|  |  | 
|  | void JSReceiver::DeleteNormalizedProperty(Handle<JSReceiver> object, | 
|  | InternalIndex entry) { | 
|  | DCHECK(!object->HasFastProperties()); | 
|  | Isolate* isolate = object->GetIsolate(); | 
|  | DCHECK(entry.is_found()); | 
|  |  | 
|  | if (object->IsJSGlobalObject()) { | 
|  | // If we have a global object, invalidate the cell and remove it from the | 
|  | // global object's dictionary. | 
|  | Handle<GlobalDictionary> dictionary( | 
|  | JSGlobalObject::cast(*object).global_dictionary(), isolate); | 
|  |  | 
|  | Handle<PropertyCell> cell(dictionary->CellAt(entry), isolate); | 
|  |  | 
|  | Handle<GlobalDictionary> new_dictionary = | 
|  | GlobalDictionary::DeleteEntry(isolate, dictionary, entry); | 
|  | JSGlobalObject::cast(*object).set_global_dictionary(*new_dictionary); | 
|  |  | 
|  | cell->ClearAndInvalidate(ReadOnlyRoots(isolate)); | 
|  | } else { | 
|  | if (V8_DICT_MODE_PROTOTYPES_BOOL) { | 
|  | Handle<OrderedNameDictionary> dictionary( | 
|  | object->property_dictionary_ordered(), isolate); | 
|  |  | 
|  | dictionary = | 
|  | OrderedNameDictionary::DeleteEntry(isolate, dictionary, entry); | 
|  | object->SetProperties(*dictionary); | 
|  | } else { | 
|  | Handle<NameDictionary> dictionary(object->property_dictionary(), isolate); | 
|  |  | 
|  | dictionary = NameDictionary::DeleteEntry(isolate, dictionary, entry); | 
|  | object->SetProperties(*dictionary); | 
|  | } | 
|  | } | 
|  | if (object->map().is_prototype_map()) { | 
|  | // Invalidate prototype validity cell as this may invalidate transitioning | 
|  | // store IC handlers. | 
|  | JSObject::InvalidatePrototypeChains(object->map()); | 
|  | } | 
|  | } | 
|  |  | 
|  | Maybe<bool> JSReceiver::DeleteProperty(LookupIterator* it, | 
|  | LanguageMode language_mode) { | 
|  | it->UpdateProtector(); | 
|  |  | 
|  | Isolate* isolate = it->isolate(); | 
|  |  | 
|  | if (it->state() == LookupIterator::JSPROXY) { | 
|  | return JSProxy::DeletePropertyOrElement(it->GetHolder<JSProxy>(), | 
|  | it->GetName(), language_mode); | 
|  | } | 
|  |  | 
|  | if (it->GetReceiver()->IsJSProxy()) { | 
|  | if (it->state() != LookupIterator::NOT_FOUND) { | 
|  | DCHECK_EQ(LookupIterator::DATA, it->state()); | 
|  | DCHECK(it->name()->IsPrivate()); | 
|  | it->Delete(); | 
|  | } | 
|  | return Just(true); | 
|  | } | 
|  | Handle<JSObject> receiver = Handle<JSObject>::cast(it->GetReceiver()); | 
|  |  | 
|  | for (; it->IsFound(); it->Next()) { | 
|  | switch (it->state()) { | 
|  | case LookupIterator::JSPROXY: | 
|  | case LookupIterator::NOT_FOUND: | 
|  | case LookupIterator::TRANSITION: | 
|  | UNREACHABLE(); | 
|  | case LookupIterator::ACCESS_CHECK: | 
|  | if (it->HasAccess()) break; | 
|  | isolate->ReportFailedAccessCheck(it->GetHolder<JSObject>()); | 
|  | RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>()); | 
|  | return Just(false); | 
|  | case LookupIterator::INTERCEPTOR: { | 
|  | ShouldThrow should_throw = | 
|  | is_sloppy(language_mode) ? kDontThrow : kThrowOnError; | 
|  | Maybe<bool> result = | 
|  | JSObject::DeletePropertyWithInterceptor(it, should_throw); | 
|  | // An exception was thrown in the interceptor. Propagate. | 
|  | if (isolate->has_pending_exception()) return Nothing<bool>(); | 
|  | // Delete with interceptor succeeded. Return result. | 
|  | // TODO(neis): In strict mode, we should probably throw if the | 
|  | // interceptor returns false. | 
|  | if (result.IsJust()) return result; | 
|  | break; | 
|  | } | 
|  | case LookupIterator::INTEGER_INDEXED_EXOTIC: | 
|  | return Just(true); | 
|  | case LookupIterator::DATA: | 
|  | case LookupIterator::ACCESSOR: { | 
|  | if (!it->IsConfigurable()) { | 
|  | // Fail if the property is not configurable. | 
|  | if (is_strict(language_mode)) { | 
|  | isolate->Throw(*isolate->factory()->NewTypeError( | 
|  | MessageTemplate::kStrictDeleteProperty, it->GetName(), | 
|  | receiver)); | 
|  | return Nothing<bool>(); | 
|  | } | 
|  | return Just(false); | 
|  | } | 
|  |  | 
|  | it->Delete(); | 
|  |  | 
|  | return Just(true); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return Just(true); | 
|  | } | 
|  |  | 
|  | Maybe<bool> JSReceiver::DeleteElement(Handle<JSReceiver> object, uint32_t index, | 
|  | LanguageMode language_mode) { | 
|  | LookupIterator it(object->GetIsolate(), object, index, object, | 
|  | LookupIterator::OWN); | 
|  | return DeleteProperty(&it, language_mode); | 
|  | } | 
|  |  | 
|  | Maybe<bool> JSReceiver::DeleteProperty(Handle<JSReceiver> object, | 
|  | Handle<Name> name, | 
|  | LanguageMode language_mode) { | 
|  | LookupIterator it(object->GetIsolate(), object, name, object, | 
|  | LookupIterator::OWN); | 
|  | return DeleteProperty(&it, language_mode); | 
|  | } | 
|  |  | 
|  | Maybe<bool> JSReceiver::DeletePropertyOrElement(Handle<JSReceiver> object, | 
|  | Handle<Name> name, | 
|  | LanguageMode language_mode) { | 
|  | Isolate* isolate = object->GetIsolate(); | 
|  | LookupIterator::Key key(isolate, name); | 
|  | LookupIterator it(isolate, object, key, object, LookupIterator::OWN); | 
|  | return DeleteProperty(&it, language_mode); | 
|  | } | 
|  |  | 
|  | // ES6 19.1.2.4 | 
|  | // static | 
|  | Object JSReceiver::DefineProperty(Isolate* isolate, Handle<Object> object, | 
|  | Handle<Object> key, | 
|  | Handle<Object> attributes) { | 
|  | // 1. If Type(O) is not Object, throw a TypeError exception. | 
|  | if (!object->IsJSReceiver()) { | 
|  | Handle<String> fun_name = | 
|  | isolate->factory()->InternalizeUtf8String("Object.defineProperty"); | 
|  | THROW_NEW_ERROR_RETURN_FAILURE( | 
|  | isolate, NewTypeError(MessageTemplate::kCalledOnNonObject, fun_name)); | 
|  | } | 
|  | // 2. Let key be ToPropertyKey(P). | 
|  | // 3. ReturnIfAbrupt(key). | 
|  | ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, key, | 
|  | Object::ToPropertyKey(isolate, key)); | 
|  | // 4. Let desc be ToPropertyDescriptor(Attributes). | 
|  | // 5. ReturnIfAbrupt(desc). | 
|  | PropertyDescriptor desc; | 
|  | if (!PropertyDescriptor::ToPropertyDescriptor(isolate, attributes, &desc)) { | 
|  | return ReadOnlyRoots(isolate).exception(); | 
|  | } | 
|  | // 6. Let success be DefinePropertyOrThrow(O,key, desc). | 
|  | Maybe<bool> success = | 
|  | DefineOwnProperty(isolate, Handle<JSReceiver>::cast(object), key, &desc, | 
|  | Just(kThrowOnError)); | 
|  | // 7. ReturnIfAbrupt(success). | 
|  | MAYBE_RETURN(success, ReadOnlyRoots(isolate).exception()); | 
|  | CHECK(success.FromJust()); | 
|  | // 8. Return O. | 
|  | return *object; | 
|  | } | 
|  |  | 
|  | // ES6 19.1.2.3.1 | 
|  | // static | 
|  | MaybeHandle<Object> JSReceiver::DefineProperties(Isolate* isolate, | 
|  | Handle<Object> object, | 
|  | Handle<Object> properties) { | 
|  | // 1. If Type(O) is not Object, throw a TypeError exception. | 
|  | if (!object->IsJSReceiver()) { | 
|  | Handle<String> fun_name = | 
|  | isolate->factory()->InternalizeUtf8String("Object.defineProperties"); | 
|  | THROW_NEW_ERROR(isolate, | 
|  | NewTypeError(MessageTemplate::kCalledOnNonObject, fun_name), | 
|  | Object); | 
|  | } | 
|  | // 2. Let props be ToObject(Properties). | 
|  | // 3. ReturnIfAbrupt(props). | 
|  | Handle<JSReceiver> props; | 
|  | ASSIGN_RETURN_ON_EXCEPTION(isolate, props, | 
|  | Object::ToObject(isolate, properties), Object); | 
|  |  | 
|  | // 4. Let keys be props.[[OwnPropertyKeys]](). | 
|  | // 5. ReturnIfAbrupt(keys). | 
|  | Handle<FixedArray> keys; | 
|  | ASSIGN_RETURN_ON_EXCEPTION( | 
|  | isolate, keys, | 
|  | KeyAccumulator::GetKeys(props, KeyCollectionMode::kOwnOnly, | 
|  | ALL_PROPERTIES), | 
|  | Object); | 
|  | // 6. Let descriptors be an empty List. | 
|  | int capacity = keys->length(); | 
|  | std::vector<PropertyDescriptor> descriptors(capacity); | 
|  | size_t descriptors_index = 0; | 
|  | // 7. Repeat for each element nextKey of keys in List order, | 
|  | for (int i = 0; i < keys->length(); ++i) { | 
|  | Handle<Object> next_key(keys->get(i), isolate); | 
|  | // 7a. Let propDesc be props.[[GetOwnProperty]](nextKey). | 
|  | // 7b. ReturnIfAbrupt(propDesc). | 
|  | LookupIterator::Key key(isolate, next_key); | 
|  | LookupIterator it(isolate, props, key, LookupIterator::OWN); | 
|  | Maybe<PropertyAttributes> maybe = JSReceiver::GetPropertyAttributes(&it); | 
|  | if (maybe.IsNothing()) return MaybeHandle<Object>(); | 
|  | PropertyAttributes attrs = maybe.FromJust(); | 
|  | // 7c. If propDesc is not undefined and propDesc.[[Enumerable]] is true: | 
|  | if (attrs == ABSENT) continue; | 
|  | if (attrs & DONT_ENUM) continue; | 
|  | // 7c i. Let descObj be Get(props, nextKey). | 
|  | // 7c ii. ReturnIfAbrupt(descObj). | 
|  | Handle<Object> desc_obj; | 
|  | ASSIGN_RETURN_ON_EXCEPTION(isolate, desc_obj, Object::GetProperty(&it), | 
|  | Object); | 
|  | // 7c iii. Let desc be ToPropertyDescriptor(descObj). | 
|  | bool success = PropertyDescriptor::ToPropertyDescriptor( | 
|  | isolate, desc_obj, &descriptors[descriptors_index]); | 
|  | // 7c iv. ReturnIfAbrupt(desc). | 
|  | if (!success) return MaybeHandle<Object>(); | 
|  | // 7c v. Append the pair (a two element List) consisting of nextKey and | 
|  | //       desc to the end of descriptors. | 
|  | descriptors[descriptors_index].set_name(next_key); | 
|  | descriptors_index++; | 
|  | } | 
|  | // 8. For each pair from descriptors in list order, | 
|  | for (size_t i = 0; i < descriptors_index; ++i) { | 
|  | PropertyDescriptor* desc = &descriptors[i]; | 
|  | // 8a. Let P be the first element of pair. | 
|  | // 8b. Let desc be the second element of pair. | 
|  | // 8c. Let status be DefinePropertyOrThrow(O, P, desc). | 
|  | Maybe<bool> status = | 
|  | DefineOwnProperty(isolate, Handle<JSReceiver>::cast(object), | 
|  | desc->name(), desc, Just(kThrowOnError)); | 
|  | // 8d. ReturnIfAbrupt(status). | 
|  | if (status.IsNothing()) return MaybeHandle<Object>(); | 
|  | CHECK(status.FromJust()); | 
|  | } | 
|  | // 9. Return o. | 
|  | return object; | 
|  | } | 
|  |  | 
|  | // static | 
|  | Maybe<bool> JSReceiver::DefineOwnProperty(Isolate* isolate, | 
|  | Handle<JSReceiver> object, | 
|  | Handle<Object> key, | 
|  | PropertyDescriptor* desc, | 
|  | Maybe<ShouldThrow> should_throw) { | 
|  | if (object->IsJSArray()) { | 
|  | return JSArray::DefineOwnProperty(isolate, Handle<JSArray>::cast(object), | 
|  | key, desc, should_throw); | 
|  | } | 
|  | if (object->IsJSProxy()) { | 
|  | return JSProxy::DefineOwnProperty(isolate, Handle<JSProxy>::cast(object), | 
|  | key, desc, should_throw); | 
|  | } | 
|  | if (object->IsJSTypedArray()) { | 
|  | return JSTypedArray::DefineOwnProperty( | 
|  | isolate, Handle<JSTypedArray>::cast(object), key, desc, should_throw); | 
|  | } | 
|  |  | 
|  | // OrdinaryDefineOwnProperty, by virtue of calling | 
|  | // DefineOwnPropertyIgnoreAttributes, can handle arguments | 
|  | // (ES#sec-arguments-exotic-objects-defineownproperty-p-desc). | 
|  | return OrdinaryDefineOwnProperty(isolate, Handle<JSObject>::cast(object), key, | 
|  | desc, should_throw); | 
|  | } | 
|  |  | 
|  | // static | 
|  | Maybe<bool> JSReceiver::OrdinaryDefineOwnProperty( | 
|  | Isolate* isolate, Handle<JSObject> object, Handle<Object> key, | 
|  | PropertyDescriptor* desc, Maybe<ShouldThrow> should_throw) { | 
|  | DCHECK(key->IsName() || key->IsNumber());  // |key| is a PropertyKey. | 
|  | LookupIterator::Key lookup_key(isolate, key); | 
|  | LookupIterator it(isolate, object, lookup_key, LookupIterator::OWN); | 
|  |  | 
|  | // Deal with access checks first. | 
|  | if (it.state() == LookupIterator::ACCESS_CHECK) { | 
|  | if (!it.HasAccess()) { | 
|  | isolate->ReportFailedAccessCheck(it.GetHolder<JSObject>()); | 
|  | RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>()); | 
|  | return Just(true); | 
|  | } | 
|  | it.Next(); | 
|  | } | 
|  |  | 
|  | return OrdinaryDefineOwnProperty(&it, desc, should_throw); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | MaybeHandle<Object> GetPropertyWithInterceptorInternal( | 
|  | LookupIterator* it, Handle<InterceptorInfo> interceptor, bool* done) { | 
|  | *done = false; | 
|  | Isolate* isolate = it->isolate(); | 
|  | // Make sure that the top context does not change when doing callbacks or | 
|  | // interceptor calls. | 
|  | AssertNoContextChange ncc(isolate); | 
|  |  | 
|  | if (interceptor->getter().IsUndefined(isolate)) { | 
|  | return isolate->factory()->undefined_value(); | 
|  | } | 
|  |  | 
|  | Handle<JSObject> holder = it->GetHolder<JSObject>(); | 
|  | Handle<Object> result; | 
|  | Handle<Object> receiver = it->GetReceiver(); | 
|  | if (!receiver->IsJSReceiver()) { | 
|  | ASSIGN_RETURN_ON_EXCEPTION( | 
|  | isolate, receiver, Object::ConvertReceiver(isolate, receiver), Object); | 
|  | } | 
|  | PropertyCallbackArguments args(isolate, interceptor->data(), *receiver, | 
|  | *holder, Just(kDontThrow)); | 
|  |  | 
|  | if (it->IsElement(*holder)) { | 
|  | result = args.CallIndexedGetter(interceptor, it->array_index()); | 
|  | } else { | 
|  | result = args.CallNamedGetter(interceptor, it->name()); | 
|  | } | 
|  |  | 
|  | RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); | 
|  | if (result.is_null()) return isolate->factory()->undefined_value(); | 
|  | *done = true; | 
|  | // Rebox handle before return | 
|  | return handle(*result, isolate); | 
|  | } | 
|  |  | 
|  | Maybe<PropertyAttributes> GetPropertyAttributesWithInterceptorInternal( | 
|  | LookupIterator* it, Handle<InterceptorInfo> interceptor) { | 
|  | Isolate* isolate = it->isolate(); | 
|  | // Make sure that the top context does not change when doing | 
|  | // callbacks or interceptor calls. | 
|  | AssertNoContextChange ncc(isolate); | 
|  | HandleScope scope(isolate); | 
|  |  | 
|  | Handle<JSObject> holder = it->GetHolder<JSObject>(); | 
|  | DCHECK_IMPLIES(!it->IsElement(*holder) && it->name()->IsSymbol(), | 
|  | interceptor->can_intercept_symbols()); | 
|  | Handle<Object> receiver = it->GetReceiver(); | 
|  | if (!receiver->IsJSReceiver()) { | 
|  | ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, receiver, | 
|  | Object::ConvertReceiver(isolate, receiver), | 
|  | Nothing<PropertyAttributes>()); | 
|  | } | 
|  | PropertyCallbackArguments args(isolate, interceptor->data(), *receiver, | 
|  | *holder, Just(kDontThrow)); | 
|  | if (!interceptor->query().IsUndefined(isolate)) { | 
|  | Handle<Object> result; | 
|  | if (it->IsElement(*holder)) { | 
|  | result = args.CallIndexedQuery(interceptor, it->array_index()); | 
|  | } else { | 
|  | result = args.CallNamedQuery(interceptor, it->name()); | 
|  | } | 
|  | if (!result.is_null()) { | 
|  | int32_t value; | 
|  | CHECK(result->ToInt32(&value)); | 
|  | return Just(static_cast<PropertyAttributes>(value)); | 
|  | } | 
|  | } else if (!interceptor->getter().IsUndefined(isolate)) { | 
|  | // TODO(verwaest): Use GetPropertyWithInterceptor? | 
|  | Handle<Object> result; | 
|  | if (it->IsElement(*holder)) { | 
|  | result = args.CallIndexedGetter(interceptor, it->array_index()); | 
|  | } else { | 
|  | result = args.CallNamedGetter(interceptor, it->name()); | 
|  | } | 
|  | if (!result.is_null()) return Just(DONT_ENUM); | 
|  | } | 
|  |  | 
|  | RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<PropertyAttributes>()); | 
|  | return Just(ABSENT); | 
|  | } | 
|  |  | 
|  | Maybe<bool> SetPropertyWithInterceptorInternal( | 
|  | LookupIterator* it, Handle<InterceptorInfo> interceptor, | 
|  | Maybe<ShouldThrow> should_throw, Handle<Object> value) { | 
|  | Isolate* isolate = it->isolate(); | 
|  | // Make sure that the top context does not change when doing callbacks or | 
|  | // interceptor calls. | 
|  | AssertNoContextChange ncc(isolate); | 
|  |  | 
|  | if (interceptor->setter().IsUndefined(isolate)) return Just(false); | 
|  |  | 
|  | Handle<JSObject> holder = it->GetHolder<JSObject>(); | 
|  | bool result; | 
|  | Handle<Object> receiver = it->GetReceiver(); | 
|  | if (!receiver->IsJSReceiver()) { | 
|  | ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, receiver, | 
|  | Object::ConvertReceiver(isolate, receiver), | 
|  | Nothing<bool>()); | 
|  | } | 
|  | PropertyCallbackArguments args(isolate, interceptor->data(), *receiver, | 
|  | *holder, should_throw); | 
|  |  | 
|  | if (it->IsElement(*holder)) { | 
|  | // TODO(neis): In the future, we may want to actually return the | 
|  | // interceptor's result, which then should be a boolean. | 
|  | result = !args.CallIndexedSetter(interceptor, it->array_index(), value) | 
|  | .is_null(); | 
|  | } else { | 
|  | result = !args.CallNamedSetter(interceptor, it->name(), value).is_null(); | 
|  | } | 
|  |  | 
|  | RETURN_VALUE_IF_SCHEDULED_EXCEPTION(it->isolate(), Nothing<bool>()); | 
|  | return Just(result); | 
|  | } | 
|  |  | 
|  | Maybe<bool> DefinePropertyWithInterceptorInternal( | 
|  | LookupIterator* it, Handle<InterceptorInfo> interceptor, | 
|  | Maybe<ShouldThrow> should_throw, PropertyDescriptor* desc) { | 
|  | Isolate* isolate = it->isolate(); | 
|  | // Make sure that the top context does not change when doing callbacks or | 
|  | // interceptor calls. | 
|  | AssertNoContextChange ncc(isolate); | 
|  |  | 
|  | if (interceptor->definer().IsUndefined(isolate)) return Just(false); | 
|  |  | 
|  | Handle<JSObject> holder = it->GetHolder<JSObject>(); | 
|  | bool result; | 
|  | Handle<Object> receiver = it->GetReceiver(); | 
|  | if (!receiver->IsJSReceiver()) { | 
|  | ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, receiver, | 
|  | Object::ConvertReceiver(isolate, receiver), | 
|  | Nothing<bool>()); | 
|  | } | 
|  | PropertyCallbackArguments args(isolate, interceptor->data(), *receiver, | 
|  | *holder, should_throw); | 
|  |  | 
|  | std::unique_ptr<v8::PropertyDescriptor> descriptor( | 
|  | new v8::PropertyDescriptor()); | 
|  | if (PropertyDescriptor::IsAccessorDescriptor(desc)) { | 
|  | descriptor.reset(new v8::PropertyDescriptor( | 
|  | v8::Utils::ToLocal(desc->get()), v8::Utils::ToLocal(desc->set()))); | 
|  | } else if (PropertyDescriptor::IsDataDescriptor(desc)) { | 
|  | if (desc->has_writable()) { | 
|  | descriptor.reset(new v8::PropertyDescriptor( | 
|  | v8::Utils::ToLocal(desc->value()), desc->writable())); | 
|  | } else { | 
|  | descriptor.reset( | 
|  | new v8::PropertyDescriptor(v8::Utils::ToLocal(desc->value()))); | 
|  | } | 
|  | } | 
|  | if (desc->has_enumerable()) { | 
|  | descriptor->set_enumerable(desc->enumerable()); | 
|  | } | 
|  | if (desc->has_configurable()) { | 
|  | descriptor->set_configurable(desc->configurable()); | 
|  | } | 
|  |  | 
|  | if (it->IsElement(*holder)) { | 
|  | result = | 
|  | !args.CallIndexedDefiner(interceptor, it->array_index(), *descriptor) | 
|  | .is_null(); | 
|  | } else { | 
|  | result = | 
|  | !args.CallNamedDefiner(interceptor, it->name(), *descriptor).is_null(); | 
|  | } | 
|  |  | 
|  | RETURN_VALUE_IF_SCHEDULED_EXCEPTION(it->isolate(), Nothing<bool>()); | 
|  | return Just(result); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | // ES6 9.1.6.1 | 
|  | // static | 
|  | Maybe<bool> JSReceiver::OrdinaryDefineOwnProperty( | 
|  | LookupIterator* it, PropertyDescriptor* desc, | 
|  | Maybe<ShouldThrow> should_throw) { | 
|  | Isolate* isolate = it->isolate(); | 
|  | // 1. Let current be O.[[GetOwnProperty]](P). | 
|  | // 2. ReturnIfAbrupt(current). | 
|  | PropertyDescriptor current; | 
|  | MAYBE_RETURN(GetOwnPropertyDescriptor(it, ¤t), Nothing<bool>()); | 
|  |  | 
|  | it->Restart(); | 
|  | // Handle interceptor | 
|  | for (; it->IsFound(); it->Next()) { | 
|  | if (it->state() == LookupIterator::INTERCEPTOR) { | 
|  | if (it->HolderIsReceiverOrHiddenPrototype()) { | 
|  | Maybe<bool> result = DefinePropertyWithInterceptorInternal( | 
|  | it, it->GetInterceptor(), should_throw, desc); | 
|  | if (result.IsNothing() || result.FromJust()) { | 
|  | return result; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // TODO(jkummerow/verwaest): It would be nice if we didn't have to reset | 
|  | // the iterator every time. Currently, the reasons why we need it are: | 
|  | // - handle interceptors correctly | 
|  | // - handle accessors correctly (which might change the holder's map) | 
|  | it->Restart(); | 
|  | // 3. Let extensible be the value of the [[Extensible]] internal slot of O. | 
|  | Handle<JSObject> object = Handle<JSObject>::cast(it->GetReceiver()); | 
|  | bool extensible = JSObject::IsExtensible(object); | 
|  |  | 
|  | return ValidateAndApplyPropertyDescriptor( | 
|  | isolate, it, extensible, desc, ¤t, should_throw, Handle<Name>()); | 
|  | } | 
|  |  | 
|  | // ES6 9.1.6.2 | 
|  | // static | 
|  | Maybe<bool> JSReceiver::IsCompatiblePropertyDescriptor( | 
|  | Isolate* isolate, bool extensible, PropertyDescriptor* desc, | 
|  | PropertyDescriptor* current, Handle<Name> property_name, | 
|  | Maybe<ShouldThrow> should_throw) { | 
|  | // 1. Return ValidateAndApplyPropertyDescriptor(undefined, undefined, | 
|  | //    Extensible, Desc, Current). | 
|  | return ValidateAndApplyPropertyDescriptor( | 
|  | isolate, nullptr, extensible, desc, current, should_throw, property_name); | 
|  | } | 
|  |  | 
|  | // ES6 9.1.6.3 | 
|  | // static | 
|  | Maybe<bool> JSReceiver::ValidateAndApplyPropertyDescriptor( | 
|  | Isolate* isolate, LookupIterator* it, bool extensible, | 
|  | PropertyDescriptor* desc, PropertyDescriptor* current, | 
|  | Maybe<ShouldThrow> should_throw, Handle<Name> property_name) { | 
|  | // We either need a LookupIterator, or a property name. | 
|  | DCHECK((it == nullptr) != property_name.is_null()); | 
|  | Handle<JSObject> object; | 
|  | if (it != nullptr) object = Handle<JSObject>::cast(it->GetReceiver()); | 
|  | bool desc_is_data_descriptor = PropertyDescriptor::IsDataDescriptor(desc); | 
|  | bool desc_is_accessor_descriptor = | 
|  | PropertyDescriptor::IsAccessorDescriptor(desc); | 
|  | bool desc_is_generic_descriptor = | 
|  | PropertyDescriptor::IsGenericDescriptor(desc); | 
|  | // 1. (Assert) | 
|  | // 2. If current is undefined, then | 
|  | if (current->is_empty()) { | 
|  | // 2a. If extensible is false, return false. | 
|  | if (!extensible) { | 
|  | RETURN_FAILURE( | 
|  | isolate, GetShouldThrow(isolate, should_throw), | 
|  | NewTypeError(MessageTemplate::kDefineDisallowed, | 
|  | it != nullptr ? it->GetName() : property_name)); | 
|  | } | 
|  | // 2c. If IsGenericDescriptor(Desc) or IsDataDescriptor(Desc) is true, then: | 
|  | // (This is equivalent to !IsAccessorDescriptor(desc).) | 
|  | DCHECK((desc_is_generic_descriptor || desc_is_data_descriptor) == | 
|  | !desc_is_accessor_descriptor); | 
|  | if (!desc_is_accessor_descriptor) { | 
|  | // 2c i. If O is not undefined, create an own data property named P of | 
|  | // object O whose [[Value]], [[Writable]], [[Enumerable]] and | 
|  | // [[Configurable]] attribute values are described by Desc. If the value | 
|  | // of an attribute field of Desc is absent, the attribute of the newly | 
|  | // created property is set to its default value. | 
|  | if (it != nullptr) { | 
|  | if (!desc->has_writable()) desc->set_writable(false); | 
|  | if (!desc->has_enumerable()) desc->set_enumerable(false); | 
|  | if (!desc->has_configurable()) desc->set_configurable(false); | 
|  | Handle<Object> value( | 
|  | desc->has_value() | 
|  | ? desc->value() | 
|  | : Handle<Object>::cast(isolate->factory()->undefined_value())); | 
|  | MaybeHandle<Object> result = | 
|  | JSObject::DefineOwnPropertyIgnoreAttributes(it, value, | 
|  | desc->ToAttributes()); | 
|  | if (result.is_null()) return Nothing<bool>(); | 
|  | } | 
|  | } else { | 
|  | // 2d. Else Desc must be an accessor Property Descriptor, | 
|  | DCHECK(desc_is_accessor_descriptor); | 
|  | // 2d i. If O is not undefined, create an own accessor property named P | 
|  | // of object O whose [[Get]], [[Set]], [[Enumerable]] and | 
|  | // [[Configurable]] attribute values are described by Desc. If the value | 
|  | // of an attribute field of Desc is absent, the attribute of the newly | 
|  | // created property is set to its default value. | 
|  | if (it != nullptr) { | 
|  | if (!desc->has_enumerable()) desc->set_enumerable(false); | 
|  | if (!desc->has_configurable()) desc->set_configurable(false); | 
|  | Handle<Object> getter( | 
|  | desc->has_get() | 
|  | ? desc->get() | 
|  | : Handle<Object>::cast(isolate->factory()->null_value())); | 
|  | Handle<Object> setter( | 
|  | desc->has_set() | 
|  | ? desc->set() | 
|  | : Handle<Object>::cast(isolate->factory()->null_value())); | 
|  | MaybeHandle<Object> result = | 
|  | JSObject::DefineAccessor(it, getter, setter, desc->ToAttributes()); | 
|  | if (result.is_null()) return Nothing<bool>(); | 
|  | } | 
|  | } | 
|  | // 2e. Return true. | 
|  | return Just(true); | 
|  | } | 
|  | // 3. Return true, if every field in Desc is absent. | 
|  | // 4. Return true, if every field in Desc also occurs in current and the | 
|  | // value of every field in Desc is the same value as the corresponding field | 
|  | // in current when compared using the SameValue algorithm. | 
|  | if ((!desc->has_enumerable() || | 
|  | desc->enumerable() == current->enumerable()) && | 
|  | (!desc->has_configurable() || | 
|  | desc->configurable() == current->configurable()) && | 
|  | (!desc->has_value() || | 
|  | (current->has_value() && current->value()->SameValue(*desc->value()))) && | 
|  | (!desc->has_writable() || | 
|  | (current->has_writable() && current->writable() == desc->writable())) && | 
|  | (!desc->has_get() || | 
|  | (current->has_get() && current->get()->SameValue(*desc->get()))) && | 
|  | (!desc->has_set() || | 
|  | (current->has_set() && current->set()->SameValue(*desc->set())))) { | 
|  | return Just(true); | 
|  | } | 
|  | // 5. If the [[Configurable]] field of current is false, then | 
|  | if (!current->configurable()) { | 
|  | // 5a. Return false, if the [[Configurable]] field of Desc is true. | 
|  | if (desc->has_configurable() && desc->configurable()) { | 
|  | RETURN_FAILURE( | 
|  | isolate, GetShouldThrow(isolate, should_throw), | 
|  | NewTypeError(MessageTemplate::kRedefineDisallowed, | 
|  | it != nullptr ? it->GetName() : property_name)); | 
|  | } | 
|  | // 5b. Return false, if the [[Enumerable]] field of Desc is present and the | 
|  | // [[Enumerable]] fields of current and Desc are the Boolean negation of | 
|  | // each other. | 
|  | if (desc->has_enumerable() && desc->enumerable() != current->enumerable()) { | 
|  | RETURN_FAILURE( | 
|  | isolate, GetShouldThrow(isolate, should_throw), | 
|  | NewTypeError(MessageTemplate::kRedefineDisallowed, | 
|  | it != nullptr ? it->GetName() : property_name)); | 
|  | } | 
|  | } | 
|  |  | 
|  | bool current_is_data_descriptor = | 
|  | PropertyDescriptor::IsDataDescriptor(current); | 
|  | // 6. If IsGenericDescriptor(Desc) is true, no further validation is required. | 
|  | if (desc_is_generic_descriptor) { | 
|  | // Nothing to see here. | 
|  |  | 
|  | // 7. Else if IsDataDescriptor(current) and IsDataDescriptor(Desc) have | 
|  | // different results, then: | 
|  | } else if (current_is_data_descriptor != desc_is_data_descriptor) { | 
|  | // 7a. Return false, if the [[Configurable]] field of current is false. | 
|  | if (!current->configurable()) { | 
|  | RETURN_FAILURE( | 
|  | isolate, GetShouldThrow(isolate, should_throw), | 
|  | NewTypeError(MessageTemplate::kRedefineDisallowed, | 
|  | it != nullptr ? it->GetName() : property_name)); | 
|  | } | 
|  | // 7b. If IsDataDescriptor(current) is true, then: | 
|  | if (current_is_data_descriptor) { | 
|  | // 7b i. If O is not undefined, convert the property named P of object O | 
|  | // from a data property to an accessor property. Preserve the existing | 
|  | // values of the converted property's [[Configurable]] and [[Enumerable]] | 
|  | // attributes and set the rest of the property's attributes to their | 
|  | // default values. | 
|  | // --> Folded into step 10. | 
|  | } else { | 
|  | // 7c i. If O is not undefined, convert the property named P of object O | 
|  | // from an accessor property to a data property. Preserve the existing | 
|  | // values of the converted property’s [[Configurable]] and [[Enumerable]] | 
|  | // attributes and set the rest of the property’s attributes to their | 
|  | // default values. | 
|  | // --> Folded into step 10. | 
|  | } | 
|  |  | 
|  | // 8. Else if IsDataDescriptor(current) and IsDataDescriptor(Desc) are both | 
|  | // true, then: | 
|  | } else if (current_is_data_descriptor && desc_is_data_descriptor) { | 
|  | // 8a. If the [[Configurable]] field of current is false, then: | 
|  | if (!current->configurable()) { | 
|  | // 8a i. Return false, if the [[Writable]] field of current is false and | 
|  | // the [[Writable]] field of Desc is true. | 
|  | if (!current->writable() && desc->has_writable() && desc->writable()) { | 
|  | RETURN_FAILURE( | 
|  | isolate, GetShouldThrow(isolate, should_throw), | 
|  | NewTypeError(MessageTemplate::kRedefineDisallowed, | 
|  | it != nullptr ? it->GetName() : property_name)); | 
|  | } | 
|  | // 8a ii. If the [[Writable]] field of current is false, then: | 
|  | if (!current->writable()) { | 
|  | // 8a ii 1. Return false, if the [[Value]] field of Desc is present and | 
|  | // SameValue(Desc.[[Value]], current.[[Value]]) is false. | 
|  | if (desc->has_value() && !desc->value()->SameValue(*current->value())) { | 
|  | RETURN_FAILURE( | 
|  | isolate, GetShouldThrow(isolate, should_throw), | 
|  | NewTypeError(MessageTemplate::kRedefineDisallowed, | 
|  | it != nullptr ? it->GetName() : property_name)); | 
|  | } | 
|  | } | 
|  | } | 
|  | } else { | 
|  | // 9. Else IsAccessorDescriptor(current) and IsAccessorDescriptor(Desc) | 
|  | // are both true, | 
|  | DCHECK(PropertyDescriptor::IsAccessorDescriptor(current) && | 
|  | desc_is_accessor_descriptor); | 
|  | // 9a. If the [[Configurable]] field of current is false, then: | 
|  | if (!current->configurable()) { | 
|  | // 9a i. Return false, if the [[Set]] field of Desc is present and | 
|  | // SameValue(Desc.[[Set]], current.[[Set]]) is false. | 
|  | if (desc->has_set() && !desc->set()->SameValue(*current->set())) { | 
|  | RETURN_FAILURE( | 
|  | isolate, GetShouldThrow(isolate, should_throw), | 
|  | NewTypeError(MessageTemplate::kRedefineDisallowed, | 
|  | it != nullptr ? it->GetName() : property_name)); | 
|  | } | 
|  | // 9a ii. Return false, if the [[Get]] field of Desc is present and | 
|  | // SameValue(Desc.[[Get]], current.[[Get]]) is false. | 
|  | if (desc->has_get() && !desc->get()->SameValue(*current->get())) { | 
|  | RETURN_FAILURE( | 
|  | isolate, GetShouldThrow(isolate, should_throw), | 
|  | NewTypeError(MessageTemplate::kRedefineDisallowed, | 
|  | it != nullptr ? it->GetName() : property_name)); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // 10. If O is not undefined, then: | 
|  | if (it != nullptr) { | 
|  | // 10a. For each field of Desc that is present, set the corresponding | 
|  | // attribute of the property named P of object O to the value of the field. | 
|  | PropertyAttributes attrs = NONE; | 
|  |  | 
|  | if (desc->has_enumerable()) { | 
|  | attrs = static_cast<PropertyAttributes>( | 
|  | attrs | (desc->enumerable() ? NONE : DONT_ENUM)); | 
|  | } else { | 
|  | attrs = static_cast<PropertyAttributes>( | 
|  | attrs | (current->enumerable() ? NONE : DONT_ENUM)); | 
|  | } | 
|  | if (desc->has_configurable()) { | 
|  | attrs = static_cast<PropertyAttributes>( | 
|  | attrs | (desc->configurable() ? NONE : DONT_DELETE)); | 
|  | } else { | 
|  | attrs = static_cast<PropertyAttributes>( | 
|  | attrs | (current->configurable() ? NONE : DONT_DELETE)); | 
|  | } | 
|  | if (desc_is_data_descriptor || | 
|  | (desc_is_generic_descriptor && current_is_data_descriptor)) { | 
|  | if (desc->has_writable()) { | 
|  | attrs = static_cast<PropertyAttributes>( | 
|  | attrs | (desc->writable() ? NONE : READ_ONLY)); | 
|  | } else { | 
|  | attrs = static_cast<PropertyAttributes>( | 
|  | attrs | (current->writable() ? NONE : READ_ONLY)); | 
|  | } | 
|  | Handle<Object> value( | 
|  | desc->has_value() ? desc->value() | 
|  | : current->has_value() | 
|  | ? current->value() | 
|  | : Handle<Object>::cast( | 
|  | isolate->factory()->undefined_value())); | 
|  | return JSObject::DefineOwnPropertyIgnoreAttributes(it, value, attrs, | 
|  | should_throw); | 
|  | } else { | 
|  | DCHECK(desc_is_accessor_descriptor || | 
|  | (desc_is_generic_descriptor && | 
|  | PropertyDescriptor::IsAccessorDescriptor(current))); | 
|  | Handle<Object> getter( | 
|  | desc->has_get() | 
|  | ? desc->get() | 
|  | : current->has_get() | 
|  | ? current->get() | 
|  | : Handle<Object>::cast(isolate->factory()->null_value())); | 
|  | Handle<Object> setter( | 
|  | desc->has_set() | 
|  | ? desc->set() | 
|  | : current->has_set() | 
|  | ? current->set() | 
|  | : Handle<Object>::cast(isolate->factory()->null_value())); | 
|  | MaybeHandle<Object> result = | 
|  | JSObject::DefineAccessor(it, getter, setter, attrs); | 
|  | if (result.is_null()) return Nothing<bool>(); | 
|  | } | 
|  | } | 
|  |  | 
|  | // 11. Return true. | 
|  | return Just(true); | 
|  | } | 
|  |  | 
|  | // static | 
|  | Maybe<bool> JSReceiver::CreateDataProperty(Isolate* isolate, | 
|  | Handle<JSReceiver> object, | 
|  | Handle<Name> key, | 
|  | Handle<Object> value, | 
|  | Maybe<ShouldThrow> should_throw) { | 
|  | LookupIterator::Key lookup_key(isolate, key); | 
|  | LookupIterator it(isolate, object, lookup_key, LookupIterator::OWN); | 
|  | return CreateDataProperty(&it, value, should_throw); | 
|  | } | 
|  |  | 
|  | // static | 
|  | Maybe<bool> JSReceiver::CreateDataProperty(LookupIterator* it, | 
|  | Handle<Object> value, | 
|  | Maybe<ShouldThrow> should_throw) { | 
|  | DCHECK(!it->check_prototype_chain()); | 
|  | Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(it->GetReceiver()); | 
|  | Isolate* isolate = receiver->GetIsolate(); | 
|  |  | 
|  | if (receiver->IsJSObject()) { | 
|  | return JSObject::CreateDataProperty(it, value, should_throw);  // Shortcut. | 
|  | } | 
|  |  | 
|  | PropertyDescriptor new_desc; | 
|  | new_desc.set_value(value); | 
|  | new_desc.set_writable(true); | 
|  | new_desc.set_enumerable(true); | 
|  | new_desc.set_configurable(true); | 
|  |  | 
|  | return JSReceiver::DefineOwnProperty(isolate, receiver, it->GetName(), | 
|  | &new_desc, should_throw); | 
|  | } | 
|  |  | 
|  | // static | 
|  | Maybe<bool> JSReceiver::GetOwnPropertyDescriptor(Isolate* isolate, | 
|  | Handle<JSReceiver> object, | 
|  | Handle<Object> key, | 
|  | PropertyDescriptor* desc) { | 
|  | DCHECK(key->IsName() || key->IsNumber());  // |key| is a PropertyKey. | 
|  | LookupIterator::Key lookup_key(isolate, key); | 
|  | LookupIterator it(isolate, object, lookup_key, LookupIterator::OWN); | 
|  | return GetOwnPropertyDescriptor(&it, desc); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | Maybe<bool> GetPropertyDescriptorWithInterceptor(LookupIterator* it, | 
|  | PropertyDescriptor* desc) { | 
|  | Handle<InterceptorInfo> interceptor; | 
|  |  | 
|  | if (it->state() == LookupIterator::ACCESS_CHECK) { | 
|  | if (it->HasAccess()) { | 
|  | it->Next(); | 
|  | } else { | 
|  | interceptor = it->GetInterceptorForFailedAccessCheck(); | 
|  | if (interceptor.is_null() && | 
|  | (!JSObject::AllCanRead(it) || | 
|  | it->state() != LookupIterator::INTERCEPTOR)) { | 
|  | it->Restart(); | 
|  | return Just(false); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (it->state() == LookupIterator::INTERCEPTOR) { | 
|  | interceptor = it->GetInterceptor(); | 
|  | } | 
|  | if (interceptor.is_null()) return Just(false); | 
|  | Isolate* isolate = it->isolate(); | 
|  | if (interceptor->descriptor().IsUndefined(isolate)) return Just(false); | 
|  |  | 
|  | Handle<Object> result; | 
|  | Handle<JSObject> holder = it->GetHolder<JSObject>(); | 
|  |  | 
|  | Handle<Object> receiver = it->GetReceiver(); | 
|  | if (!receiver->IsJSReceiver()) { | 
|  | ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, receiver, | 
|  | Object::ConvertReceiver(isolate, receiver), | 
|  | Nothing<bool>()); | 
|  | } | 
|  |  | 
|  | PropertyCallbackArguments args(isolate, interceptor->data(), *receiver, | 
|  | *holder, Just(kDontThrow)); | 
|  | if (it->IsElement(*holder)) { | 
|  | result = args.CallIndexedDescriptor(interceptor, it->array_index()); | 
|  | } else { | 
|  | result = args.CallNamedDescriptor(interceptor, it->name()); | 
|  | } | 
|  | // An exception was thrown in the interceptor. Propagate. | 
|  | RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>()); | 
|  | if (!result.is_null()) { | 
|  | // Request successfully intercepted, try to set the property | 
|  | // descriptor. | 
|  | Utils::ApiCheck( | 
|  | PropertyDescriptor::ToPropertyDescriptor(isolate, result, desc), | 
|  | it->IsElement(*holder) ? "v8::IndexedPropertyDescriptorCallback" | 
|  | : "v8::NamedPropertyDescriptorCallback", | 
|  | "Invalid property descriptor."); | 
|  |  | 
|  | return Just(true); | 
|  | } | 
|  |  | 
|  | it->Next(); | 
|  | return Just(false); | 
|  | } | 
|  | }  // namespace | 
|  |  | 
|  | // ES6 9.1.5.1 | 
|  | // Returns true on success, false if the property didn't exist, nothing if | 
|  | // an exception was thrown. | 
|  | // static | 
|  | Maybe<bool> JSReceiver::GetOwnPropertyDescriptor(LookupIterator* it, | 
|  | PropertyDescriptor* desc) { | 
|  | Isolate* isolate = it->isolate(); | 
|  | // "Virtual" dispatch. | 
|  | if (it->IsFound() && it->GetHolder<JSReceiver>()->IsJSProxy()) { | 
|  | return JSProxy::GetOwnPropertyDescriptor(isolate, it->GetHolder<JSProxy>(), | 
|  | it->GetName(), desc); | 
|  | } | 
|  |  | 
|  | Maybe<bool> intercepted = GetPropertyDescriptorWithInterceptor(it, desc); | 
|  | MAYBE_RETURN(intercepted, Nothing<bool>()); | 
|  | if (intercepted.FromJust()) { | 
|  | return Just(true); | 
|  | } | 
|  |  | 
|  | // Request was not intercepted, continue as normal. | 
|  | // 1. (Assert) | 
|  | // 2. If O does not have an own property with key P, return undefined. | 
|  | Maybe<PropertyAttributes> maybe = JSObject::GetPropertyAttributes(it); | 
|  | MAYBE_RETURN(maybe, Nothing<bool>()); | 
|  | PropertyAttributes attrs = maybe.FromJust(); | 
|  | if (attrs == ABSENT) return Just(false); | 
|  | DCHECK(!isolate->has_pending_exception()); | 
|  |  | 
|  | // 3. Let D be a newly created Property Descriptor with no fields. | 
|  | DCHECK(desc->is_empty()); | 
|  | // 4. Let X be O's own property whose key is P. | 
|  | // 5. If X is a data property, then | 
|  | bool is_accessor_pair = it->state() == LookupIterator::ACCESSOR && | 
|  | it->GetAccessors()->IsAccessorPair(); | 
|  | if (!is_accessor_pair) { | 
|  | // 5a. Set D.[[Value]] to the value of X's [[Value]] attribute. | 
|  | Handle<Object> value; | 
|  | if (!Object::GetProperty(it).ToHandle(&value)) { | 
|  | DCHECK(isolate->has_pending_exception()); | 
|  | return Nothing<bool>(); | 
|  | } | 
|  | desc->set_value(value); | 
|  | // 5b. Set D.[[Writable]] to the value of X's [[Writable]] attribute | 
|  | desc->set_writable((attrs & READ_ONLY) == 0); | 
|  | } else { | 
|  | // 6. Else X is an accessor property, so | 
|  | Handle<AccessorPair> accessors = | 
|  | Handle<AccessorPair>::cast(it->GetAccessors()); | 
|  | Handle<NativeContext> native_context = | 
|  | it->GetHolder<JSReceiver>()->GetCreationContext(); | 
|  | // 6a. Set D.[[Get]] to the value of X's [[Get]] attribute. | 
|  | desc->set_get(AccessorPair::GetComponent(isolate, native_context, accessors, | 
|  | ACCESSOR_GETTER)); | 
|  | // 6b. Set D.[[Set]] to the value of X's [[Set]] attribute. | 
|  | desc->set_set(AccessorPair::GetComponent(isolate, native_context, accessors, | 
|  | ACCESSOR_SETTER)); | 
|  | } | 
|  |  | 
|  | // 7. Set D.[[Enumerable]] to the value of X's [[Enumerable]] attribute. | 
|  | desc->set_enumerable((attrs & DONT_ENUM) == 0); | 
|  | // 8. Set D.[[Configurable]] to the value of X's [[Configurable]] attribute. | 
|  | desc->set_configurable((attrs & DONT_DELETE) == 0); | 
|  | // 9. Return D. | 
|  | DCHECK(PropertyDescriptor::IsAccessorDescriptor(desc) != | 
|  | PropertyDescriptor::IsDataDescriptor(desc)); | 
|  | return Just(true); | 
|  | } | 
|  | Maybe<bool> JSReceiver::SetIntegrityLevel(Handle<JSReceiver> receiver, | 
|  | IntegrityLevel level, | 
|  | ShouldThrow should_throw) { | 
|  | DCHECK(level == SEALED || level == FROZEN); | 
|  |  | 
|  | if (receiver->IsJSObject()) { | 
|  | Handle<JSObject> object = Handle<JSObject>::cast(receiver); | 
|  |  | 
|  | if (!object->HasSloppyArgumentsElements() && | 
|  | !object->IsJSModuleNamespace()) {  // Fast path. | 
|  | // Prevent memory leaks by not adding unnecessary transitions. | 
|  | Maybe<bool> test = JSObject::TestIntegrityLevel(object, level); | 
|  | MAYBE_RETURN(test, Nothing<bool>()); | 
|  | if (test.FromJust()) return test; | 
|  |  | 
|  | if (level == SEALED) { | 
|  | return JSObject::PreventExtensionsWithTransition<SEALED>(object, | 
|  | should_throw); | 
|  | } else { | 
|  | return JSObject::PreventExtensionsWithTransition<FROZEN>(object, | 
|  | should_throw); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | Isolate* isolate = receiver->GetIsolate(); | 
|  |  | 
|  | MAYBE_RETURN(JSReceiver::PreventExtensions(receiver, should_throw), | 
|  | Nothing<bool>()); | 
|  |  | 
|  | Handle<FixedArray> keys; | 
|  | ASSIGN_RETURN_ON_EXCEPTION_VALUE( | 
|  | isolate, keys, JSReceiver::OwnPropertyKeys(receiver), Nothing<bool>()); | 
|  |  | 
|  | PropertyDescriptor no_conf; | 
|  | no_conf.set_configurable(false); | 
|  |  | 
|  | PropertyDescriptor no_conf_no_write; | 
|  | no_conf_no_write.set_configurable(false); | 
|  | no_conf_no_write.set_writable(false); | 
|  |  | 
|  | if (level == SEALED) { | 
|  | for (int i = 0; i < keys->length(); ++i) { | 
|  | Handle<Object> key(keys->get(i), isolate); | 
|  | MAYBE_RETURN(DefineOwnProperty(isolate, receiver, key, &no_conf, | 
|  | Just(kThrowOnError)), | 
|  | Nothing<bool>()); | 
|  | } | 
|  | return Just(true); | 
|  | } | 
|  |  | 
|  | for (int i = 0; i < keys->length(); ++i) { | 
|  | Handle<Object> key(keys->get(i), isolate); | 
|  | PropertyDescriptor current_desc; | 
|  | Maybe<bool> owned = JSReceiver::GetOwnPropertyDescriptor( | 
|  | isolate, receiver, key, ¤t_desc); | 
|  | MAYBE_RETURN(owned, Nothing<bool>()); | 
|  | if (owned.FromJust()) { | 
|  | PropertyDescriptor desc = | 
|  | PropertyDescriptor::IsAccessorDescriptor(¤t_desc) | 
|  | ? no_conf | 
|  | : no_conf_no_write; | 
|  | MAYBE_RETURN( | 
|  | DefineOwnProperty(isolate, receiver, key, &desc, Just(kThrowOnError)), | 
|  | Nothing<bool>()); | 
|  | } | 
|  | } | 
|  | return Just(true); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  | Maybe<bool> GenericTestIntegrityLevel(Handle<JSReceiver> receiver, | 
|  | PropertyAttributes level) { | 
|  | DCHECK(level == SEALED || level == FROZEN); | 
|  |  | 
|  | Maybe<bool> extensible = JSReceiver::IsExtensible(receiver); | 
|  | MAYBE_RETURN(extensible, Nothing<bool>()); | 
|  | if (extensible.FromJust()) return Just(false); | 
|  |  | 
|  | Isolate* isolate = receiver->GetIsolate(); | 
|  |  | 
|  | Handle<FixedArray> keys; | 
|  | ASSIGN_RETURN_ON_EXCEPTION_VALUE( | 
|  | isolate, keys, JSReceiver::OwnPropertyKeys(receiver), Nothing<bool>()); | 
|  |  | 
|  | for (int i = 0; i < keys->length(); ++i) { | 
|  | Handle<Object> key(keys->get(i), isolate); | 
|  | PropertyDescriptor current_desc; | 
|  | Maybe<bool> owned = JSReceiver::GetOwnPropertyDescriptor( | 
|  | isolate, receiver, key, ¤t_desc); | 
|  | MAYBE_RETURN(owned, Nothing<bool>()); | 
|  | if (owned.FromJust()) { | 
|  | if (current_desc.configurable()) return Just(false); | 
|  | if (level == FROZEN && | 
|  | PropertyDescriptor::IsDataDescriptor(¤t_desc) && | 
|  | current_desc.writable()) { | 
|  | return Just(false); | 
|  | } | 
|  | } | 
|  | } | 
|  | return Just(true); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | Maybe<bool> JSReceiver::TestIntegrityLevel(Handle<JSReceiver> receiver, | 
|  | IntegrityLevel level) { | 
|  | if (!receiver->map().IsCustomElementsReceiverMap()) { | 
|  | return JSObject::TestIntegrityLevel(Handle<JSObject>::cast(receiver), | 
|  | level); | 
|  | } | 
|  | return GenericTestIntegrityLevel(receiver, level); | 
|  | } | 
|  |  | 
|  | Maybe<bool> JSReceiver::PreventExtensions(Handle<JSReceiver> object, | 
|  | ShouldThrow should_throw) { | 
|  | if (object->IsJSProxy()) { | 
|  | return JSProxy::PreventExtensions(Handle<JSProxy>::cast(object), | 
|  | should_throw); | 
|  | } | 
|  | DCHECK(object->IsJSObject()); | 
|  | return JSObject::PreventExtensions(Handle<JSObject>::cast(object), | 
|  | should_throw); | 
|  | } | 
|  |  | 
|  | Maybe<bool> JSReceiver::IsExtensible(Handle<JSReceiver> object) { | 
|  | if (object->IsJSProxy()) { | 
|  | return JSProxy::IsExtensible(Handle<JSProxy>::cast(object)); | 
|  | } | 
|  | return Just(JSObject::IsExtensible(Handle<JSObject>::cast(object))); | 
|  | } | 
|  |  | 
|  | // static | 
|  | MaybeHandle<Object> JSReceiver::ToPrimitive(Handle<JSReceiver> receiver, | 
|  | ToPrimitiveHint hint) { | 
|  | Isolate* const isolate = receiver->GetIsolate(); | 
|  | Handle<Object> exotic_to_prim; | 
|  | ASSIGN_RETURN_ON_EXCEPTION( | 
|  | isolate, exotic_to_prim, | 
|  | Object::GetMethod(receiver, isolate->factory()->to_primitive_symbol()), | 
|  | Object); | 
|  | if (!exotic_to_prim->IsUndefined(isolate)) { | 
|  | Handle<Object> hint_string = | 
|  | isolate->factory()->ToPrimitiveHintString(hint); | 
|  | Handle<Object> result; | 
|  | ASSIGN_RETURN_ON_EXCEPTION( | 
|  | isolate, result, | 
|  | Execution::Call(isolate, exotic_to_prim, receiver, 1, &hint_string), | 
|  | Object); | 
|  | if (result->IsPrimitive()) return result; | 
|  | THROW_NEW_ERROR(isolate, | 
|  | NewTypeError(MessageTemplate::kCannotConvertToPrimitive), | 
|  | Object); | 
|  | } | 
|  | return OrdinaryToPrimitive(receiver, (hint == ToPrimitiveHint::kString) | 
|  | ? OrdinaryToPrimitiveHint::kString | 
|  | : OrdinaryToPrimitiveHint::kNumber); | 
|  | } | 
|  |  | 
|  | // static | 
|  | MaybeHandle<Object> JSReceiver::OrdinaryToPrimitive( | 
|  | Handle<JSReceiver> receiver, OrdinaryToPrimitiveHint hint) { | 
|  | Isolate* const isolate = receiver->GetIsolate(); | 
|  | Handle<String> method_names[2]; | 
|  | switch (hint) { | 
|  | case OrdinaryToPrimitiveHint::kNumber: | 
|  | method_names[0] = isolate->factory()->valueOf_string(); | 
|  | method_names[1] = isolate->factory()->toString_string(); | 
|  | break; | 
|  | case OrdinaryToPrimitiveHint::kString: | 
|  | method_names[0] = isolate->factory()->toString_string(); | 
|  | method_names[1] = isolate->factory()->valueOf_string(); | 
|  | break; | 
|  | } | 
|  | for (Handle<String> name : method_names) { | 
|  | Handle<Object> method; | 
|  | ASSIGN_RETURN_ON_EXCEPTION(isolate, method, | 
|  | JSReceiver::GetProperty(isolate, receiver, name), | 
|  | Object); | 
|  | if (method->IsCallable()) { | 
|  | Handle<Object> result; | 
|  | ASSIGN_RETURN_ON_EXCEPTION( | 
|  | isolate, result, | 
|  | Execution::Call(isolate, method, receiver, 0, nullptr), Object); | 
|  | if (result->IsPrimitive()) return result; | 
|  | } | 
|  | } | 
|  | THROW_NEW_ERROR(isolate, | 
|  | NewTypeError(MessageTemplate::kCannotConvertToPrimitive), | 
|  | Object); | 
|  | } | 
|  |  | 
|  | V8_WARN_UNUSED_RESULT Maybe<bool> FastGetOwnValuesOrEntries( | 
|  | Isolate* isolate, Handle<JSReceiver> receiver, bool get_entries, | 
|  | Handle<FixedArray>* result) { | 
|  | Handle<Map> map(JSReceiver::cast(*receiver).map(), isolate); | 
|  |  | 
|  | if (!map->IsJSObjectMap()) return Just(false); | 
|  | if (!map->OnlyHasSimpleProperties()) return Just(false); | 
|  |  | 
|  | Handle<JSObject> object(JSObject::cast(*receiver), isolate); | 
|  | Handle<DescriptorArray> descriptors(map->instance_descriptors(kRelaxedLoad), | 
|  | isolate); | 
|  |  | 
|  | int number_of_own_descriptors = map->NumberOfOwnDescriptors(); | 
|  | size_t number_of_own_elements = | 
|  | object->GetElementsAccessor()->GetCapacity(*object, object->elements()); | 
|  |  | 
|  | if (number_of_own_elements > | 
|  | static_cast<size_t>(FixedArray::kMaxLength - number_of_own_descriptors)) { | 
|  | isolate->Throw(*isolate->factory()->NewRangeError( | 
|  | MessageTemplate::kInvalidArrayLength)); | 
|  | return Nothing<bool>(); | 
|  | } | 
|  | // The static cast is safe after the range check right above. | 
|  | Handle<FixedArray> values_or_entries = isolate->factory()->NewFixedArray( | 
|  | static_cast<int>(number_of_own_descriptors + number_of_own_elements)); | 
|  | int count = 0; | 
|  |  | 
|  | if (object->elements() != ReadOnlyRoots(isolate).empty_fixed_array()) { | 
|  | MAYBE_RETURN(object->GetElementsAccessor()->CollectValuesOrEntries( | 
|  | isolate, object, values_or_entries, get_entries, &count, | 
|  | ENUMERABLE_STRINGS), | 
|  | Nothing<bool>()); | 
|  | } | 
|  |  | 
|  | // We may have already lost stability, if CollectValuesOrEntries had | 
|  | // side-effects. | 
|  | bool stable = *map == object->map(); | 
|  | if (stable) { | 
|  | descriptors.PatchValue(map->instance_descriptors(kRelaxedLoad)); | 
|  | } | 
|  |  | 
|  | for (InternalIndex index : InternalIndex::Range(number_of_own_descriptors)) { | 
|  | HandleScope inner_scope(isolate); | 
|  |  | 
|  | Handle<Name> next_key(descriptors->GetKey(index), isolate); | 
|  | if (!next_key->IsString()) continue; | 
|  | Handle<Object> prop_value; | 
|  |  | 
|  | // Directly decode from the descriptor array if |from| did not change shape. | 
|  | if (stable) { | 
|  | DCHECK_EQ(object->map(), *map); | 
|  | DCHECK_EQ(*descriptors, map->instance_descriptors(kRelaxedLoad)); | 
|  |  | 
|  | PropertyDetails details = descriptors->GetDetails(index); | 
|  | if (!details.IsEnumerable()) continue; | 
|  | if (details.kind() == kData) { | 
|  | if (details.location() == kDescriptor) { | 
|  | prop_value = handle(descriptors->GetStrongValue(index), isolate); | 
|  | } else { | 
|  | Representation representation = details.representation(); | 
|  | FieldIndex field_index = FieldIndex::ForPropertyIndex( | 
|  | *map, details.field_index(), representation); | 
|  | prop_value = | 
|  | JSObject::FastPropertyAt(object, representation, field_index); | 
|  | } | 
|  | } else { | 
|  | LookupIterator it(isolate, object, next_key, | 
|  | LookupIterator::OWN_SKIP_INTERCEPTOR); | 
|  | DCHECK_EQ(LookupIterator::ACCESSOR, it.state()); | 
|  | ASSIGN_RETURN_ON_EXCEPTION_VALUE( | 
|  | isolate, prop_value, Object::GetProperty(&it), Nothing<bool>()); | 
|  | stable = object->map() == *map; | 
|  | descriptors.PatchValue(map->instance_descriptors(kRelaxedLoad)); | 
|  | } | 
|  | } else { | 
|  | // If the map did change, do a slower lookup. We are still guaranteed that | 
|  | // the object has a simple shape, and that the key is a name. | 
|  | LookupIterator it(isolate, object, next_key, | 
|  | LookupIterator::OWN_SKIP_INTERCEPTOR); | 
|  | if (!it.IsFound()) continue; | 
|  | DCHECK(it.state() == LookupIterator::DATA || | 
|  | it.state() == LookupIterator::ACCESSOR); | 
|  | if (!it.IsEnumerable()) continue; | 
|  | ASSIGN_RETURN_ON_EXCEPTION_VALUE( | 
|  | isolate, prop_value, Object::GetProperty(&it), Nothing<bool>()); | 
|  | } | 
|  |  | 
|  | if (get_entries) { | 
|  | prop_value = MakeEntryPair(isolate, next_key, prop_value); | 
|  | } | 
|  |  | 
|  | values_or_entries->set(count, *prop_value); | 
|  | count++; | 
|  | } | 
|  |  | 
|  | DCHECK_LE(count, values_or_entries->length()); | 
|  | *result = FixedArray::ShrinkOrEmpty(isolate, values_or_entries, count); | 
|  | return Just(true); | 
|  | } | 
|  |  | 
|  | MaybeHandle<FixedArray> GetOwnValuesOrEntries(Isolate* isolate, | 
|  | Handle<JSReceiver> object, | 
|  | PropertyFilter filter, | 
|  | bool try_fast_path, | 
|  | bool get_entries) { | 
|  | Handle<FixedArray> values_or_entries; | 
|  | if (try_fast_path && filter == ENUMERABLE_STRINGS) { | 
|  | Maybe<bool> fast_values_or_entries = FastGetOwnValuesOrEntries( | 
|  | isolate, object, get_entries, &values_or_entries); | 
|  | if (fast_values_or_entries.IsNothing()) return MaybeHandle<FixedArray>(); | 
|  | if (fast_values_or_entries.FromJust()) return values_or_entries; | 
|  | } | 
|  |  | 
|  | PropertyFilter key_filter = | 
|  | static_cast<PropertyFilter>(filter & ~ONLY_ENUMERABLE); | 
|  |  | 
|  | Handle<FixedArray> keys; | 
|  | ASSIGN_RETURN_ON_EXCEPTION_VALUE( | 
|  | isolate, keys, | 
|  | KeyAccumulator::GetKeys(object, KeyCollectionMode::kOwnOnly, key_filter, | 
|  | GetKeysConversion::kConvertToString), | 
|  | MaybeHandle<FixedArray>()); | 
|  |  | 
|  | values_or_entries = isolate->factory()->NewFixedArray(keys->length()); | 
|  | int length = 0; | 
|  |  | 
|  | for (int i = 0; i < keys->length(); ++i) { | 
|  | Handle<Name> key = | 
|  | Handle<Name>::cast(handle(keys->get(isolate, i), isolate)); | 
|  |  | 
|  | if (filter & ONLY_ENUMERABLE) { | 
|  | PropertyDescriptor descriptor; | 
|  | Maybe<bool> did_get_descriptor = JSReceiver::GetOwnPropertyDescriptor( | 
|  | isolate, object, key, &descriptor); | 
|  | MAYBE_RETURN(did_get_descriptor, MaybeHandle<FixedArray>()); | 
|  | if (!did_get_descriptor.FromJust() || !descriptor.enumerable()) continue; | 
|  | } | 
|  |  | 
|  | Handle<Object> value; | 
|  | ASSIGN_RETURN_ON_EXCEPTION_VALUE( | 
|  | isolate, value, Object::GetPropertyOrElement(isolate, object, key), | 
|  | MaybeHandle<FixedArray>()); | 
|  |  | 
|  | if (get_entries) { | 
|  | Handle<FixedArray> entry_storage = | 
|  | isolate->factory()->NewUninitializedFixedArray(2); | 
|  | entry_storage->set(0, *key); | 
|  | entry_storage->set(1, *value); | 
|  | value = isolate->factory()->NewJSArrayWithElements(entry_storage, | 
|  | PACKED_ELEMENTS, 2); | 
|  | } | 
|  |  | 
|  | values_or_entries->set(length, *value); | 
|  | length++; | 
|  | } | 
|  | DCHECK_LE(length, values_or_entries->length()); | 
|  | return FixedArray::ShrinkOrEmpty(isolate, values_or_entries, length); | 
|  | } | 
|  |  | 
|  | MaybeHandle<FixedArray> JSReceiver::GetOwnValues(Handle<JSReceiver> object, | 
|  | PropertyFilter filter, | 
|  | bool try_fast_path) { | 
|  | return GetOwnValuesOrEntries(object->GetIsolate(), object, filter, | 
|  | try_fast_path, false); | 
|  | } | 
|  |  | 
|  | MaybeHandle<FixedArray> JSReceiver::GetOwnEntries(Handle<JSReceiver> object, | 
|  | PropertyFilter filter, | 
|  | bool try_fast_path) { | 
|  | return GetOwnValuesOrEntries(object->GetIsolate(), object, filter, | 
|  | try_fast_path, true); | 
|  | } | 
|  |  | 
|  | Maybe<bool> JSReceiver::SetPrototype(Handle<JSReceiver> object, | 
|  | Handle<Object> value, bool from_javascript, | 
|  | ShouldThrow should_throw) { | 
|  | if (object->IsJSProxy()) { | 
|  | return JSProxy::SetPrototype(Handle<JSProxy>::cast(object), value, | 
|  | from_javascript, should_throw); | 
|  | } | 
|  | return JSObject::SetPrototype(Handle<JSObject>::cast(object), value, | 
|  | from_javascript, should_throw); | 
|  | } | 
|  |  | 
|  | bool JSReceiver::HasProxyInPrototype(Isolate* isolate) { | 
|  | for (PrototypeIterator iter(isolate, *this, kStartAtReceiver, | 
|  | PrototypeIterator::END_AT_NULL); | 
|  | !iter.IsAtEnd(); iter.AdvanceIgnoringProxies()) { | 
|  | if (iter.GetCurrent().IsJSProxy()) return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool JSReceiver::IsCodeLike(Isolate* isolate) const { | 
|  | DisallowGarbageCollection no_gc; | 
|  | Object maybe_constructor = map().GetConstructor(); | 
|  | if (!maybe_constructor.IsJSFunction()) return false; | 
|  | if (!JSFunction::cast(maybe_constructor).shared().IsApiFunction()) { | 
|  | return false; | 
|  | } | 
|  | Object instance_template = JSFunction::cast(maybe_constructor) | 
|  | .shared() | 
|  | .get_api_func_data() | 
|  | .GetInstanceTemplate(); | 
|  | if (instance_template.IsUndefined(isolate)) return false; | 
|  | return ObjectTemplateInfo::cast(instance_template).code_like(); | 
|  | } | 
|  |  | 
|  | // static | 
|  | MaybeHandle<JSObject> JSObject::New(Handle<JSFunction> constructor, | 
|  | Handle<JSReceiver> new_target, | 
|  | Handle<AllocationSite> site) { | 
|  | // If called through new, new.target can be: | 
|  | // - a subclass of constructor, | 
|  | // - a proxy wrapper around constructor, or | 
|  | // - the constructor itself. | 
|  | // If called through Reflect.construct, it's guaranteed to be a constructor. | 
|  | Isolate* const isolate = constructor->GetIsolate(); | 
|  | DCHECK(constructor->IsConstructor()); | 
|  | DCHECK(new_target->IsConstructor()); | 
|  | DCHECK(!constructor->has_initial_map() || | 
|  | constructor->initial_map().instance_type() != JS_FUNCTION_TYPE); | 
|  |  | 
|  | Handle<Map> initial_map; | 
|  | ASSIGN_RETURN_ON_EXCEPTION( | 
|  | isolate, initial_map, | 
|  | JSFunction::GetDerivedMap(isolate, constructor, new_target), JSObject); | 
|  | int initial_capacity = V8_DICT_MODE_PROTOTYPES_BOOL | 
|  | ? OrderedNameDictionary::kInitialCapacity | 
|  | : NameDictionary::kInitialCapacity; | 
|  | Handle<JSObject> result = isolate->factory()->NewFastOrSlowJSObjectFromMap( | 
|  | initial_map, initial_capacity, AllocationType::kYoung, site); | 
|  | isolate->counters()->constructed_objects()->Increment(); | 
|  | isolate->counters()->constructed_objects_runtime()->Increment(); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | // 9.1.12 ObjectCreate ( proto [ , internalSlotsList ] ) | 
|  | // Notice: This is NOT 19.1.2.2 Object.create ( O, Properties ) | 
|  | MaybeHandle<JSObject> JSObject::ObjectCreate(Isolate* isolate, | 
|  | Handle<Object> prototype) { | 
|  | // Generate the map with the specified {prototype} based on the Object | 
|  | // function's initial map from the current native context. | 
|  | // TODO(bmeurer): Use a dedicated cache for Object.create; think about | 
|  | // slack tracking for Object.create. | 
|  | Handle<Map> map = | 
|  | Map::GetObjectCreateMap(isolate, Handle<HeapObject>::cast(prototype)); | 
|  |  | 
|  | // Actually allocate the object. | 
|  | return isolate->factory()->NewFastOrSlowJSObjectFromMap(map); | 
|  | } | 
|  |  | 
|  | void JSObject::EnsureWritableFastElements(Handle<JSObject> object) { | 
|  | DCHECK(object->HasSmiOrObjectElements() || | 
|  | object->HasFastStringWrapperElements() || | 
|  | object->HasAnyNonextensibleElements()); | 
|  | FixedArray raw_elems = FixedArray::cast(object->elements()); | 
|  | Isolate* isolate = object->GetIsolate(); | 
|  | if (raw_elems.map() != ReadOnlyRoots(isolate).fixed_cow_array_map()) return; | 
|  | Handle<FixedArray> elems(raw_elems, isolate); | 
|  | Handle<FixedArray> writable_elems = isolate->factory()->CopyFixedArrayWithMap( | 
|  | elems, isolate->factory()->fixed_array_map()); | 
|  | object->set_elements(*writable_elems); | 
|  | isolate->counters()->cow_arrays_converted()->Increment(); | 
|  | } | 
|  |  | 
|  | int JSObject::GetHeaderSize(InstanceType type, | 
|  | bool function_has_prototype_slot) { | 
|  | switch (type) { | 
|  | case JS_OBJECT_TYPE: | 
|  | case JS_API_OBJECT_TYPE: | 
|  | case JS_SPECIAL_API_OBJECT_TYPE: | 
|  | return JSObject::kHeaderSize; | 
|  | case JS_GENERATOR_OBJECT_TYPE: | 
|  | return JSGeneratorObject::kHeaderSize; | 
|  | case JS_ASYNC_FUNCTION_OBJECT_TYPE: | 
|  | return JSAsyncFunctionObject::kHeaderSize; | 
|  | case JS_ASYNC_GENERATOR_OBJECT_TYPE: | 
|  | return JSAsyncGeneratorObject::kHeaderSize; | 
|  | case JS_ASYNC_FROM_SYNC_ITERATOR_TYPE: | 
|  | return JSAsyncFromSyncIterator::kHeaderSize; | 
|  | case JS_GLOBAL_PROXY_TYPE: | 
|  | return JSGlobalProxy::kHeaderSize; | 
|  | case JS_GLOBAL_OBJECT_TYPE: | 
|  | return JSGlobalObject::kHeaderSize; | 
|  | case JS_BOUND_FUNCTION_TYPE: | 
|  | return JSBoundFunction::kHeaderSize; | 
|  | case JS_FUNCTION_TYPE: | 
|  | return JSFunction::GetHeaderSize(function_has_prototype_slot); | 
|  | case JS_PRIMITIVE_WRAPPER_TYPE: | 
|  | return JSPrimitiveWrapper::kHeaderSize; | 
|  | case JS_DATE_TYPE: | 
|  | return JSDate::kHeaderSize; | 
|  | case JS_ARRAY_TYPE: | 
|  | return JSArray::kHeaderSize; | 
|  | case JS_ARRAY_BUFFER_TYPE: | 
|  | return JSArrayBuffer::kHeaderSize; | 
|  | case JS_ARRAY_ITERATOR_TYPE: | 
|  | return JSArrayIterator::kHeaderSize; | 
|  | case JS_TYPED_ARRAY_TYPE: | 
|  | return JSTypedArray::kHeaderSize; | 
|  | case JS_DATA_VIEW_TYPE: | 
|  | return JSDataView::kHeaderSize; | 
|  | case JS_SET_TYPE: | 
|  | return JSSet::kHeaderSize; | 
|  | case JS_MAP_TYPE: | 
|  | return JSMap::kHeaderSize; | 
|  | case JS_SET_KEY_VALUE_ITERATOR_TYPE: | 
|  | case JS_SET_VALUE_ITERATOR_TYPE: | 
|  | return JSSetIterator::kHeaderSize; | 
|  | case JS_MAP_KEY_ITERATOR_TYPE: | 
|  | case JS_MAP_KEY_VALUE_ITERATOR_TYPE: | 
|  | case JS_MAP_VALUE_ITERATOR_TYPE: | 
|  | return JSMapIterator::kHeaderSize; | 
|  | case JS_WEAK_REF_TYPE: | 
|  | return JSWeakRef::kHeaderSize; | 
|  | case JS_FINALIZATION_REGISTRY_TYPE: | 
|  | return JSFinalizationRegistry::kHeaderSize; | 
|  | case JS_WEAK_MAP_TYPE: | 
|  | return JSWeakMap::kHeaderSize; | 
|  | case JS_WEAK_SET_TYPE: | 
|  | return JSWeakSet::kHeaderSize; | 
|  | case JS_PROMISE_TYPE: | 
|  | return JSPromise::kHeaderSize; | 
|  | case JS_REG_EXP_TYPE: | 
|  | return JSRegExp::kHeaderSize; | 
|  | case JS_REG_EXP_STRING_ITERATOR_TYPE: | 
|  | return JSRegExpStringIterator::kHeaderSize; | 
|  | case JS_CONTEXT_EXTENSION_OBJECT_TYPE: | 
|  | return JSObject::kHeaderSize; | 
|  | case JS_MESSAGE_OBJECT_TYPE: | 
|  | return JSMessageObject::kHeaderSize; | 
|  | case JS_ARGUMENTS_OBJECT_TYPE: | 
|  | return JSObject::kHeaderSize; | 
|  | case JS_ERROR_TYPE: | 
|  | return JSObject::kHeaderSize; | 
|  | case JS_STRING_ITERATOR_TYPE: | 
|  | return JSStringIterator::kHeaderSize; | 
|  | case JS_MODULE_NAMESPACE_TYPE: | 
|  | return JSModuleNamespace::kHeaderSize; | 
|  | #ifdef V8_INTL_SUPPORT | 
|  | case JS_V8_BREAK_ITERATOR_TYPE: | 
|  | return JSV8BreakIterator::kHeaderSize; | 
|  | case JS_COLLATOR_TYPE: | 
|  | return JSCollator::kHeaderSize; | 
|  | case JS_DATE_TIME_FORMAT_TYPE: | 
|  | return JSDateTimeFormat::kHeaderSize; | 
|  | case JS_DISPLAY_NAMES_TYPE: | 
|  | return JSDisplayNames::kHeaderSize; | 
|  | case JS_LIST_FORMAT_TYPE: | 
|  | return JSListFormat::kHeaderSize; | 
|  | case JS_LOCALE_TYPE: | 
|  | return JSLocale::kHeaderSize; | 
|  | case JS_NUMBER_FORMAT_TYPE: | 
|  | return JSNumberFormat::kHeaderSize; | 
|  | case JS_PLURAL_RULES_TYPE: | 
|  | return JSPluralRules::kHeaderSize; | 
|  | case JS_RELATIVE_TIME_FORMAT_TYPE: | 
|  | return JSRelativeTimeFormat::kHeaderSize; | 
|  | case JS_SEGMENT_ITERATOR_TYPE: | 
|  | return JSSegmentIterator::kHeaderSize; | 
|  | case JS_SEGMENTER_TYPE: | 
|  | return JSSegmenter::kHeaderSize; | 
|  | case JS_SEGMENTS_TYPE: | 
|  | return JSSegments::kHeaderSize; | 
|  | #endif  // V8_INTL_SUPPORT | 
|  | case WASM_GLOBAL_OBJECT_TYPE: | 
|  | return WasmGlobalObject::kHeaderSize; | 
|  | case WASM_INSTANCE_OBJECT_TYPE: | 
|  | return WasmInstanceObject::kHeaderSize; | 
|  | case WASM_MEMORY_OBJECT_TYPE: | 
|  | return WasmMemoryObject::kHeaderSize; | 
|  | case WASM_MODULE_OBJECT_TYPE: | 
|  | return WasmModuleObject::kHeaderSize; | 
|  | case WASM_TABLE_OBJECT_TYPE: | 
|  | return WasmTableObject::kHeaderSize; | 
|  | case WASM_EXCEPTION_OBJECT_TYPE: | 
|  | return WasmExceptionObject::kHeaderSize; | 
|  | default: | 
|  | UNREACHABLE(); | 
|  | } | 
|  | } | 
|  |  | 
|  | // static | 
|  | bool JSObject::AllCanRead(LookupIterator* it) { | 
|  | // Skip current iteration, it's in state ACCESS_CHECK or INTERCEPTOR, both of | 
|  | // which have already been checked. | 
|  | DCHECK(it->state() == LookupIterator::ACCESS_CHECK || | 
|  | it->state() == LookupIterator::INTERCEPTOR); | 
|  | for (it->Next(); it->IsFound(); it->Next()) { | 
|  | if (it->state() == LookupIterator::ACCESSOR) { | 
|  | auto accessors = it->GetAccessors(); | 
|  | if (accessors->IsAccessorInfo()) { | 
|  | if (AccessorInfo::cast(*accessors).all_can_read()) return true; | 
|  | } | 
|  | } else if (it->state() == LookupIterator::INTERCEPTOR) { | 
|  | if (it->GetInterceptor()->all_can_read()) return true; | 
|  | } else if (it->state() == LookupIterator::JSPROXY) { | 
|  | // Stop lookupiterating. And no, AllCanNotRead. | 
|  | return false; | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | MaybeHandle<Object> JSObject::GetPropertyWithFailedAccessCheck( | 
|  | LookupIterator* it) { | 
|  | Isolate* isolate = it->isolate(); | 
|  | Handle<JSObject> checked = it->GetHolder<JSObject>(); | 
|  | Handle<InterceptorInfo> interceptor = | 
|  | it->GetInterceptorForFailedAccessCheck(); | 
|  | if (interceptor.is_null()) { | 
|  | while (AllCanRead(it)) { | 
|  | if (it->state() == LookupIterator::ACCESSOR) { | 
|  | return Object::GetPropertyWithAccessor(it); | 
|  | } | 
|  | DCHECK_EQ(LookupIterator::INTERCEPTOR, it->state()); | 
|  | bool done; | 
|  | Handle<Object> result; | 
|  | ASSIGN_RETURN_ON_EXCEPTION(isolate, result, | 
|  | GetPropertyWithInterceptor(it, &done), Object); | 
|  | if (done) return result; | 
|  | } | 
|  |  | 
|  | } else { | 
|  | Handle<Object> result; | 
|  | bool done; | 
|  | ASSIGN_RETURN_ON_EXCEPTION( | 
|  | isolate, result, | 
|  | GetPropertyWithInterceptorInternal(it, interceptor, &done), Object); | 
|  | if (done) return result; | 
|  | } | 
|  |  | 
|  | // Cross-Origin [[Get]] of Well-Known Symbols does not throw, and returns | 
|  | // undefined. | 
|  | Handle<Name> name = it->GetName(); | 
|  | if (name->IsSymbol() && Symbol::cast(*name).is_well_known_symbol()) { | 
|  | return it->factory()->undefined_value(); | 
|  | } | 
|  |  | 
|  | isolate->ReportFailedAccessCheck(checked); | 
|  | RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); | 
|  | return it->factory()->undefined_value(); | 
|  | } | 
|  |  | 
|  | Maybe<PropertyAttributes> JSObject::GetPropertyAttributesWithFailedAccessCheck( | 
|  | LookupIterator* it) { | 
|  | Isolate* isolate = it->isolate(); | 
|  | Handle<JSObject> checked = it->GetHolder<JSObject>(); | 
|  | Handle<InterceptorInfo> interceptor = | 
|  | it->GetInterceptorForFailedAccessCheck(); | 
|  | if (interceptor.is_null()) { | 
|  | while (AllCanRead(it)) { | 
|  | if (it->state() == LookupIterator::ACCESSOR) { | 
|  | return Just(it->property_attributes()); | 
|  | } | 
|  | DCHECK_EQ(LookupIterator::INTERCEPTOR, it->state()); | 
|  | auto result = GetPropertyAttributesWithInterceptor(it); | 
|  | if (isolate->has_scheduled_exception()) break; | 
|  | if (result.IsJust() && result.FromJust() != ABSENT) return result; | 
|  | } | 
|  | } else { | 
|  | Maybe<PropertyAttributes> result = | 
|  | GetPropertyAttributesWithInterceptorInternal(it, interceptor); | 
|  | if (isolate->has_pending_exception()) return Nothing<PropertyAttributes>(); | 
|  | if (result.FromMaybe(ABSENT) != ABSENT) return result; | 
|  | } | 
|  | isolate->ReportFailedAccessCheck(checked); | 
|  | RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<PropertyAttributes>()); | 
|  | return Just(ABSENT); | 
|  | } | 
|  |  | 
|  | // static | 
|  | bool JSObject::AllCanWrite(LookupIterator* it) { | 
|  | for (; it->IsFound() && it->state() != LookupIterator::JSPROXY; it->Next()) { | 
|  | if (it->state() == LookupIterator::ACCESSOR) { | 
|  | Handle<Object> accessors = it->GetAccessors(); | 
|  | if (accessors->IsAccessorInfo()) { | 
|  | if (AccessorInfo::cast(*accessors).all_can_write()) return true; | 
|  | } | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | Maybe<bool> JSObject::SetPropertyWithFailedAccessCheck( | 
|  | LookupIterator* it, Handle<Object> value, Maybe<ShouldThrow> should_throw) { | 
|  | Isolate* isolate = it->isolate(); | 
|  | Handle<JSObject> checked = it->GetHolder<JSObject>(); | 
|  | Handle<InterceptorInfo> interceptor = | 
|  | it->GetInterceptorForFailedAccessCheck(); | 
|  | if (interceptor.is_null()) { | 
|  | if (AllCanWrite(it)) { | 
|  | return Object::SetPropertyWithAccessor(it, value, should_throw); | 
|  | } | 
|  | } else { | 
|  | Maybe<bool> result = SetPropertyWithInterceptorInternal( | 
|  | it, interceptor, should_throw, value); | 
|  | if (isolate->has_pending_exception()) return Nothing<bool>(); | 
|  | if (result.IsJust()) return result; | 
|  | } | 
|  | isolate->ReportFailedAccessCheck(checked); | 
|  | RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>()); | 
|  | return Just(true); | 
|  | } | 
|  |  | 
|  | void JSObject::SetNormalizedProperty(Handle<JSObject> object, Handle<Name> name, | 
|  | Handle<Object> value, | 
|  | PropertyDetails details) { | 
|  | DCHECK(!object->HasFastProperties()); | 
|  | DCHECK(name->IsUniqueName()); | 
|  | Isolate* isolate = object->GetIsolate(); | 
|  |  | 
|  | uint32_t hash = name->Hash(); | 
|  |  | 
|  | if (object->IsJSGlobalObject()) { | 
|  | Handle<JSGlobalObject> global_obj = Handle<JSGlobalObject>::cast(object); | 
|  | Handle<GlobalDictionary> dictionary(global_obj->global_dictionary(), | 
|  | isolate); | 
|  | ReadOnlyRoots roots(isolate); | 
|  | InternalIndex entry = dictionary->FindEntry(isolate, roots, name, hash); | 
|  |  | 
|  | if (entry.is_not_found()) { | 
|  | DCHECK_IMPLIES(global_obj->map().is_prototype_map(), | 
|  | Map::IsPrototypeChainInvalidated(global_obj->map())); | 
|  | auto cell = isolate->factory()->NewPropertyCell(name); | 
|  | cell->set_value(*value); | 
|  | auto cell_type = value->IsUndefined(roots) ? PropertyCellType::kUndefined | 
|  | : PropertyCellType::kConstant; | 
|  | details = details.set_cell_type(cell_type); | 
|  | value = cell; | 
|  | dictionary = | 
|  | GlobalDictionary::Add(isolate, dictionary, name, value, details); | 
|  | global_obj->set_global_dictionary(*dictionary); | 
|  | } else { | 
|  | Handle<PropertyCell> cell = PropertyCell::PrepareForValue( | 
|  | isolate, dictionary, entry, value, details); | 
|  | cell->set_value(*value); | 
|  | } | 
|  | } else { | 
|  | if (V8_DICT_MODE_PROTOTYPES_BOOL) { | 
|  | Handle<OrderedNameDictionary> dictionary( | 
|  | object->property_dictionary_ordered(), isolate); | 
|  | InternalIndex entry = dictionary->FindEntry(isolate, *name); | 
|  | if (entry.is_not_found()) { | 
|  | DCHECK_IMPLIES(object->map().is_prototype_map(), | 
|  | Map::IsPrototypeChainInvalidated(object->map())); | 
|  | dictionary = OrderedNameDictionary::Add(isolate, dictionary, name, | 
|  | value, details) | 
|  | .ToHandleChecked(); | 
|  | object->SetProperties(*dictionary); | 
|  | } else { | 
|  | dictionary->SetEntry(entry, *name, *value, details); | 
|  | } | 
|  | } else { | 
|  | Handle<NameDictionary> dictionary(object->property_dictionary(), isolate); | 
|  | InternalIndex entry = dictionary->FindEntry(isolate, name); | 
|  | if (entry.is_not_found()) { | 
|  | DCHECK_IMPLIES(object->map().is_prototype_map(), | 
|  | Map::IsPrototypeChainInvalidated(object->map())); | 
|  | dictionary = | 
|  | NameDictionary::Add(isolate, dictionary, name, value, details); | 
|  | object->SetProperties(*dictionary); | 
|  | } else { | 
|  | PropertyDetails original_details = dictionary->DetailsAt(entry); | 
|  | int enumeration_index = original_details.dictionary_index(); | 
|  | DCHECK_GT(enumeration_index, 0); | 
|  | details = details.set_index(enumeration_index); | 
|  | dictionary->SetEntry(entry, *name, *value, details); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void JSObject::JSObjectShortPrint(StringStream* accumulator) { | 
|  | switch (map().instance_type()) { | 
|  | case JS_ARRAY_TYPE: { | 
|  | double length = JSArray::cast(*this).length().IsUndefined() | 
|  | ? 0 | 
|  | : JSArray::cast(*this).length().Number(); | 
|  | accumulator->Add("<JSArray[%u]>", static_cast<uint32_t>(length)); | 
|  | break; | 
|  | } | 
|  | case JS_BOUND_FUNCTION_TYPE: { | 
|  | JSBoundFunction bound_function = JSBoundFunction::cast(*this); | 
|  | accumulator->Add("<JSBoundFunction"); | 
|  | accumulator->Add(" (BoundTargetFunction %p)>", | 
|  | reinterpret_cast<void*>( | 
|  | bound_function.bound_target_function().ptr())); | 
|  | break; | 
|  | } | 
|  | case JS_WEAK_MAP_TYPE: { | 
|  | accumulator->Add("<JSWeakMap>"); | 
|  | break; | 
|  | } | 
|  | case JS_WEAK_SET_TYPE: { | 
|  | accumulator->Add("<JSWeakSet>"); | 
|  | break; | 
|  | } | 
|  | case JS_REG_EXP_TYPE: { | 
|  | accumulator->Add("<JSRegExp"); | 
|  | JSRegExp regexp = JSRegExp::cast(*this); | 
|  | if (regexp.source().IsString()) { | 
|  | accumulator->Add(" "); | 
|  | String::cast(regexp.source()).StringShortPrint(accumulator); | 
|  | } | 
|  | accumulator->Add(">"); | 
|  |  | 
|  | break; | 
|  | } | 
|  | case JS_FUNCTION_TYPE: { | 
|  | JSFunction function = JSFunction::cast(*this); | 
|  | Object fun_name = function.shared().DebugName(); | 
|  | bool printed = false; | 
|  | if (fun_name.IsString()) { | 
|  | String str = String::cast(fun_name); | 
|  | if (str.length() > 0) { | 
|  | accumulator->Add("<JSFunction "); | 
|  | accumulator->Put(str); | 
|  | printed = true; | 
|  | } | 
|  | } | 
|  | if (!printed) { | 
|  | accumulator->Add("<JSFunction"); | 
|  | } | 
|  | if (FLAG_trace_file_names) { | 
|  | Object source_name = Script::cast(function.shared().script()).name(); | 
|  | if (source_name.IsString()) { | 
|  | String str = String::cast(source_name); | 
|  | if (str.length() > 0) { | 
|  | accumulator->Add(" <"); | 
|  | accumulator->Put(str); | 
|  | accumulator->Add(">"); | 
|  | } | 
|  | } | 
|  | } | 
|  | accumulator->Add(" (sfi = %p)", | 
|  | reinterpret_cast<void*>(function.shared().ptr())); | 
|  | accumulator->Put('>'); | 
|  | break; | 
|  | } | 
|  | case JS_GENERATOR_OBJECT_TYPE: { | 
|  | accumulator->Add("<JSGenerator>"); | 
|  | break; | 
|  | } | 
|  | case JS_ASYNC_FUNCTION_OBJECT_TYPE: { | 
|  | accumulator->Add("<JSAsyncFunctionObject>"); | 
|  | break; | 
|  | } | 
|  | case JS_ASYNC_GENERATOR_OBJECT_TYPE: { | 
|  | accumulator->Add("<JS AsyncGenerator>"); | 
|  | break; | 
|  | } | 
|  |  | 
|  | // All other JSObjects are rather similar to each other (JSObject, | 
|  | // JSGlobalProxy, JSGlobalObject, JSUndetectable, JSPrimitiveWrapper). | 
|  | default: { | 
|  | Map map_of_this = map(); | 
|  | Heap* heap = GetHeap(); | 
|  | Object constructor = map_of_this.GetConstructor(); | 
|  | bool printed = false; | 
|  | if (constructor.IsHeapObject() && | 
|  | !heap->Contains(HeapObject::cast(constructor))) { | 
|  | accumulator->Add("!!!INVALID CONSTRUCTOR!!!"); | 
|  | } else { | 
|  | bool global_object = IsJSGlobalProxy(); | 
|  | if (constructor.IsJSFunction()) { | 
|  | if (!heap->Contains(JSFunction::cast(constructor).shared())) { | 
|  | accumulator->Add("!!!INVALID SHARED ON CONSTRUCTOR!!!"); | 
|  | } else { | 
|  | String constructor_name = | 
|  | JSFunction::cast(constructor).shared().Name(); | 
|  | if (constructor_name.length() > 0) { | 
|  | accumulator->Add(global_object ? "<GlobalObject " : "<"); | 
|  | accumulator->Put(constructor_name); | 
|  | accumulator->Add(" %smap = %p", | 
|  | map_of_this.is_deprecated() ? "deprecated-" : "", | 
|  | map_of_this); | 
|  | printed = true; | 
|  | } | 
|  | } | 
|  | } else if (constructor.IsFunctionTemplateInfo()) { | 
|  | accumulator->Add(global_object ? "<RemoteObject>" : "<RemoteObject>"); | 
|  | printed = true; | 
|  | } | 
|  | if (!printed) { | 
|  | accumulator->Add("<JS%sObject", global_object ? "Global " : ""); | 
|  | } | 
|  | } | 
|  | if (IsJSPrimitiveWrapper()) { | 
|  | accumulator->Add(" value = "); | 
|  | JSPrimitiveWrapper::cast(*this).value().ShortPrint(accumulator); | 
|  | } | 
|  | accumulator->Put('>'); | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void JSObject::PrintElementsTransition(FILE* file, Handle<JSObject> object, | 
|  | ElementsKind from_kind, | 
|  | Handle<FixedArrayBase> from_elements, | 
|  | ElementsKind to_kind, | 
|  | Handle<FixedArrayBase> to_elements) { | 
|  | if (from_kind != to_kind) { | 
|  | OFStream os(file); | 
|  | os << "elements transition [" << ElementsKindToString(from_kind) << " -> " | 
|  | << ElementsKindToString(to_kind) << "] in "; | 
|  | JavaScriptFrame::PrintTop(object->GetIsolate(), file, false, true); | 
|  | PrintF(file, " for "); | 
|  | object->ShortPrint(file); | 
|  | PrintF(file, " from "); | 
|  | from_elements->ShortPrint(file); | 
|  | PrintF(file, " to "); | 
|  | to_elements->ShortPrint(file); | 
|  | PrintF(file, "\n"); | 
|  | } | 
|  | } | 
|  |  | 
|  | void JSObject::PrintInstanceMigration(FILE* file, Map original_map, | 
|  | Map new_map) { | 
|  | if (new_map.is_dictionary_map()) { | 
|  | PrintF(file, "[migrating to slow]\n"); | 
|  | return; | 
|  | } | 
|  | PrintF(file, "[migrating]"); | 
|  | DescriptorArray o = original_map.instance_descriptors(kRelaxedLoad); | 
|  | DescriptorArray n = new_map.instance_descriptors(kRelaxedLoad); | 
|  | for (InternalIndex i : original_map.IterateOwnDescriptors()) { | 
|  | Representation o_r = o.GetDetails(i).representation(); | 
|  | Representation n_r = n.GetDetails(i).representation(); | 
|  | if (!o_r.Equals(n_r)) { | 
|  | String::cast(o.GetKey(i)).PrintOn(file); | 
|  | PrintF(file, ":%s->%s ", o_r.Mnemonic(), n_r.Mnemonic()); | 
|  | } else if (o.GetDetails(i).location() == kDescriptor && | 
|  | n.GetDetails(i).location() == kField) { | 
|  | Name name = o.GetKey(i); | 
|  | if (name.IsString()) { | 
|  | String::cast(name).PrintOn(file); | 
|  | } else { | 
|  | PrintF(file, "{symbol %p}", reinterpret_cast<void*>(name.ptr())); | 
|  | } | 
|  | PrintF(file, " "); | 
|  | } | 
|  | } | 
|  | if (original_map.elements_kind() != new_map.elements_kind()) { | 
|  | PrintF(file, "elements_kind[%i->%i]", original_map.elements_kind(), | 
|  | new_map.elements_kind()); | 
|  | } | 
|  | PrintF(file, "\n"); | 
|  | } | 
|  |  | 
|  | bool JSObject::IsUnmodifiedApiObject(FullObjectSlot o) { | 
|  | Object object = *o; | 
|  | if (object.IsSmi()) return false; | 
|  | HeapObject heap_object = HeapObject::cast(object); | 
|  | if (!object.IsJSObject()) return false; | 
|  | JSObject js_object = JSObject::cast(object); | 
|  | if (!js_object.IsDroppableApiWrapper()) return false; | 
|  | Object maybe_constructor = js_object.map().GetConstructor(); | 
|  | if (!maybe_constructor.IsJSFunction()) return false; | 
|  | JSFunction constructor = JSFunction::cast(maybe_constructor); | 
|  | if (js_object.elements().length() != 0) return false; | 
|  | // Check that the object is not a key in a WeakMap (over-approximation). | 
|  | if (!js_object.GetIdentityHash().IsUndefined()) return false; | 
|  |  | 
|  | return constructor.initial_map() == heap_object.map(); | 
|  | } | 
|  |  | 
|  | // static | 
|  | void JSObject::UpdatePrototypeUserRegistration(Handle<Map> old_map, | 
|  | Handle<Map> new_map, | 
|  | Isolate* isolate) { | 
|  | DCHECK(old_map->is_prototype_map()); | 
|  | DCHECK(new_map->is_prototype_map()); | 
|  | bool was_registered = JSObject::UnregisterPrototypeUser(old_map, isolate); | 
|  | new_map->set_prototype_info(old_map->prototype_info()); | 
|  | old_map->set_prototype_info(Smi::zero()); | 
|  | if (FLAG_trace_prototype_users) { | 
|  | PrintF("Moving prototype_info %p from map %p to map %p.\n", | 
|  | reinterpret_cast<void*>(new_map->prototype_info().ptr()), | 
|  | reinterpret_cast<void*>(old_map->ptr()), | 
|  | reinterpret_cast<void*>(new_map->ptr())); | 
|  | } | 
|  | if (was_registered) { | 
|  | if (new_map->prototype_info().IsPrototypeInfo()) { | 
|  | // The new map isn't registered with its prototype yet; reflect this fact | 
|  | // in the PrototypeInfo it just inherited from the old map. | 
|  | PrototypeInfo::cast(new_map->prototype_info()) | 
|  | .set_registry_slot(PrototypeInfo::UNREGISTERED); | 
|  | } | 
|  | JSObject::LazyRegisterPrototypeUser(new_map, isolate); | 
|  | } | 
|  | } | 
|  |  | 
|  | // static | 
|  | void JSObject::NotifyMapChange(Handle<Map> old_map, Handle<Map> new_map, | 
|  | Isolate* isolate) { | 
|  | if (!old_map->is_prototype_map()) return; | 
|  |  | 
|  | InvalidatePrototypeChains(*old_map); | 
|  |  | 
|  | // If the map was registered with its prototype before, ensure that it | 
|  | // registers with its new prototype now. This preserves the invariant that | 
|  | // when a map on a prototype chain is registered with its prototype, then | 
|  | // all prototypes further up the chain are also registered with their | 
|  | // respective prototypes. | 
|  | UpdatePrototypeUserRegistration(old_map, new_map, isolate); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // To migrate a fast instance to a fast map: | 
|  | // - First check whether the instance needs to be rewritten. If not, simply | 
|  | //   change the map. | 
|  | // - Otherwise, allocate a fixed array large enough to hold all fields, in | 
|  | //   addition to unused space. | 
|  | // - Copy all existing properties in, in the following order: backing store | 
|  | //   properties, unused fields, inobject properties. | 
|  | // - If all allocation succeeded, commit the state atomically: | 
|  | //   * Copy inobject properties from the backing store back into the object. | 
|  | //   * Trim the difference in instance size of the object. This also cleanly | 
|  | //     frees inobject properties that moved to the backing store. | 
|  | //   * If there are properties left in the backing store, trim of the space used | 
|  | //     to temporarily store the inobject properties. | 
|  | //   * If there are properties left in the backing store, install the backing | 
|  | //     store. | 
|  | void MigrateFastToFast(Isolate* isolate, Handle<JSObject> object, | 
|  | Handle<Map> new_map) { | 
|  | Handle<Map> old_map(object->map(), isolate); | 
|  | // In case of a regular transition. | 
|  | if (new_map->GetBackPointer(isolate) == *old_map) { | 
|  | // If the map does not add named properties, simply set the map. | 
|  | if (old_map->NumberOfOwnDescriptors() == | 
|  | new_map->NumberOfOwnDescriptors()) { | 
|  | object->synchronized_set_map(*new_map); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // If the map adds a new kDescriptor property, simply set the map. | 
|  | PropertyDetails details = new_map->GetLastDescriptorDetails(isolate); | 
|  | if (details.location() == kDescriptor) { | 
|  | object->synchronized_set_map(*new_map); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Check if we still have space in the {object}, in which case we | 
|  | // can also simply set the map (modulo a special case for mutable | 
|  | // double boxes). | 
|  | FieldIndex index = | 
|  | FieldIndex::ForDescriptor(isolate, *new_map, new_map->LastAdded()); | 
|  | if (index.is_inobject() || index.outobject_array_index() < | 
|  | object->property_array(isolate).length()) { | 
|  | // We still need to allocate HeapNumbers for double fields | 
|  | // if either double field unboxing is disabled or the double field | 
|  | // is in the PropertyArray backing store (where we don't support | 
|  | // double field unboxing). | 
|  | if (index.is_double() && !new_map->IsUnboxedDoubleField(isolate, index)) { | 
|  | auto value = isolate->factory()->NewHeapNumberWithHoleNaN(); | 
|  | object->RawFastPropertyAtPut(index, *value); | 
|  | } | 
|  | object->synchronized_set_map(*new_map); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // This migration is a transition from a map that has run out of property | 
|  | // space. Extend the backing store. | 
|  | int grow_by = new_map->UnusedPropertyFields() + 1; | 
|  | Handle<PropertyArray> old_storage(object->property_array(isolate), isolate); | 
|  | Handle<PropertyArray> new_storage = | 
|  | isolate->factory()->CopyPropertyArrayAndGrow(old_storage, grow_by); | 
|  |  | 
|  | // Properly initialize newly added property. | 
|  | Handle<Object> value; | 
|  | if (details.representation().IsDouble()) { | 
|  | value = isolate->factory()->NewHeapNumberWithHoleNaN(); | 
|  | } else { | 
|  | value = isolate->factory()->uninitialized_value(); | 
|  | } | 
|  | DCHECK_EQ(kField, details.location()); | 
|  | DCHECK_EQ(kData, details.kind()); | 
|  | DCHECK(!index.is_inobject());  // Must be a backing store index. | 
|  | new_storage->set(index.outobject_array_index(), *value); | 
|  |  | 
|  | // From here on we cannot fail and we shouldn't GC anymore. | 
|  | DisallowHeapAllocation no_allocation; | 
|  |  | 
|  | // Set the new property value and do the map transition. | 
|  | object->SetProperties(*new_storage); | 
|  | object->synchronized_set_map(*new_map); | 
|  | return; | 
|  | } | 
|  |  | 
|  | int old_number_of_fields; | 
|  | int number_of_fields = new_map->NumberOfFields(); | 
|  | int inobject = new_map->GetInObjectProperties(); | 
|  | int unused = new_map->UnusedPropertyFields(); | 
|  |  | 
|  | // Nothing to do if no functions were converted to fields and no smis were | 
|  | // converted to doubles. | 
|  | if (!old_map->InstancesNeedRewriting(*new_map, number_of_fields, inobject, | 
|  | unused, &old_number_of_fields)) { | 
|  | object->synchronized_set_map(*new_map); | 
|  | return; | 
|  | } | 
|  |  | 
|  | int total_size = number_of_fields + unused; | 
|  | int external = total_size - inobject; | 
|  | Handle<PropertyArray> array = isolate->factory()->NewPropertyArray(external); | 
|  |  | 
|  | // We use this array to temporarily store the inobject properties. | 
|  | Handle<FixedArray> inobject_props = | 
|  | isolate->factory()->NewFixedArray(inobject); | 
|  |  | 
|  | Handle<DescriptorArray> old_descriptors( | 
|  | old_map->instance_descriptors(isolate, kRelaxedLoad), isolate); | 
|  | Handle<DescriptorArray> new_descriptors( | 
|  | new_map->instance_descriptors(isolate, kRelaxedLoad), isolate); | 
|  | int old_nof = old_map->NumberOfOwnDescriptors(); | 
|  | int new_nof = new_map->NumberOfOwnDescriptors(); | 
|  |  | 
|  | // This method only supports generalizing instances to at least the same | 
|  | // number of properties. | 
|  | DCHECK(old_nof <= new_nof); | 
|  |  | 
|  | for (InternalIndex i : InternalIndex::Range(old_nof)) { | 
|  | PropertyDetails details = new_descriptors->GetDetails(i); | 
|  | if (details.location() != kField) continue; | 
|  | DCHECK_EQ(kData, details.kind()); | 
|  | PropertyDetails old_details = old_descriptors->GetDetails(i); | 
|  | Representation old_representation = old_details.representation(); | 
|  | Representation representation = details.representation(); | 
|  | Handle<Object> value; | 
|  | if (old_details.location() == kDescriptor) { | 
|  | if (old_details.kind() == kAccessor) { | 
|  | // In case of kAccessor -> kData property reconfiguration, the property | 
|  | // must already be prepared for data of certain type. | 
|  | DCHECK(!details.representation().IsNone()); | 
|  | if (details.representation().IsDouble()) { | 
|  | value = isolate->factory()->NewHeapNumberWithHoleNaN(); | 
|  | } else { | 
|  | value = isolate->factory()->uninitialized_value(); | 
|  | } | 
|  | } else { | 
|  | DCHECK_EQ(kData, old_details.kind()); | 
|  | value = handle(old_descriptors->GetStrongValue(isolate, i), isolate); | 
|  | DCHECK(!old_representation.IsDouble() && !representation.IsDouble()); | 
|  | } | 
|  | } else { | 
|  | DCHECK_EQ(kField, old_details.location()); | 
|  | FieldIndex index = FieldIndex::ForDescriptor(isolate, *old_map, i); | 
|  | if (object->IsUnboxedDoubleField(isolate, index)) { | 
|  | uint64_t old_bits = object->RawFastDoublePropertyAsBitsAt(index); | 
|  | value = isolate->factory()->NewHeapNumberFromBits(old_bits); | 
|  | } else { | 
|  | value = handle(object->RawFastPropertyAt(isolate, index), isolate); | 
|  | if (!old_representation.IsDouble() && representation.IsDouble()) { | 
|  | DCHECK_IMPLIES(old_representation.IsNone(), | 
|  | value->IsUninitialized(isolate)); | 
|  | value = Object::NewStorageFor(isolate, value, representation); | 
|  | } else if (old_representation.IsDouble() && | 
|  | !representation.IsDouble()) { | 
|  | value = Object::WrapForRead(isolate, value, old_representation); | 
|  | } | 
|  | } | 
|  | } | 
|  | DCHECK(!(representation.IsDouble() && value->IsSmi())); | 
|  | int target_index = new_descriptors->GetFieldIndex(i); | 
|  | if (target_index < inobject) { | 
|  | inobject_props->set(target_index, *value); | 
|  | } else { | 
|  | array->set(target_index - inobject, *value); | 
|  | } | 
|  | } | 
|  |  | 
|  | for (InternalIndex i : InternalIndex::Range(old_nof, new_nof)) { | 
|  | PropertyDetails details = new_descriptors->GetDetails(i); | 
|  | if (details.location() != kField) continue; | 
|  | DCHECK_EQ(kData, details.kind()); | 
|  | Handle<Object> value; | 
|  | if (details.representation().IsDouble()) { | 
|  | value = isolate->factory()->NewHeapNumberWithHoleNaN(); | 
|  | } else { | 
|  | value = isolate->factory()->uninitialized_value(); | 
|  | } | 
|  | int target_index = new_descriptors->GetFieldIndex(i); | 
|  | if (target_index < inobject) { | 
|  | inobject_props->set(target_index, *value); | 
|  | } else { | 
|  | array->set(target_index - inobject, *value); | 
|  | } | 
|  | } | 
|  |  | 
|  | // From here on we cannot fail and we shouldn't GC anymore. | 
|  | DisallowHeapAllocation no_allocation; | 
|  |  | 
|  | Heap* heap = isolate->heap(); | 
|  |  | 
|  | // Invalidate slots manually later in case of tagged to untagged translation. | 
|  | // In all other cases the recorded slot remains dereferenceable. | 
|  | heap->NotifyObjectLayoutChange(*object, no_allocation, | 
|  | InvalidateRecordedSlots::kNo); | 
|  |  | 
|  | // Copy (real) inobject properties. If necessary, stop at number_of_fields to | 
|  | // avoid overwriting |one_pointer_filler_map|. | 
|  | int limit = std::min(inobject, number_of_fields); | 
|  | for (int i = 0; i < limit; i++) { | 
|  | FieldIndex index = FieldIndex::ForPropertyIndex(*new_map, i); | 
|  | Object value = inobject_props->get(isolate, i); | 
|  | // Can't use JSObject::FastPropertyAtPut() because proper map was not set | 
|  | // yet. | 
|  | if (new_map->IsUnboxedDoubleField(isolate, index)) { | 
|  | DCHECK(value.IsHeapNumber(isolate)); | 
|  | // Ensure that all bits of the double value are preserved. | 
|  | object->RawFastDoublePropertyAsBitsAtPut( | 
|  | index, HeapNumber::cast(value).value_as_bits()); | 
|  | if (i < old_number_of_fields && !old_map->IsUnboxedDoubleField(index)) { | 
|  | // Transition from tagged to untagged slot. | 
|  | MemoryChunk* chunk = MemoryChunk::FromHeapObject(*object); | 
|  | chunk->InvalidateRecordedSlots(*object); | 
|  | } else { | 
|  | #ifdef DEBUG | 
|  | heap->VerifyClearedSlot(*object, object->RawField(index.offset())); | 
|  | #endif | 
|  | } | 
|  | } else { | 
|  | object->RawFastPropertyAtPut(index, value); | 
|  | } | 
|  | } | 
|  |  | 
|  | object->SetProperties(*array); | 
|  |  | 
|  | // Create filler object past the new instance size. | 
|  | int old_instance_size = old_map->instance_size(); | 
|  | int new_instance_size = new_map->instance_size(); | 
|  | int instance_size_delta = old_instance_size - new_instance_size; | 
|  | DCHECK_GE(instance_size_delta, 0); | 
|  |  | 
|  | if (instance_size_delta > 0) { | 
|  | Address address = object->address(); | 
|  | heap->CreateFillerObjectAt(address + new_instance_size, instance_size_delta, | 
|  | ClearRecordedSlots::kYes); | 
|  | } | 
|  |  | 
|  | // We are storing the new map using release store after creating a filler for | 
|  | // the left-over space to avoid races with the sweeper thread. | 
|  | object->synchronized_set_map(*new_map); | 
|  | } | 
|  |  | 
|  | void MigrateFastToSlow(Isolate* isolate, Handle<JSObject> object, | 
|  | Handle<Map> new_map, | 
|  | int expected_additional_properties) { | 
|  | // The global object is always normalized. | 
|  | DCHECK(!object->IsJSGlobalObject(isolate)); | 
|  | // JSGlobalProxy must never be normalized | 
|  | DCHECK(!object->IsJSGlobalProxy(isolate)); | 
|  |  | 
|  | DCHECK_IMPLIES(new_map->is_prototype_map(), | 
|  | Map::IsPrototypeChainInvalidated(*new_map)); | 
|  |  | 
|  | HandleScope scope(isolate); | 
|  | Handle<Map> map(object->map(isolate), isolate); | 
|  |  | 
|  | // Allocate new content. | 
|  | int real_size = map->NumberOfOwnDescriptors(); | 
|  | int property_count = real_size; | 
|  | if (expected_additional_properties > 0) { | 
|  | property_count += expected_additional_properties; | 
|  | } else { | 
|  | // Make space for two more properties. | 
|  | int initial_capacity = V8_DICT_MODE_PROTOTYPES_BOOL | 
|  | ? OrderedNameDictionary::kInitialCapacity | 
|  | : NameDictionary::kInitialCapacity; | 
|  | property_count += initial_capacity; | 
|  | } | 
|  |  | 
|  | Handle<NameDictionary> dictionary; | 
|  | Handle<OrderedNameDictionary> ord_dictionary; | 
|  | if (V8_DICT_MODE_PROTOTYPES_BOOL) { | 
|  | ord_dictionary = | 
|  | isolate->factory()->NewOrderedNameDictionary(property_count); | 
|  | } else { | 
|  | dictionary = isolate->factory()->NewNameDictionary(property_count); | 
|  | } | 
|  |  | 
|  | Handle<DescriptorArray> descs( | 
|  | map->instance_descriptors(isolate, kRelaxedLoad), isolate); | 
|  | for (InternalIndex i : InternalIndex::Range(real_size)) { | 
|  | PropertyDetails details = descs->GetDetails(i); | 
|  | Handle<Name> key(descs->GetKey(isolate, i), isolate); | 
|  | Handle<Object> value; | 
|  | if (details.location() == kField) { | 
|  | FieldIndex index = FieldIndex::ForDescriptor(isolate, *map, i); | 
|  | if (details.kind() == kData) { | 
|  | if (object->IsUnboxedDoubleField(isolate, index)) { | 
|  | double old_value = object->RawFastDoublePropertyAt(index); | 
|  | value = isolate->factory()->NewHeapNumber(old_value); | 
|  | } else { | 
|  | value = handle(object->RawFastPropertyAt(isolate, index), isolate); | 
|  | if (details.representation().IsDouble()) { | 
|  | DCHECK(value->IsHeapNumber(isolate)); | 
|  | double old_value = Handle<HeapNumber>::cast(value)->value(); | 
|  | value = isolate->factory()->NewHeapNumber(old_value); | 
|  | } | 
|  | } | 
|  | } else { | 
|  | DCHECK_EQ(kAccessor, details.kind()); | 
|  | value = handle(object->RawFastPropertyAt(isolate, index), isolate); | 
|  | } | 
|  |  | 
|  | } else { | 
|  | DCHECK_EQ(kDescriptor, details.location()); | 
|  | value = handle(descs->GetStrongValue(isolate, i), isolate); | 
|  | } | 
|  | DCHECK(!value.is_null()); | 
|  | PropertyDetails d(details.kind(), details.attributes(), | 
|  | PropertyCellType::kNoCell); | 
|  | if (V8_DICT_MODE_PROTOTYPES_BOOL) { | 
|  | ord_dictionary = | 
|  | OrderedNameDictionary::Add(isolate, ord_dictionary, key, value, d) | 
|  | .ToHandleChecked(); | 
|  | } else { | 
|  | dictionary = NameDictionary::Add(isolate, dictionary, key, value, d); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!V8_DICT_MODE_PROTOTYPES_BOOL) { | 
|  | // Copy the next enumeration index from instance descriptor. | 
|  | dictionary->set_next_enumeration_index(real_size + 1); | 
|  | } | 
|  |  | 
|  | // From here on we cannot fail and we shouldn't GC anymore. | 
|  | DisallowHeapAllocation no_allocation; | 
|  |  | 
|  | Heap* heap = isolate->heap(); | 
|  |  | 
|  | // Invalidate slots manually later in case the new map has in-object | 
|  | // properties. If not, it is not possible to store an untagged value | 
|  | // in a recorded slot. | 
|  | heap->NotifyObjectLayoutChange(*object, no_allocation, | 
|  | InvalidateRecordedSlots::kNo); | 
|  |  | 
|  | // Resize the object in the heap if necessary. | 
|  | int old_instance_size = map->instance_size(); | 
|  | int new_instance_size = new_map->instance_size(); | 
|  | int instance_size_delta = old_instance_size - new_instance_size; | 
|  | DCHECK_GE(instance_size_delta, 0); | 
|  |  | 
|  | if (instance_size_delta > 0) { | 
|  | heap->CreateFillerObjectAt(object->address() + new_instance_size, | 
|  | instance_size_delta, ClearRecordedSlots::kYes); | 
|  | } | 
|  |  | 
|  | // We are storing the new map using release store after creating a filler for | 
|  | // the left-over space to avoid races with the sweeper thread. | 
|  | object->synchronized_set_map(*new_map); | 
|  |  | 
|  | if (V8_DICT_MODE_PROTOTYPES_BOOL) { | 
|  | object->SetProperties(*ord_dictionary); | 
|  | } else { | 
|  | object->SetProperties(*dictionary); | 
|  | } | 
|  |  | 
|  | // Ensure that in-object space of slow-mode object does not contain random | 
|  | // garbage. | 
|  | int inobject_properties = new_map->GetInObjectProperties(); | 
|  | if (inobject_properties) { | 
|  | MemoryChunk* chunk = MemoryChunk::FromHeapObject(*object); | 
|  | chunk->InvalidateRecordedSlots(*object); | 
|  |  | 
|  | for (int i = 0; i < inobject_properties; i++) { | 
|  | FieldIndex index = FieldIndex::ForPropertyIndex(*new_map, i); | 
|  | object->RawFastPropertyAtPut(index, Smi::zero()); | 
|  | } | 
|  | } | 
|  |  | 
|  | isolate->counters()->props_to_dictionary()->Increment(); | 
|  |  | 
|  | #ifdef DEBUG | 
|  | if (FLAG_trace_normalization) { | 
|  | StdoutStream os; | 
|  | os << "Object properties have been normalized:\n"; | 
|  | object->Print(os); | 
|  | } | 
|  | #endif | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | void JSObject::MigrateToMap(Isolate* isolate, Handle<JSObject> object, | 
|  | Handle<Map> new_map, | 
|  | int expected_additional_properties) { | 
|  | if (object->map(isolate) == *new_map) return; | 
|  | Handle<Map> old_map(object->map(isolate), isolate); | 
|  | NotifyMapChange(old_map, new_map, isolate); | 
|  |  | 
|  | if (old_map->is_dictionary_map()) { | 
|  | // For slow-to-fast migrations JSObject::MigrateSlowToFast() | 
|  | // must be used instead. | 
|  | CHECK(new_map->is_dictionary_map()); | 
|  |  | 
|  | // Slow-to-slow migration is trivial. | 
|  | object->synchronized_set_map(*new_map); | 
|  | } else if (!new_map->is_dictionary_map()) { | 
|  | MigrateFastToFast(isolate, object, new_map); | 
|  | if (old_map->is_prototype_map()) { | 
|  | DCHECK(!old_map->is_stable()); | 
|  | DCHECK(new_map->is_stable()); | 
|  | DCHECK(new_map->owns_descriptors()); | 
|  | DCHECK(old_map->owns_descriptors()); | 
|  | // Transfer ownership to the new map. Keep the descriptor pointer of the | 
|  | // old map intact because the concurrent marker might be iterating the | 
|  | // object with the old map. | 
|  | old_map->set_owns_descriptors(false); | 
|  | DCHECK(old_map->is_abandoned_prototype_map()); | 
|  | // Ensure that no transition was inserted for prototype migrations. | 
|  | DCHECK_EQ(0, TransitionsAccessor(isolate, old_map).NumberOfTransitions()); | 
|  | DCHECK(new_map->GetBackPointer(isolate).IsUndefined(isolate)); | 
|  | DCHECK(object->map(isolate) != *old_map); | 
|  | } | 
|  | } else { | 
|  | MigrateFastToSlow(isolate, object, new_map, expected_additional_properties); | 
|  | } | 
|  |  | 
|  | // Careful: Don't allocate here! | 
|  | // For some callers of this method, |object| might be in an inconsistent | 
|  | // state now: the new map might have a new elements_kind, but the object's | 
|  | // elements pointer hasn't been updated yet. Callers will fix this, but in | 
|  | // the meantime, (indirectly) calling JSObjectVerify() must be avoided. | 
|  | // When adding code here, add a DisallowHeapAllocation too. | 
|  | } | 
|  |  | 
|  | void JSObject::ForceSetPrototype(Handle<JSObject> object, | 
|  | Handle<HeapObject> proto) { | 
|  | // object.__proto__ = proto; | 
|  | Isolate* isolate = object->GetIsolate(); | 
|  | Handle<Map> old_map = Handle<Map>(object->map(), isolate); | 
|  | Handle<Map> new_map = Map::Copy(isolate, old_map, "ForceSetPrototype"); | 
|  | Map::SetPrototype(isolate, new_map, proto); | 
|  | JSObject::MigrateToMap(isolate, object, new_map); | 
|  | } | 
|  |  | 
|  | Maybe<bool> JSObject::SetPropertyWithInterceptor( | 
|  | LookupIterator* it, Maybe<ShouldThrow> should_throw, Handle<Object> value) { | 
|  | DCHECK_EQ(LookupIterator::INTERCEPTOR, it->state()); | 
|  | return SetPropertyWithInterceptorInternal(it, it->GetInterceptor(), | 
|  | should_throw, value); | 
|  | } | 
|  |  | 
|  | Handle<Map> JSObject::GetElementsTransitionMap(Handle<JSObject> object, | 
|  | ElementsKind to_kind) { | 
|  | Handle<Map> map(object->map(), object->GetIsolate()); | 
|  | return Map::TransitionElementsTo(object->GetIsolate(), map, to_kind); | 
|  | } | 
|  |  | 
|  | // static | 
|  | MaybeHandle<NativeContext> JSObject::GetFunctionRealm(Handle<JSObject> object) { | 
|  | DCHECK(object->map().is_constructor()); | 
|  | DCHECK(!object->IsJSFunction()); | 
|  | return object->GetCreationContext(); | 
|  | } | 
|  |  | 
|  | void JSObject::AllocateStorageForMap(Handle<JSObject> object, Handle<Map> map) { | 
|  | DCHECK(object->map().GetInObjectProperties() == map->GetInObjectProperties()); | 
|  | ElementsKind obj_kind = object->map().elements_kind(); | 
|  | ElementsKind map_kind = map->elements_kind(); | 
|  | if (map_kind != obj_kind) { | 
|  | ElementsKind to_kind = GetMoreGeneralElementsKind(map_kind, obj_kind); | 
|  | if (IsDictionaryElementsKind(obj_kind)) { | 
|  | to_kind = obj_kind; | 
|  | } | 
|  | if (IsDictionaryElementsKind(to_kind)) { | 
|  | NormalizeElements(object); | 
|  | } else { | 
|  | TransitionElementsKind(object, to_kind); | 
|  | } | 
|  | map = Map::ReconfigureElementsKind(object->GetIsolate(), map, to_kind); | 
|  | } | 
|  | int number_of_fields = map->NumberOfFields(); | 
|  | int inobject = map->GetInObjectProperties(); | 
|  | int unused = map->UnusedPropertyFields(); | 
|  | int total_size = number_of_fields + unused; | 
|  | int external = total_size - inobject; | 
|  | // Allocate mutable double boxes if necessary. It is always necessary if we | 
|  | // have external properties, but is also necessary if we only have inobject | 
|  | // properties but don't unbox double fields. | 
|  | if (!FLAG_unbox_double_fields || external > 0) { | 
|  | Isolate* isolate = object->GetIsolate(); | 
|  |  | 
|  | Handle<DescriptorArray> descriptors(map->instance_descriptors(kRelaxedLoad), | 
|  | isolate); | 
|  | Handle<FixedArray> storage; | 
|  | if (!FLAG_unbox_double_fields) { | 
|  | storage = isolate->factory()->NewFixedArray(inobject); | 
|  | } | 
|  |  | 
|  | Handle<PropertyArray> array = | 
|  | isolate->factory()->NewPropertyArray(external); | 
|  |  | 
|  | for (InternalIndex i : map->IterateOwnDescriptors()) { | 
|  | PropertyDetails details = descriptors->GetDetails(i); | 
|  | Representation representation = details.representation(); | 
|  | if (!representation.IsDouble()) continue; | 
|  | FieldIndex index = FieldIndex::ForDescriptor(*map, i); | 
|  | if (map->IsUnboxedDoubleField(index)) continue; | 
|  | auto box = isolate->factory()->NewHeapNumberWithHoleNaN(); | 
|  | if (index.is_inobject()) { | 
|  | storage->set(index.property_index(), *box); | 
|  | } else { | 
|  | array->set(index.outobject_array_index(), *box); | 
|  | } | 
|  | } | 
|  |  | 
|  | object->SetProperties(*array); | 
|  |  | 
|  | if (!FLAG_unbox_double_fields) { | 
|  | for (int i = 0; i < inobject; i++) { | 
|  | FieldIndex index = FieldIndex::ForPropertyIndex(*map, i); | 
|  | Object value = storage->get(i); | 
|  | object->RawFastPropertyAtPut(index, value); | 
|  | } | 
|  | } | 
|  | } | 
|  | object->synchronized_set_map(*map); | 
|  | } | 
|  |  | 
|  | void JSObject::MigrateInstance(Isolate* isolate, Handle<JSObject> object) { | 
|  | Handle<Map> original_map(object->map(), isolate); | 
|  | Handle<Map> map = Map::Update(isolate, original_map); | 
|  | map->set_is_migration_target(true); | 
|  | JSObject::MigrateToMap(isolate, object, map); | 
|  | if (FLAG_trace_migration) { | 
|  | object->PrintInstanceMigration(stdout, *original_map, *map); | 
|  | } | 
|  | #if VERIFY_HEAP | 
|  | if (FLAG_verify_heap) { | 
|  | object->JSObjectVerify(isolate); | 
|  | } | 
|  | #endif | 
|  | } | 
|  |  | 
|  | // static | 
|  | bool JSObject::TryMigrateInstance(Isolate* isolate, Handle<JSObject> object) { | 
|  | DisallowDeoptimization no_deoptimization(isolate); | 
|  | Handle<Map> original_map(object->map(), isolate); | 
|  | Handle<Map> new_map; | 
|  | if (!Map::TryUpdate(isolate, original_map).ToHandle(&new_map)) { | 
|  | return false; | 
|  | } | 
|  | JSObject::MigrateToMap(isolate, object, new_map); | 
|  | if (FLAG_trace_migration && *original_map != object->map()) { | 
|  | object->PrintInstanceMigration(stdout, *original_map, object->map()); | 
|  | } | 
|  | #if VERIFY_HEAP | 
|  | if (FLAG_verify_heap) { | 
|  | object->JSObjectVerify(isolate); | 
|  | } | 
|  | #endif | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void JSObject::AddProperty(Isolate* isolate, Handle<JSObject> object, | 
|  | Handle<Name> name, Handle<Object> value, | 
|  | PropertyAttributes attributes) { | 
|  | LookupIterator it(isolate, object, name, object, | 
|  | LookupIterator::OWN_SKIP_INTERCEPTOR); | 
|  | CHECK_NE(LookupIterator::ACCESS_CHECK, it.state()); | 
|  | #ifdef DEBUG | 
|  | uint32_t index; | 
|  | DCHECK(!object->IsJSProxy()); | 
|  | DCHECK(!name->AsArrayIndex(&index)); | 
|  | Maybe<PropertyAttributes> maybe = GetPropertyAttributes(&it); | 
|  | DCHECK(maybe.IsJust()); | 
|  | DCHECK(!it.IsFound()); | 
|  | DCHECK(object->map().is_extensible() || name->IsPrivate()); | 
|  | #endif | 
|  | CHECK(Object::AddDataProperty(&it, value, attributes, | 
|  | Just(ShouldThrow::kThrowOnError), | 
|  | StoreOrigin::kNamed) | 
|  | .IsJust()); | 
|  | } | 
|  |  | 
|  | void JSObject::AddProperty(Isolate* isolate, Handle<JSObject> object, | 
|  | const char* name, Handle<Object> value, | 
|  | PropertyAttributes attributes) { | 
|  | JSObject::AddProperty(isolate, object, | 
|  | isolate->factory()->InternalizeUtf8String(name), value, | 
|  | attributes); | 
|  | } | 
|  |  | 
|  | // Reconfigures a property to a data property with attributes, even if it is not | 
|  | // reconfigurable. | 
|  | // Requires a LookupIterator that does not look at the prototype chain beyond | 
|  | // hidden prototypes. | 
|  | MaybeHandle<Object> JSObject::DefineOwnPropertyIgnoreAttributes( | 
|  | LookupIterator* it, Handle<Object> value, PropertyAttributes attributes, | 
|  | AccessorInfoHandling handling) { | 
|  | MAYBE_RETURN_NULL(DefineOwnPropertyIgnoreAttributes( | 
|  | it, value, attributes, Just(ShouldThrow::kThrowOnError), handling)); | 
|  | return value; | 
|  | } | 
|  |  | 
|  | Maybe<bool> JSObject::DefineOwnPropertyIgnoreAttributes( | 
|  | LookupIterator* it, Handle<Object> value, PropertyAttributes attributes, | 
|  | Maybe<ShouldThrow> should_throw, AccessorInfoHandling handling) { | 
|  | it->UpdateProtector(); | 
|  | Handle<JSObject> object = Handle<JSObject>::cast(it->GetReceiver()); | 
|  |  | 
|  | for (; it->IsFound(); it->Next()) { | 
|  | switch (it->state()) { | 
|  | case LookupIterator::JSPROXY: | 
|  | case LookupIterator::NOT_FOUND: | 
|  | case LookupIterator::TRANSITION: | 
|  | UNREACHABLE(); | 
|  |  | 
|  | case LookupIterator::ACCESS_CHECK: | 
|  | if (!it->HasAccess()) { | 
|  | it->isolate()->ReportFailedAccessCheck(it->GetHolder<JSObject>()); | 
|  | RETURN_VALUE_IF_SCHEDULED_EXCEPTION(it->isolate(), Nothing<bool>()); | 
|  | return Just(true); | 
|  | } | 
|  | break; | 
|  |  | 
|  | // If there's an interceptor, try to store the property with the | 
|  | // interceptor. | 
|  | // In case of success, the attributes will have been reset to the default | 
|  | // attributes of the interceptor, rather than the incoming attributes. | 
|  | // | 
|  | // TODO(verwaest): JSProxy afterwards verify the attributes that the | 
|  | // JSProxy claims it has, and verifies that they are compatible. If not, | 
|  | // they throw. Here we should do the same. | 
|  | case LookupIterator::INTERCEPTOR: | 
|  | if (handling == DONT_FORCE_FIELD) { | 
|  | Maybe<bool> result = | 
|  | JSObject::SetPropertyWithInterceptor(it, should_throw, value); | 
|  | if (result.IsNothing() || result.FromJust()) return result; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case LookupIterator::ACCESSOR: { | 
|  | Handle<Object> accessors = it->GetAccessors(); | 
|  |  | 
|  | // Special handling for AccessorInfo, which behaves like a data | 
|  | // property. | 
|  | if (accessors->IsAccessorInfo() && handling == DONT_FORCE_FIELD) { | 
|  | PropertyAttributes current_attributes = it->property_attributes(); | 
|  | // Ensure the context isn't changed after calling into accessors. | 
|  | AssertNoContextChange ncc(it->isolate()); | 
|  |  | 
|  | // Update the attributes before calling the setter. The setter may | 
|  | // later change the shape of the property. | 
|  | if (current_attributes != attributes) { | 
|  | it->TransitionToAccessorPair(accessors, attributes); | 
|  | } | 
|  |  | 
|  | return Object::SetPropertyWithAccessor(it, value, should_throw); | 
|  | } | 
|  |  | 
|  | it->ReconfigureDataProperty(value, attributes); | 
|  | return Just(true); | 
|  | } | 
|  | case LookupIterator::INTEGER_INDEXED_EXOTIC: | 
|  | return Object::RedefineIncompatibleProperty( | 
|  | it->isolate(), it->GetName(), value, should_throw); | 
|  |  | 
|  | case LookupIterator::DATA: { | 
|  | // Regular property update if the attributes match. | 
|  | if (it->property_attributes() == attributes) { | 
|  | return Object::SetDataProperty(it, value); | 
|  | } | 
|  |  | 
|  | // Special case: properties of typed arrays cannot be reconfigured to | 
|  | // non-writable nor to non-enumerable. | 
|  | if (it->IsElement() && object->HasTypedArrayElements()) { | 
|  | return Object::RedefineIncompatibleProperty( | 
|  | it->isolate(), it->GetName(), value, should_throw); | 
|  | } | 
|  |  | 
|  | // Reconfigure the data property if the attributes mismatch. | 
|  | it->ReconfigureDataProperty(value, attributes); | 
|  |  | 
|  | return Just(true); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return Object::AddDataProperty(it, value, attributes, should_throw, | 
|  | StoreOrigin::kNamed); | 
|  | } | 
|  |  | 
|  | MaybeHandle<Object> JSObject::SetOwnPropertyIgnoreAttributes( | 
|  | Handle<JSObject> object, Handle<Name> name, Handle<Object> value, | 
|  | PropertyAttributes attributes) { | 
|  | DCHECK(!value->IsTheHole()); | 
|  | LookupIterator it(object->GetIsolate(), object, name, object, | 
|  | LookupIterator::OWN); | 
|  | return DefineOwnPropertyIgnoreAttributes(&it, value, attributes); | 
|  | } | 
|  |  | 
|  | MaybeHandle<Object> JSObject::SetOwnElementIgnoreAttributes( | 
|  | Handle<JSObject> object, size_t index, Handle<Object> value, | 
|  | PropertyAttributes attributes) { | 
|  | DCHECK(!object->IsJSTypedArray()); | 
|  | Isolate* isolate = object->GetIsolate(); | 
|  | LookupIterator it(isolate, object, index, object, LookupIterator::OWN); | 
|  | return DefineOwnPropertyIgnoreAttributes(&it, value, attributes); | 
|  | } | 
|  |  | 
|  | MaybeHandle<Object> JSObject::DefinePropertyOrElementIgnoreAttributes( | 
|  | Handle<JSObject> object, Handle<Name> name, Handle<Object> value, | 
|  | PropertyAttributes attributes) { | 
|  | Isolate* isolate = object->GetIsolate(); | 
|  | LookupIterator::Key key(isolate, name); | 
|  | LookupIterator it(isolate, object, key, object, LookupIterator::OWN); | 
|  | return DefineOwnPropertyIgnoreAttributes(&it, value, attributes); | 
|  | } | 
|  |  | 
|  | Maybe<PropertyAttributes> JSObject::GetPropertyAttributesWithInterceptor( | 
|  | LookupIterator* it) { | 
|  | return GetPropertyAttributesWithInterceptorInternal(it, it->GetInterceptor()); | 
|  | } | 
|  |  | 
|  | void JSObject::NormalizeProperties(Isolate* isolate, Handle<JSObject> object, | 
|  | PropertyNormalizationMode mode, | 
|  | int expected_additional_properties, | 
|  | const char* reason) { | 
|  | if (!object->HasFastProperties()) return; | 
|  |  | 
|  | Handle<Map> map(object->map(), isolate); | 
|  | Handle<Map> new_map = | 
|  | Map::Normalize(isolate, map, map->elements_kind(), mode, reason); | 
|  |  | 
|  | JSObject::MigrateToMap(isolate, object, new_map, | 
|  | expected_additional_properties); | 
|  | } | 
|  |  | 
|  | void JSObject::MigrateSlowToFast(Handle<JSObject> object, | 
|  | int unused_property_fields, | 
|  | const char* reason) { | 
|  | if (object->HasFastProperties()) return; | 
|  | DCHECK(!object->IsJSGlobalObject()); | 
|  | Isolate* isolate = object->GetIsolate(); | 
|  | Factory* factory = isolate->factory(); | 
|  |  | 
|  | Handle<NameDictionary> dictionary; | 
|  | Handle<OrderedNameDictionary> ord_dictionary; | 
|  | int number_of_elements; | 
|  | if (V8_DICT_MODE_PROTOTYPES_BOOL) { | 
|  | ord_dictionary = handle(object->property_dictionary_ordered(), isolate); | 
|  | number_of_elements = ord_dictionary->NumberOfElements(); | 
|  | } else { | 
|  | dictionary = handle(object->property_dictionary(), isolate); | 
|  | number_of_elements = dictionary->NumberOfElements(); | 
|  | } | 
|  |  | 
|  | // Make sure we preserve dictionary representation if there are too many | 
|  | // descriptors. | 
|  | if (number_of_elements > kMaxNumberOfDescriptors) return; | 
|  |  | 
|  | Handle<FixedArray> iteration_order; | 
|  | int iteration_length; | 
|  | if (V8_DICT_MODE_PROTOTYPES_BOOL) { | 
|  | // |iteration_order| remains empty handle, we don't need it. | 
|  | iteration_length = ord_dictionary->UsedCapacity(); | 
|  | } else { | 
|  | iteration_order = NameDictionary::IterationIndices(isolate, dictionary); | 
|  | iteration_length = dictionary->NumberOfElements(); | 
|  | } | 
|  |  | 
|  | int number_of_fields = 0; | 
|  |  | 
|  | // Compute the length of the instance descriptor. | 
|  | ReadOnlyRoots roots(isolate); | 
|  | for (int i = 0; i < iteration_length; i++) { | 
|  | PropertyKind kind; | 
|  | if (V8_DICT_MODE_PROTOTYPES_BOOL) { | 
|  | InternalIndex index(i); | 
|  | Object key = ord_dictionary->KeyAt(index); | 
|  | if (!OrderedNameDictionary::IsKey(roots, key)) { | 
|  | // Ignore deleted entries. | 
|  | continue; | 
|  | } | 
|  | kind = ord_dictionary->DetailsAt(index).kind(); | 
|  | } else { | 
|  | InternalIndex index(Smi::ToInt(iteration_order->get(i))); | 
|  | DCHECK(dictionary->IsKey(roots, dictionary->KeyAt(isolate, index))); | 
|  | kind = dictionary->DetailsAt(index).kind(); | 
|  | } | 
|  |  | 
|  | if (kind == kData) { | 
|  | number_of_fields += 1; | 
|  | } | 
|  | } | 
|  |  | 
|  | Handle<Map> old_map(object->map(), isolate); | 
|  |  | 
|  | int inobject_props = old_map->GetInObjectProperties(); | 
|  |  | 
|  | // Allocate new map. | 
|  | Handle<Map> new_map = Map::CopyDropDescriptors(isolate, old_map); | 
|  | // We should not only set this bit if we need to. We should not retain the | 
|  | // old bit because turning a map into dictionary always sets this bit. | 
|  | new_map->set_may_have_interesting_symbols(new_map->has_named_interceptor() || | 
|  | new_map->is_access_check_needed()); | 
|  | new_map->set_is_dictionary_map(false); | 
|  |  | 
|  | NotifyMapChange(old_map, new_map, isolate); | 
|  |  | 
|  | if (number_of_elements == 0) { | 
|  | DisallowHeapAllocation no_gc; | 
|  | DCHECK_LE(unused_property_fields, inobject_props); | 
|  | // Transform the object. | 
|  | new_map->SetInObjectUnusedPropertyFields(inobject_props); | 
|  | object->synchronized_set_map(*new_map); | 
|  | object->SetProperties(ReadOnlyRoots(isolate).empty_fixed_array()); | 
|  | // Check that it really works. | 
|  | DCHECK(object->HasFastProperties()); | 
|  | if (FLAG_trace_maps) { | 
|  | LOG(isolate, MapEvent("SlowToFast", old_map, new_map, reason)); | 
|  | } | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Allocate the instance descriptor. | 
|  | Handle<DescriptorArray> descriptors = | 
|  | DescriptorArray::Allocate(isolate, number_of_elements, 0); | 
|  |  | 
|  | int number_of_allocated_fields = | 
|  | number_of_fields + unused_property_fields - inobject_props; | 
|  | if (number_of_allocated_fields < 0) { | 
|  | // There is enough inobject space for all fields (including unused). | 
|  | number_of_allocated_fields = 0; | 
|  | unused_property_fields = inobject_props - number_of_fields; | 
|  | } | 
|  |  | 
|  | // Allocate the property array for the fields. | 
|  | Handle<PropertyArray> fields = | 
|  | factory->NewPropertyArray(number_of_allocated_fields); | 
|  |  | 
|  | bool is_transitionable_elements_kind = | 
|  | IsTransitionableFastElementsKind(old_map->elements_kind()); | 
|  |  | 
|  | // Fill in the instance descriptor and the fields. | 
|  | int current_offset = 0; | 
|  | int descriptor_index = 0; | 
|  | for (int i = 0; i < iteration_length; i++) { | 
|  | Name k; | 
|  | Object value; | 
|  | PropertyDetails details = PropertyDetails::Empty(); | 
|  |  | 
|  | if (V8_DICT_MODE_PROTOTYPES_BOOL) { | 
|  | InternalIndex index(i); | 
|  | Object key_obj = ord_dictionary->KeyAt(index); | 
|  | if (!OrderedNameDictionary::IsKey(roots, key_obj)) { | 
|  | continue; | 
|  | } | 
|  | k = Name::cast(key_obj); | 
|  |  | 
|  | value = ord_dictionary->ValueAt(index); | 
|  | details = ord_dictionary->DetailsAt(index); | 
|  | } else { | 
|  | InternalIndex index(Smi::ToInt(iteration_order->get(i))); | 
|  | k = dictionary->NameAt(index); | 
|  |  | 
|  | value = dictionary->ValueAt(index); | 
|  | details = dictionary->DetailsAt(index); | 
|  | } | 
|  |  | 
|  | // Dictionary keys are internalized upon insertion. | 
|  | // TODO(jkummerow): Turn this into a DCHECK if it's not hit in the wild. | 
|  | CHECK(k.IsUniqueName()); | 
|  | Handle<Name> key(k, isolate); | 
|  |  | 
|  | // Properly mark the {new_map} if the {key} is an "interesting symbol". | 
|  | if (key->IsInterestingSymbol()) { | 
|  | new_map->set_may_have_interesting_symbols(true); | 
|  | } | 
|  |  | 
|  | DCHECK_EQ(kField, details.location()); | 
|  | DCHECK_EQ(PropertyConstness::kMutable, details.constness()); | 
|  |  | 
|  | Descriptor d; | 
|  | if (details.kind() == kData) { | 
|  | // Ensure that we make constant field only when elements kind is not | 
|  | // transitionable. | 
|  | PropertyConstness constness = is_transitionable_elements_kind | 
|  | ? PropertyConstness::kMutable | 
|  | : PropertyConstness::kConst; | 
|  | d = Descriptor::DataField( | 
|  | key, current_offset, details.attributes(), constness, | 
|  | // TODO(verwaest): value->OptimalRepresentation(); | 
|  | Representation::Tagged(), MaybeObjectHandle(FieldType::Any(isolate))); | 
|  | } else { | 
|  | DCHECK_EQ(kAccessor, details.kind()); | 
|  | d = Descriptor::AccessorConstant(key, handle(value, isolate), | 
|  | details.attributes()); | 
|  | } | 
|  | details = d.GetDetails(); | 
|  | if (details.location() == kField) { | 
|  | if (current_offset < inobject_props) { | 
|  | object->InObjectPropertyAtPut(current_offset, value, | 
|  | UPDATE_WRITE_BARRIER); | 
|  | } else { | 
|  | int offset = current_offset - inobject_props; | 
|  | fields->set(offset, value); | 
|  | } | 
|  | current_offset += details.field_width_in_words(); | 
|  | } | 
|  | descriptors->Set(InternalIndex(descriptor_index++), &d); | 
|  | } | 
|  | DCHECK_EQ(current_offset, number_of_fields); | 
|  | DCHECK_EQ(descriptor_index, number_of_elements); | 
|  |  | 
|  | descriptors->Sort(); | 
|  |  | 
|  | Handle<LayoutDescriptor> layout_descriptor = LayoutDescriptor::New( | 
|  | isolate, new_map, descriptors, descriptors->number_of_descriptors()); | 
|  |  | 
|  | DisallowHeapAllocation no_gc; | 
|  | new_map->InitializeDescriptors(isolate, *descriptors, *layout_descriptor); | 
|  | if (number_of_allocated_fields == 0) { | 
|  | new_map->SetInObjectUnusedPropertyFields(unused_property_fields); | 
|  | } else { | 
|  | new_map->SetOutOfObjectUnusedPropertyFields(unused_property_fields); | 
|  | } | 
|  |  | 
|  | if (FLAG_trace_maps) { | 
|  | LOG(isolate, MapEvent("SlowToFast", old_map, new_map, reason)); | 
|  | } | 
|  | // Transform the object. | 
|  | object->synchronized_set_map(*new_map); | 
|  |  | 
|  | object->SetProperties(*fields); | 
|  | DCHECK(object->IsJSObject()); | 
|  |  | 
|  | // Check that it really works. | 
|  | DCHECK(object->HasFastProperties()); | 
|  | } | 
|  |  | 
|  | void JSObject::RequireSlowElements(NumberDictionary dictionary) { | 
|  | DCHECK_NE(dictionary, | 
|  | ReadOnlyRoots(GetIsolate()).empty_slow_element_dictionary()); | 
|  | if (dictionary.requires_slow_elements()) return; | 
|  | dictionary.set_requires_slow_elements(); | 
|  | if (map().is_prototype_map()) { | 
|  | // If this object is a prototype (the callee will check), invalidate any | 
|  | // prototype chains involving it. | 
|  | InvalidatePrototypeChains(map()); | 
|  | } | 
|  | } | 
|  |  | 
|  | Handle<NumberDictionary> JSObject::NormalizeElements(Handle<JSObject> object) { | 
|  | DCHECK(!object->HasTypedArrayElements()); | 
|  | Isolate* isolate = object->GetIsolate(); | 
|  | bool is_sloppy_arguments = object->HasSloppyArgumentsElements(); | 
|  | { | 
|  | DisallowHeapAllocation no_gc; | 
|  | FixedArrayBase elements = object->elements(); | 
|  |  | 
|  | if (is_sloppy_arguments) { | 
|  | elements = SloppyArgumentsElements::cast(elements).arguments(); | 
|  | } | 
|  |  | 
|  | if (elements.IsNumberDictionary()) { | 
|  | return handle(NumberDictionary::cast(elements), isolate); | 
|  | } | 
|  | } | 
|  |  | 
|  | DCHECK(object->HasSmiOrObjectElements() || object->HasDoubleElements() || | 
|  | object->HasFastArgumentsElements() || | 
|  | object->HasFastStringWrapperElements() || | 
|  | object->HasSealedElements() || object->HasNonextensibleElements()); | 
|  |  | 
|  | Handle<NumberDictionary> dictionary = | 
|  | object->GetElementsAccessor()->Normalize(object); | 
|  |  | 
|  | // Switch to using the dictionary as the backing storage for elements. | 
|  | ElementsKind target_kind = is_sloppy_arguments | 
|  | ? SLOW_SLOPPY_ARGUMENTS_ELEMENTS | 
|  | : object->HasFastStringWrapperElements() | 
|  | ? SLOW_STRING_WRAPPER_ELEMENTS | 
|  | : DICTIONARY_ELEMENTS; | 
|  | Handle<Map> new_map = JSObject::GetElementsTransitionMap(object, target_kind); | 
|  | // Set the new map first to satify the elements type assert in set_elements(). | 
|  | JSObject::MigrateToMap(isolate, object, new_map); | 
|  |  | 
|  | if (is_sloppy_arguments) { | 
|  | SloppyArgumentsElements::cast(object->elements()) | 
|  | .set_arguments(*dictionary); | 
|  | } else { | 
|  | object->set_elements(*dictionary); | 
|  | } | 
|  |  | 
|  | isolate->counters()->elements_to_dictionary()->Increment(); | 
|  |  | 
|  | #ifdef DEBUG | 
|  | if (FLAG_trace_normalization) { | 
|  | StdoutStream os; | 
|  | os << "Object elements have been normalized:\n"; | 
|  | object->Print(os); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | DCHECK(object->HasDictionaryElements() || | 
|  | object->HasSlowArgumentsElements() || | 
|  | object->HasSlowStringWrapperElements()); | 
|  | return dictionary; | 
|  | } | 
|  |  | 
|  | Maybe<bool> JSObject::DeletePropertyWithInterceptor(LookupIterator* it, | 
|  | ShouldThrow should_throw) { | 
|  | Isolate* isolate = it->isolate(); | 
|  | // Make sure that the top context does not change when doing callbacks or | 
|  | // interceptor calls. | 
|  | AssertNoContextChange ncc(isolate); | 
|  |  | 
|  | DCHECK_EQ(LookupIterator::INTERCEPTOR, it->state()); | 
|  | Handle<InterceptorInfo> interceptor(it->GetInterceptor()); | 
|  | if (interceptor->deleter().IsUndefined(isolate)) return Nothing<bool>(); | 
|  |  | 
|  | Handle<JSObject> holder = it->GetHolder<JSObject>(); | 
|  | Handle<Object> receiver = it->GetReceiver(); | 
|  | if (!receiver->IsJSReceiver()) { | 
|  | ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, receiver, | 
|  | Object::ConvertReceiver(isolate, receiver), | 
|  | Nothing<bool>()); | 
|  | } | 
|  |  | 
|  | PropertyCallbackArguments args(isolate, interceptor->data(), *receiver, | 
|  | *holder, Just(should_throw)); | 
|  | Handle<Object> result; | 
|  | if (it->IsElement(*holder)) { | 
|  | result = args.CallIndexedDeleter(interceptor, it->array_index()); | 
|  | } else { | 
|  | result = args.CallNamedDeleter(interceptor, it->name()); | 
|  | } | 
|  |  | 
|  | RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>()); | 
|  | if (result.is_null()) return Nothing<bool>(); | 
|  |  | 
|  | DCHECK(result->IsBoolean()); | 
|  | // Rebox CustomArguments::kReturnValueOffset before returning. | 
|  | return Just(result->IsTrue(isolate)); | 
|  | } | 
|  |  | 
|  | Maybe<bool> JSObject::CreateDataProperty(LookupIterator* it, | 
|  | Handle<Object> value, | 
|  | Maybe<ShouldThrow> should_throw) { | 
|  | DCHECK(it->GetReceiver()->IsJSObject()); | 
|  | MAYBE_RETURN(JSReceiver::GetPropertyAttributes(it), Nothing<bool>()); | 
|  | Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(it->GetReceiver()); | 
|  | Isolate* isolate = receiver->GetIsolate(); | 
|  |  | 
|  | if (it->IsFound()) { | 
|  | Maybe<PropertyAttributes> attributes = GetPropertyAttributes(it); | 
|  | MAYBE_RETURN(attributes, Nothing<bool>()); | 
|  | if ((attributes.FromJust() & DONT_DELETE) != 0) { | 
|  | RETURN_FAILURE( | 
|  | isolate, GetShouldThrow(isolate, should_throw), | 
|  | NewTypeError(MessageTemplate::kRedefineDisallowed, it->GetName())); | 
|  | } | 
|  | } else { | 
|  | if (!JSObject::IsExtensible(Handle<JSObject>::cast(it->GetReceiver()))) { | 
|  | RETURN_FAILURE( | 
|  | isolate, GetShouldThrow(isolate, should_throw), | 
|  | NewTypeError(MessageTemplate::kDefineDisallowed, it->GetName())); | 
|  | } | 
|  | } | 
|  |  | 
|  | RETURN_ON_EXCEPTION_VALUE(it->isolate(), | 
|  | DefineOwnPropertyIgnoreAttributes(it, value, NONE), | 
|  | Nothing<bool>()); | 
|  |  | 
|  | return Just(true); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | template <typename Dictionary> | 
|  | bool TestDictionaryPropertiesIntegrityLevel(Dictionary dict, | 
|  | ReadOnlyRoots roots, | 
|  | PropertyAttributes level) { | 
|  | DCHECK(level == SEALED || level == FROZEN); | 
|  |  | 
|  | for (InternalIndex i : dict.IterateEntries()) { | 
|  | Object key; | 
|  | if (!dict.ToKey(roots, i, &key)) continue; | 
|  | if (key.FilterKey(ALL_PROPERTIES)) continue; | 
|  | PropertyDetails details = dict.DetailsAt(i); | 
|  | if (details.IsConfigurable()) return false; | 
|  | if (level == FROZEN && details.kind() == kData && !details.IsReadOnly()) { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool TestFastPropertiesIntegrityLevel(Map map, PropertyAttributes level) { | 
|  | DCHECK(level == SEALED || level == FROZEN); | 
|  | DCHECK(!map.IsCustomElementsReceiverMap()); | 
|  | DCHECK(!map.is_dictionary_map()); | 
|  |  | 
|  | DescriptorArray descriptors = map.instance_descriptors(kRelaxedLoad); | 
|  | for (InternalIndex i : map.IterateOwnDescriptors()) { | 
|  | if (descriptors.GetKey(i).IsPrivate()) continue; | 
|  | PropertyDetails details = descriptors.GetDetails(i); | 
|  | if (details.IsConfigurable()) return false; | 
|  | if (level == FROZEN && details.kind() == kData && !details.IsReadOnly()) { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool TestPropertiesIntegrityLevel(JSObject object, PropertyAttributes level) { | 
|  | DCHECK(!object.map().IsCustomElementsReceiverMap()); | 
|  |  | 
|  | if (object.HasFastProperties()) { | 
|  | return TestFastPropertiesIntegrityLevel(object.map(), level); | 
|  | } | 
|  |  | 
|  | if (V8_DICT_MODE_PROTOTYPES_BOOL) { | 
|  | return TestDictionaryPropertiesIntegrityLevel( | 
|  | object.property_dictionary_ordered(), object.GetReadOnlyRoots(), level); | 
|  | } else { | 
|  | return TestDictionaryPropertiesIntegrityLevel( | 
|  | object.property_dictionary(), object.GetReadOnlyRoots(), level); | 
|  | } | 
|  | } | 
|  |  | 
|  | bool TestElementsIntegrityLevel(JSObject object, PropertyAttributes level) { | 
|  | DCHECK(!object.HasSloppyArgumentsElements()); | 
|  |  | 
|  | ElementsKind kind = object.GetElementsKind(); | 
|  |  | 
|  | if (IsDictionaryElementsKind(kind)) { | 
|  | return TestDictionaryPropertiesIntegrityLevel( | 
|  | NumberDictionary::cast(object.elements()), object.GetReadOnlyRoots(), | 
|  | level); | 
|  | } | 
|  | if (IsTypedArrayElementsKind(kind)) { | 
|  | if (level == FROZEN && JSArrayBufferView::cast(object).byte_length() > 0) { | 
|  | return false;  // TypedArrays with elements can't be frozen. | 
|  | } | 
|  | return TestPropertiesIntegrityLevel(object, level); | 
|  | } | 
|  | if (IsFrozenElementsKind(kind)) return true; | 
|  | if (IsSealedElementsKind(kind) && level != FROZEN) return true; | 
|  | if (IsNonextensibleElementsKind(kind) && level == NONE) return true; | 
|  |  | 
|  | ElementsAccessor* accessor = ElementsAccessor::ForKind(kind); | 
|  | // Only DICTIONARY_ELEMENTS and SLOW_SLOPPY_ARGUMENTS_ELEMENTS have | 
|  | // PropertyAttributes so just test if empty | 
|  | return accessor->NumberOfElements(object) == 0; | 
|  | } | 
|  |  | 
|  | bool FastTestIntegrityLevel(JSObject object, PropertyAttributes level) { | 
|  | DCHECK(!object.map().IsCustomElementsReceiverMap()); | 
|  |  | 
|  | return !object.map().is_extensible() && | 
|  | TestElementsIntegrityLevel(object, level) && | 
|  | TestPropertiesIntegrityLevel(object, level); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | Maybe<bool> JSObject::TestIntegrityLevel(Handle<JSObject> object, | 
|  | IntegrityLevel level) { | 
|  | if (!object->map().IsCustomElementsReceiverMap() && | 
|  | !object->HasSloppyArgumentsElements()) { | 
|  | return Just(FastTestIntegrityLevel(*object, level)); | 
|  | } | 
|  | return GenericTestIntegrityLevel(Handle<JSReceiver>::cast(object), level); | 
|  | } | 
|  |  | 
|  | Maybe<bool> JSObject::PreventExtensions(Handle<JSObject> object, | 
|  | ShouldThrow should_throw) { | 
|  | Isolate* isolate = object->GetIsolate(); | 
|  |  | 
|  | if (!object->HasSloppyArgumentsElements()) { | 
|  | return PreventExtensionsWithTransition<NONE>(object, should_throw); | 
|  | } | 
|  |  | 
|  | if (object->IsAccessCheckNeeded() && | 
|  | !isolate->MayAccess(handle(isolate->context(), isolate), object)) { | 
|  | isolate->ReportFailedAccessCheck(object); | 
|  | RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>()); | 
|  | RETURN_FAILURE(isolate, should_throw, | 
|  | NewTypeError(MessageTemplate::kNoAccess)); | 
|  | } | 
|  |  | 
|  | if (!object->map().is_extensible()) return Just(true); | 
|  |  | 
|  | if (object->IsJSGlobalProxy()) { | 
|  | PrototypeIterator iter(isolate, object); | 
|  | if (iter.IsAtEnd()) return Just(true); | 
|  | DCHECK(PrototypeIterator::GetCurrent(iter)->IsJSGlobalObject()); | 
|  | return PreventExtensions(PrototypeIterator::GetCurrent<JSObject>(iter), | 
|  | should_throw); | 
|  | } | 
|  |  | 
|  | if (object->map().has_named_interceptor() || | 
|  | object->map().has_indexed_interceptor()) { | 
|  | RETURN_FAILURE(isolate, should_throw, | 
|  | NewTypeError(MessageTemplate::kCannotPreventExt)); | 
|  | } | 
|  |  | 
|  | if (!object->HasTypedArrayElements()) { | 
|  | // If there are fast elements we normalize. | 
|  | Handle<NumberDictionary> dictionary = NormalizeElements(object); | 
|  | DCHECK(object->HasDictionaryElements() || | 
|  | object->HasSlowArgumentsElements()); | 
|  |  | 
|  | // Make sure that we never go back to fast case. | 
|  | if (*dictionary != ReadOnlyRoots(isolate).empty_slow_element_dictionary()) { | 
|  | object->RequireSlowElements(*dictionary); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Do a map transition, other objects with this map may still | 
|  | // be extensible. | 
|  | // TODO(adamk): Extend the NormalizedMapCache to handle non-extensible maps. | 
|  | Handle<Map> new_map = | 
|  | Map::Copy(isolate, handle(object->map(), isolate), "PreventExtensions"); | 
|  |  | 
|  | new_map->set_is_extensible(false); | 
|  | JSObject::MigrateToMap(isolate, object, new_map); | 
|  | DCHECK(!object->map().is_extensible()); | 
|  |  | 
|  | return Just(true); | 
|  | } | 
|  |  | 
|  | bool JSObject::IsExtensible(Handle<JSObject> object) { | 
|  | Isolate* isolate = object->GetIsolate(); | 
|  | if (object->IsAccessCheckNeeded() && | 
|  | !isolate->MayAccess(handle(isolate->context(), isolate), object)) { | 
|  | return true; | 
|  | } | 
|  | if (object->IsJSGlobalProxy()) { | 
|  | PrototypeIterator iter(isolate, *object); | 
|  | if (iter.IsAtEnd()) return false; | 
|  | DCHECK(iter.GetCurrent().IsJSGlobalObject()); | 
|  | return iter.GetCurrent<JSObject>().map().is_extensible(); | 
|  | } | 
|  | return object->map().is_extensible(); | 
|  | } | 
|  |  | 
|  | template <typename Dictionary> | 
|  | void JSObject::ApplyAttributesToDictionary( | 
|  | Isolate* isolate, ReadOnlyRoots roots, Handle<Dictionary> dictionary, | 
|  | const PropertyAttributes attributes) { | 
|  | for (InternalIndex i : dictionary->IterateEntries()) { | 
|  | Object k; | 
|  | if (!dictionary->ToKey(roots, i, &k)) continue; | 
|  | if (k.FilterKey(ALL_PROPERTIES)) continue; | 
|  | PropertyDetails details = dictionary->DetailsAt(i); | 
|  | int attrs = attributes; | 
|  | // READ_ONLY is an invalid attribute for JS setters/getters. | 
|  | if ((attributes & READ_ONLY) && details.kind() == kAccessor) { | 
|  | Object v = dictionary->ValueAt(i); | 
|  | if (v.IsAccessorPair()) attrs &= ~READ_ONLY; | 
|  | } | 
|  | details = details.CopyAddAttributes(static_cast<PropertyAttributes>(attrs)); | 
|  | dictionary->DetailsAtPut(i, details); | 
|  | } | 
|  | } | 
|  |  | 
|  | template void JSObject::ApplyAttributesToDictionary( | 
|  | Isolate* isolate, ReadOnlyRoots roots, Handle<NumberDictionary> dictionary, | 
|  | const PropertyAttributes attributes); | 
|  |  | 
|  | Handle<NumberDictionary> CreateElementDictionary(Isolate* isolate, | 
|  | Handle<JSObject> object) { | 
|  | Handle<NumberDictionary> new_element_dictionary; | 
|  | if (!object->HasTypedArrayElements() && !object->HasDictionaryElements() && | 
|  | !object->HasSlowStringWrapperElements()) { | 
|  | int length = object->IsJSArray() | 
|  | ? Smi::ToInt(Handle<JSArray>::cast(object)->length()) | 
|  | : object->elements().length(); | 
|  | new_element_dictionary = | 
|  | length == 0 ? isolate->factory()->empty_slow_element_dictionary() | 
|  | : object->GetElementsAccessor()->Normalize(object); | 
|  | } | 
|  | return new_element_dictionary; | 
|  | } | 
|  |  | 
|  | template <PropertyAttributes attrs> | 
|  | Maybe<bool> JSObject::PreventExtensionsWithTransition( | 
|  | Handle<JSObject> object, ShouldThrow should_throw) { | 
|  | STATIC_ASSERT(attrs == NONE || attrs == SEALED || attrs == FROZEN); | 
|  |  | 
|  | // Sealing/freezing sloppy arguments or namespace objects should be handled | 
|  | // elsewhere. | 
|  | DCHECK(!object->HasSloppyArgumentsElements()); | 
|  | DCHECK_IMPLIES(object->IsJSModuleNamespace(), attrs == NONE); | 
|  |  | 
|  | Isolate* isolate = object->GetIsolate(); | 
|  | if (object->IsAccessCheckNeeded() && | 
|  | !isolate->MayAccess(handle(isolate->context(), isolate), object)) { | 
|  | isolate->ReportFailedAccessCheck(object); | 
|  | RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>()); | 
|  | RETURN_FAILURE(isolate, should_throw, | 
|  | NewTypeError(MessageTemplate::kNoAccess)); | 
|  | } | 
|  |  | 
|  | if (attrs == NONE && !object->map().is_extensible()) return Just(true); | 
|  | { | 
|  | ElementsKind old_elements_kind = object->map().elements_kind(); | 
|  | if (IsFrozenElementsKind(old_elements_kind)) return Just(true); | 
|  | if (attrs != FROZEN && IsSealedElementsKind(old_elements_kind)) | 
|  | return Just(true); | 
|  | } | 
|  |  | 
|  | if (object->IsJSGlobalProxy()) { | 
|  | PrototypeIterator iter(isolate, object); | 
|  | if (iter.IsAtEnd()) return Just(true); | 
|  | DCHECK(PrototypeIterator::GetCurrent(iter)->IsJSGlobalObject()); | 
|  | return PreventExtensionsWithTransition<attrs>( | 
|  | PrototypeIterator::GetCurrent<JSObject>(iter), should_throw); | 
|  | } | 
|  |  | 
|  | if (object->map().has_named_interceptor() || | 
|  | object->map().has_indexed_interceptor()) { | 
|  | MessageTemplate message = MessageTemplate::kNone; | 
|  | switch (attrs) { | 
|  | case NONE: | 
|  | message = MessageTemplate::kCannotPreventExt; | 
|  | break; | 
|  |  | 
|  | case SEALED: | 
|  | message = MessageTemplate::kCannotSeal; | 
|  | break; | 
|  |  | 
|  | case FROZEN: | 
|  | message = MessageTemplate::kCannotFreeze; | 
|  | break; | 
|  | } | 
|  | RETURN_FAILURE(isolate, should_throw, NewTypeError(message)); | 
|  | } | 
|  |  | 
|  | Handle<Symbol> transition_marker; | 
|  | if (attrs == NONE) { | 
|  | transition_marker = isolate->factory()->nonextensible_symbol(); | 
|  | } else if (attrs == SEALED) { | 
|  | transition_marker = isolate->factory()->sealed_symbol(); | 
|  | } else { | 
|  | DCHECK(attrs == FROZEN); | 
|  | transition_marker = isolate->factory()->frozen_symbol(); | 
|  | } | 
|  |  | 
|  | // Currently, there are only have sealed/frozen Object element kinds and | 
|  | // Map::MigrateToMap doesn't handle properties' attributes reconfiguring and | 
|  | // elements kind change in one go. If seal or freeze with Smi or Double | 
|  | // elements kind, we will transition to Object elements kind first to make | 
|  | // sure of valid element access. | 
|  | if (FLAG_enable_sealed_frozen_elements_kind) { | 
|  | switch (object->map().elements_kind()) { | 
|  | case PACKED_SMI_ELEMENTS: | 
|  | case PACKED_DOUBLE_ELEMENTS: | 
|  | JSObject::TransitionElementsKind(object, PACKED_ELEMENTS); | 
|  | break; | 
|  | case HOLEY_SMI_ELEMENTS: | 
|  | case HOLEY_DOUBLE_ELEMENTS: | 
|  | JSObject::TransitionElementsKind(object, HOLEY_ELEMENTS); | 
|  | break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Make sure we only use this element dictionary in case we can't transition | 
|  | // to sealed, frozen elements kind. | 
|  | Handle<NumberDictionary> new_element_dictionary; | 
|  |  | 
|  | Handle<Map> old_map(object->map(), isolate); | 
|  | old_map = Map::Update(isolate, old_map); | 
|  | TransitionsAccessor transitions(isolate, old_map); | 
|  | Map transition = transitions.SearchSpecial(*transition_marker); | 
|  | if (!transition.is_null()) { | 
|  | Handle<Map> transition_map(transition, isolate); | 
|  | DCHECK(transition_map->has_dictionary_elements() || | 
|  | transition_map->has_typed_array_elements() || | 
|  | transition_map->elements_kind() == SLOW_STRING_WRAPPER_ELEMENTS || | 
|  | transition_map->has_any_nonextensible_elements()); | 
|  | DCHECK(!transition_map->is_extensible()); | 
|  | if (!transition_map->has_any_nonextensible_elements()) { | 
|  | new_element_dictionary = CreateElementDictionary(isolate, object); | 
|  | } | 
|  | JSObject::MigrateToMap(isolate, object, transition_map); | 
|  | } else if (transitions.CanHaveMoreTransitions()) { | 
|  | // Create a new descriptor array with the appropriate property attributes | 
|  | Handle<Map> new_map = Map::CopyForPreventExtensions( | 
|  | isolate, old_map, attrs, transition_marker, "CopyForPreventExtensions"); | 
|  | if (!new_map->has_any_nonextensible_elements()) { | 
|  | new_element_dictionary = CreateElementDictionary(isolate, object); | 
|  | } | 
|  | JSObject::MigrateToMap(isolate, object, new_map); | 
|  | } else { | 
|  | DCHECK(old_map->is_dictionary_map() || !old_map->is_prototype_map()); | 
|  | // Slow path: need to normalize properties for safety | 
|  | NormalizeProperties(isolate, object, CLEAR_INOBJECT_PROPERTIES, 0, | 
|  | "SlowPreventExtensions"); | 
|  |  | 
|  | // Create a new map, since other objects with this map may be extensible. | 
|  | // TODO(adamk): Extend the NormalizedMapCache to handle non-extensible maps. | 
|  | Handle<Map> new_map = Map::Copy(isolate, handle(object->map(), isolate), | 
|  | "SlowCopyForPreventExtensions"); | 
|  | new_map->set_is_extensible(false); | 
|  | new_element_dictionary = CreateElementDictionary(isolate, object); | 
|  | if (!new_element_dictionary.is_null()) { | 
|  | ElementsKind new_kind = | 
|  | IsStringWrapperElementsKind(old_map->elements_kind()) | 
|  | ? SLOW_STRING_WRAPPER_ELEMENTS | 
|  | : DICTIONARY_ELEMENTS; | 
|  | new_map->set_elements_kind(new_kind); | 
|  | } | 
|  | JSObject::MigrateToMap(isolate, object, new_map); | 
|  |  | 
|  | if (attrs != NONE) { | 
|  | ReadOnlyRoots roots(isolate); | 
|  | if (object->IsJSGlobalObject()) { | 
|  | Handle<GlobalDictionary> dictionary( | 
|  | JSGlobalObject::cast(*object).global_dictionary(), isolate); | 
|  | JSObject::ApplyAttributesToDictionary(isolate, roots, dictionary, | 
|  | attrs); | 
|  | } else if (V8_DICT_MODE_PROTOTYPES_BOOL) { | 
|  | Handle<OrderedNameDictionary> dictionary( | 
|  | object->property_dictionary_ordered(), isolate); | 
|  | JSObject::ApplyAttributesToDictionary(isolate, roots, dictionary, | 
|  | attrs); | 
|  | } else { | 
|  | Handle<NameDictionary> dictionary(object->property_dictionary(), | 
|  | isolate); | 
|  | JSObject::ApplyAttributesToDictionary(isolate, roots, dictionary, | 
|  | attrs); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (object->map().has_any_nonextensible_elements()) { | 
|  | DCHECK(new_element_dictionary.is_null()); | 
|  | return Just(true); | 
|  | } | 
|  |  | 
|  | // Both seal and preventExtensions always go through without modifications to | 
|  | // typed array elements. Freeze works only if there are no actual elements. | 
|  | if (object->HasTypedArrayElements()) { | 
|  | if (attrs == FROZEN && JSArrayBufferView::cast(*object).byte_length() > 0) { | 
|  | isolate->Throw(*isolate->factory()->NewTypeError( | 
|  | MessageTemplate::kCannotFreezeArrayBufferView)); | 
|  | return Nothing<bool>(); | 
|  | } | 
|  | return Just(true); | 
|  | } | 
|  |  | 
|  | DCHECK(object->map().has_dictionary_elements() || | 
|  | object->map().elements_kind() == SLOW_STRING_WRAPPER_ELEMENTS); | 
|  | if (!new_element_dictionary.is_null()) { | 
|  | object->set_elements(*new_element_dictionary); | 
|  | } | 
|  |  | 
|  | if (object->elements() != | 
|  | ReadOnlyRoots(isolate).empty_slow_element_dictionary()) { | 
|  | Handle<NumberDictionary> dictionary(object->element_dictionary(), isolate); | 
|  | // Make sure we never go back to the fast case | 
|  | object->RequireSlowElements(*dictionary); | 
|  | if (attrs != NONE) { | 
|  | JSObject::ApplyAttributesToDictionary(isolate, ReadOnlyRoots(isolate), | 
|  | dictionary, attrs); | 
|  | } | 
|  | } | 
|  |  | 
|  | return Just(true); | 
|  | } | 
|  |  | 
|  | Handle<Object> JSObject::FastPropertyAt(Handle<JSObject> object, | 
|  | Representation representation, | 
|  | FieldIndex index) { | 
|  | Isolate* isolate = object->GetIsolate(); | 
|  | if (object->IsUnboxedDoubleField(index)) { | 
|  | DCHECK(representation.IsDouble()); | 
|  | double value = object->RawFastDoublePropertyAt(index); | 
|  | return isolate->factory()->NewHeapNumber(value); | 
|  | } | 
|  | Handle<Object> raw_value(object->RawFastPropertyAt(index), isolate); | 
|  | return Object::WrapForRead(isolate, raw_value, representation); | 
|  | } | 
|  |  | 
|  | // TODO(cbruni/jkummerow): Consider moving this into elements.cc. | 
|  | bool JSObject::HasEnumerableElements() { | 
|  | // TODO(cbruni): cleanup | 
|  | JSObject object = *this; | 
|  | switch (object.GetElementsKind()) { | 
|  | case PACKED_SMI_ELEMENTS: | 
|  | case PACKED_ELEMENTS: | 
|  | case PACKED_FROZEN_ELEMENTS: | 
|  | case PACKED_SEALED_ELEMENTS: | 
|  | case PACKED_NONEXTENSIBLE_ELEMENTS: | 
|  | case PACKED_DOUBLE_ELEMENTS: { | 
|  | int length = object.IsJSArray() | 
|  | ? Smi::ToInt(JSArray::cast(object).length()) | 
|  | : object.elements().length(); | 
|  | return length > 0; | 
|  | } | 
|  | case HOLEY_SMI_ELEMENTS: | 
|  | case HOLEY_FROZEN_ELEMENTS: | 
|  | case HOLEY_SEALED_ELEMENTS: | 
|  | case HOLEY_NONEXTENSIBLE_ELEMENTS: | 
|  | case HOLEY_ELEMENTS: { | 
|  | FixedArray elements = FixedArray::cast(object.elements()); | 
|  | int length = object.IsJSArray() | 
|  | ? Smi::ToInt(JSArray::cast(object).length()) | 
|  | : elements.length(); | 
|  | Isolate* isolate = GetIsolate(); | 
|  | for (int i = 0; i < length; i++) { | 
|  | if (!elements.is_the_hole(isolate, i)) return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  | case HOLEY_DOUBLE_ELEMENTS: { | 
|  | int length = object.IsJSArray() | 
|  | ? Smi::ToInt(JSArray::cast(object).length()) | 
|  | : object.elements().length(); | 
|  | // Zero-length arrays would use the empty FixedArray... | 
|  | if (length == 0) return false; | 
|  | // ...so only cast to FixedDoubleArray otherwise. | 
|  | FixedDoubleArray elements = FixedDoubleArray::cast(object.elements()); | 
|  | for (int i = 0; i < length; i++) { | 
|  | if (!elements.is_the_hole(i)) return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  | #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) case TYPE##_ELEMENTS: | 
|  |  | 
|  | TYPED_ARRAYS(TYPED_ARRAY_CASE) | 
|  | #undef TYPED_ARRAY_CASE | 
|  | { | 
|  | size_t length = JSTypedArray::cast(object).length(); | 
|  | return length > 0; | 
|  | } | 
|  | case DICTIONARY_ELEMENTS: { | 
|  | NumberDictionary elements = NumberDictionary::cast(object.elements()); | 
|  | return elements.NumberOfEnumerableProperties() > 0; | 
|  | } | 
|  | case FAST_SLOPPY_ARGUMENTS_ELEMENTS: | 
|  | case SLOW_SLOPPY_ARGUMENTS_ELEMENTS: | 
|  | // We're approximating non-empty arguments objects here. | 
|  | return true; | 
|  | case FAST_STRING_WRAPPER_ELEMENTS: | 
|  | case SLOW_STRING_WRAPPER_ELEMENTS: | 
|  | if (String::cast(JSPrimitiveWrapper::cast(object).value()).length() > 0) { | 
|  | return true; | 
|  | } | 
|  | return object.elements().length() > 0; | 
|  | case NO_ELEMENTS: | 
|  | return false; | 
|  | } | 
|  | UNREACHABLE(); | 
|  | } | 
|  |  | 
|  | MaybeHandle<Object> JSObject::DefineAccessor(Handle<JSObject> object, | 
|  | Handle<Name> name, | 
|  | Handle<Object> getter, | 
|  | Handle<Object> setter, | 
|  | PropertyAttributes attributes) { | 
|  | Isolate* isolate = object->GetIsolate(); | 
|  |  | 
|  | LookupIterator::Key key(isolate, name); | 
|  | LookupIterator it(isolate, object, key, LookupIterator::OWN_SKIP_INTERCEPTOR); | 
|  | return DefineAccessor(&it, getter, setter, attributes); | 
|  | } | 
|  |  | 
|  | MaybeHandle<Object> JSObject::DefineAccessor(LookupIterator* it, | 
|  | Handle<Object> getter, | 
|  | Handle<Object> setter, | 
|  | PropertyAttributes attributes) { | 
|  | Isolate* isolate = it->isolate(); | 
|  |  | 
|  | it->UpdateProtector(); | 
|  |  | 
|  | if (it->state() == LookupIterator::ACCESS_CHECK) { | 
|  | if (!it->HasAccess()) { | 
|  | isolate->ReportFailedAccessCheck(it->GetHolder<JSObject>()); | 
|  | RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); | 
|  | return isolate->factory()->undefined_value(); | 
|  | } | 
|  | it->Next(); | 
|  | } | 
|  |  | 
|  | Handle<JSObject> object = Handle<JSObject>::cast(it->GetReceiver()); | 
|  | // Ignore accessors on typed arrays. | 
|  | if (it->IsElement() && object->HasTypedArrayElements()) { | 
|  | return it->factory()->undefined_value(); | 
|  | } | 
|  |  | 
|  | DCHECK(getter->IsCallable() || getter->IsUndefined(isolate) || | 
|  | getter->IsNull(isolate) || getter->IsFunctionTemplateInfo()); | 
|  | DCHECK(setter->IsCallable() || setter->IsUndefined(isolate) || | 
|  | setter->IsNull(isolate) || setter->IsFunctionTemplateInfo()); | 
|  | it->TransitionToAccessorProperty(getter, setter, attributes); | 
|  |  | 
|  | return isolate->factory()->undefined_value(); | 
|  | } | 
|  |  | 
|  | MaybeHandle<Object> JSObject::SetAccessor(Handle<JSObject> object, | 
|  | Handle<Name> name, | 
|  | Handle<AccessorInfo> info, | 
|  | PropertyAttributes attributes) { | 
|  | Isolate* isolate = object->GetIsolate(); | 
|  |  | 
|  | LookupIterator::Key key(isolate, name); | 
|  | LookupIterator it(isolate, object, key, LookupIterator::OWN_SKIP_INTERCEPTOR); | 
|  |  | 
|  | // Duplicate ACCESS_CHECK outside of GetPropertyAttributes for the case that | 
|  | // the FailedAccessCheckCallbackFunction doesn't throw an exception. | 
|  | // | 
|  | // TODO(verwaest): Force throw an exception if the callback doesn't, so we can | 
|  | // remove reliance on default return values. | 
|  | if (it.state() == LookupIterator::ACCESS_CHECK) { | 
|  | if (!it.HasAccess()) { | 
|  | isolate->ReportFailedAccessCheck(object); | 
|  | RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); | 
|  | return it.factory()->undefined_value(); | 
|  | } | 
|  | it.Next(); | 
|  | } | 
|  |  | 
|  | // Ignore accessors on typed arrays. | 
|  | if (it.IsElement() && object->HasTypedArrayElements()) { | 
|  | return it.factory()->undefined_value(); | 
|  | } | 
|  |  | 
|  | CHECK(GetPropertyAttributes(&it).IsJust()); | 
|  |  | 
|  | // ES5 forbids turning a property into an accessor if it's not | 
|  | // configurable. See 8.6.1 (Table 5). | 
|  | if (it.IsFound() && !it.IsConfigurable()) { | 
|  | return it.factory()->undefined_value(); | 
|  | } | 
|  |  | 
|  | it.TransitionToAccessorPair(info, attributes); | 
|  |  | 
|  | return object; | 
|  | } | 
|  |  | 
|  | Object JSObject::SlowReverseLookup(Object value) { | 
|  | if (HasFastProperties()) { | 
|  | DescriptorArray descs = map().instance_descriptors(kRelaxedLoad); | 
|  | bool value_is_number = value.IsNumber(); | 
|  | for (InternalIndex i : map().IterateOwnDescriptors()) { | 
|  | PropertyDetails details = descs.GetDetails(i); | 
|  | if (details.location() == kField) { | 
|  | DCHECK_EQ(kData, details.kind()); | 
|  | FieldIndex field_index = FieldIndex::ForDescriptor(map(), i); | 
|  | if (IsUnboxedDoubleField(field_index)) { | 
|  | if (value_is_number) { | 
|  | double property = RawFastDoublePropertyAt(field_index); | 
|  | if (property == value.Number()) { | 
|  | return descs.GetKey(i); | 
|  | } | 
|  | } | 
|  | } else { | 
|  | Object property = RawFastPropertyAt(field_index); | 
|  | if (field_index.is_double()) { | 
|  | DCHECK(property.IsHeapNumber()); | 
|  | if (value_is_number && property.Number() == value.Number()) { | 
|  | return descs.GetKey(i); | 
|  | } | 
|  | } else if (property == value) { | 
|  | return descs.GetKey(i); | 
|  | } | 
|  | } | 
|  | } else { | 
|  | DCHECK_EQ(kDescriptor, details.location()); | 
|  | if (details.kind() == kData) { | 
|  | if (descs.GetStrongValue(i) == value) { | 
|  | return descs.GetKey(i); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | return GetReadOnlyRoots().undefined_value(); | 
|  | } else if (IsJSGlobalObject()) { | 
|  | return JSGlobalObject::cast(*this).global_dictionary().SlowReverseLookup( | 
|  | value); | 
|  | } else if (V8_DICT_MODE_PROTOTYPES_BOOL) { | 
|  | return property_dictionary_ordered().SlowReverseLookup(GetIsolate(), value); | 
|  | } else { | 
|  | return property_dictionary().SlowReverseLookup(value); | 
|  | } | 
|  | } | 
|  |  | 
|  | void JSObject::PrototypeRegistryCompactionCallback(HeapObject value, | 
|  | int old_index, | 
|  | int new_index) { | 
|  | DCHECK(value.IsMap() && Map::cast(value).is_prototype_map()); | 
|  | Map map = Map::cast(value); | 
|  | DCHECK(map.prototype_info().IsPrototypeInfo()); | 
|  | PrototypeInfo proto_info = PrototypeInfo::cast(map.prototype_info()); | 
|  | DCHECK_EQ(old_index, proto_info.registry_slot()); | 
|  | proto_info.set_registry_slot(new_index); | 
|  | } | 
|  |  | 
|  | // static | 
|  | void JSObject::MakePrototypesFast(Handle<Object> receiver, | 
|  | WhereToStart where_to_start, | 
|  | Isolate* isolate) { | 
|  | if (!receiver->IsJSReceiver()) return; | 
|  | for (PrototypeIterator iter(isolate, Handle<JSReceiver>::cast(receiver), | 
|  | where_to_start); | 
|  | !iter.IsAtEnd(); iter.Advance()) { | 
|  | Handle<Object> current = PrototypeIterator::GetCurrent(iter); | 
|  | if (!current->IsJSObject()) return; | 
|  | Handle<JSObject> current_obj = Handle<JSObject>::cast(current); | 
|  | Map current_map = current_obj->map(); | 
|  | if (current_map.is_prototype_map()) { | 
|  | // If the map is already marked as should be fast, we're done. Its | 
|  | // prototypes will have been marked already as well. | 
|  | if (current_map.should_be_fast_prototype_map()) return; | 
|  | Handle<Map> map(current_map, isolate); | 
|  | Map::SetShouldBeFastPrototypeMap(map, true, isolate); | 
|  | JSObject::OptimizeAsPrototype(current_obj); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | static bool PrototypeBenefitsFromNormalization(Handle<JSObject> object) { | 
|  | DisallowHeapAllocation no_gc; | 
|  | if (!object->HasFastProperties()) return false; | 
|  | if (object->IsJSGlobalProxy()) return false; | 
|  | if (object->GetIsolate()->bootstrapper()->IsActive()) return false; | 
|  | return !object->map().is_prototype_map() || | 
|  | !object->map().should_be_fast_prototype_map(); | 
|  | } | 
|  |  | 
|  | // static | 
|  | void JSObject::OptimizeAsPrototype(Handle<JSObject> object, | 
|  | bool enable_setup_mode) { | 
|  | Isolate* isolate = object->GetIsolate(); | 
|  | if (object->IsJSGlobalObject()) return; | 
|  | if (enable_setup_mode && PrototypeBenefitsFromNormalization(object)) { | 
|  | // First normalize to ensure all JSFunctions are DATA_CONSTANT. | 
|  | JSObject::NormalizeProperties(isolate, object, KEEP_INOBJECT_PROPERTIES, 0, | 
|  | "NormalizeAsPrototype"); | 
|  | } | 
|  | if (object->map().is_prototype_map()) { | 
|  | if (object->map().should_be_fast_prototype_map() && | 
|  | !object->HasFastProperties()) { | 
|  | JSObject::MigrateSlowToFast(object, 0, "OptimizeAsPrototype"); | 
|  | } | 
|  | } else { | 
|  | Handle<Map> new_map = | 
|  | Map::Copy(isolate, handle(object->map(), isolate), "CopyAsPrototype"); | 
|  | JSObject::MigrateToMap(isolate, object, new_map); | 
|  | object->map().set_is_prototype_map(true); | 
|  |  | 
|  | // Replace the pointer to the exact constructor with the Object function | 
|  | // from the same context if undetectable from JS. This is to avoid keeping | 
|  | // memory alive unnecessarily. | 
|  | Object maybe_constructor = object->map().GetConstructor(); | 
|  | if (maybe_constructor.IsJSFunction()) { | 
|  | JSFunction constructor = JSFunction::cast(maybe_constructor); | 
|  | if (!constructor.shared().IsApiFunction()) { | 
|  | Context context = constructor.context().native_context(); | 
|  | JSFunction object_function = context.object_function(); | 
|  | object->map().SetConstructor(object_function); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // static | 
|  | void JSObject::ReoptimizeIfPrototype(Handle<JSObject> object) { | 
|  | if (!object->map().is_prototype_map()) return; | 
|  | if (!object->map().should_be_fast_prototype_map()) return; | 
|  | OptimizeAsPrototype(object); | 
|  | } | 
|  |  | 
|  | // static | 
|  | void JSObject::LazyRegisterPrototypeUser(Handle<Map> user, Isolate* isolate) { | 
|  | // Contract: In line with InvalidatePrototypeChains()'s requirements, | 
|  | // leaf maps don't need to register as users, only prototypes do. | 
|  | DCHECK(user->is_prototype_map()); | 
|  |  | 
|  | Handle<Map> current_user = user; | 
|  | Handle<PrototypeInfo> current_user_info = | 
|  | Map::GetOrCreatePrototypeInfo(user, isolate); | 
|  | for (PrototypeIterator iter(isolate, user); !iter.IsAtEnd(); iter.Advance()) { | 
|  | // Walk up the prototype chain as far as links haven't been registered yet. | 
|  | if (current_user_info->registry_slot() != PrototypeInfo::UNREGISTERED) { | 
|  | break; | 
|  | } | 
|  | Handle<Object> maybe_proto = PrototypeIterator::GetCurrent(iter); | 
|  | // Proxies on the prototype chain are not supported. They make it | 
|  | // impossible to make any assumptions about the prototype chain anyway. | 
|  | if (maybe_proto->IsJSProxy()) return; | 
|  | Handle<JSObject> proto = Handle<JSObject>::cast(maybe_proto); | 
|  | Handle<PrototypeInfo> proto_info = | 
|  | Map::GetOrCreatePrototypeInfo(proto, isolate); | 
|  | Handle<Object> maybe_registry(proto_info->prototype_users(), isolate); | 
|  | Handle<WeakArrayList> registry = | 
|  | maybe_registry->IsSmi() | 
|  | ? handle(ReadOnlyRoots(isolate->heap()).empty_weak_array_list(), | 
|  | isolate) | 
|  | : Handle<WeakArrayList>::cast(maybe_registry); | 
|  | int slot = 0; | 
|  | Handle<WeakArrayList> new_array = | 
|  | PrototypeUsers::Add(isolate, registry, current_user, &slot); | 
|  | current_user_info->set_registry_slot(slot); | 
|  | if (!maybe_registry.is_identical_to(new_array)) { | 
|  | proto_info->set_prototype_users(*new_array); | 
|  | } | 
|  | if (FLAG_trace_prototype_users) { | 
|  | PrintF("Registering %p as a user of prototype %p (map=%p).\n", | 
|  | reinterpret_cast<void*>(current_user->ptr()), | 
|  | reinterpret_cast<void*>(proto->ptr()), | 
|  | reinterpret_cast<void*>(proto->map().ptr())); | 
|  | } | 
|  |  | 
|  | current_user = handle(proto->map(), isolate); | 
|  | current_user_info = proto_info; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Can be called regardless of whether |user| was actually registered with | 
|  | // |prototype|. Returns true when there was a registration. | 
|  | // static | 
|  | bool JSObject::UnregisterPrototypeUser(Handle<Map> user, Isolate* isolate) { | 
|  | DCHECK(user->is_prototype_map()); | 
|  | // If it doesn't have a PrototypeInfo, it was never registered. | 
|  | if (!user->prototype_info().IsPrototypeInfo()) return false; | 
|  | // If it had no prototype before, see if it had users that might expect | 
|  | // registration. | 
|  | if (!user->prototype().IsJSObject()) { | 
|  | Object users = | 
|  | PrototypeInfo::cast(user->prototype_info()).prototype_users(); | 
|  | return users.IsWeakArrayList(); | 
|  | } | 
|  | Handle<JSObject> prototype(JSObject::cast(user->prototype()), isolate); | 
|  | Handle<PrototypeInfo> user_info = | 
|  | Map::GetOrCreatePrototypeInfo(user, isolate); | 
|  | int slot = user_info->registry_slot(); | 
|  | if (slot == PrototypeInfo::UNREGISTERED) return false; | 
|  | DCHECK(prototype->map().is_prototype_map()); | 
|  | Object maybe_proto_info = prototype->map().prototype_info(); | 
|  | // User knows its registry slot, prototype info and user registry must exist. | 
|  | DCHECK(maybe_proto_info.IsPrototypeInfo()); | 
|  | Handle<PrototypeInfo> proto_info(PrototypeInfo::cast(maybe_proto_info), | 
|  | isolate); | 
|  | Handle<WeakArrayList> prototype_users( | 
|  | WeakArrayList::cast(proto_info->prototype_users()), isolate); | 
|  | DCHECK_EQ(prototype_users->Get(slot), HeapObjectReference::Weak(*user)); | 
|  | PrototypeUsers::MarkSlotEmpty(*prototype_users, slot); | 
|  | if (FLAG_trace_prototype_users) { | 
|  | PrintF("Unregistering %p as a user of prototype %p.\n", | 
|  | reinterpret_cast<void*>(user->ptr()), | 
|  | reinterpret_cast<void*>(prototype->ptr())); | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // This function must be kept in sync with | 
|  | // AccessorAssembler::InvalidateValidityCellIfPrototype() which does pre-checks | 
|  | // before jumping here. | 
|  | void InvalidateOnePrototypeValidityCellInternal(Map map) { | 
|  | DCHECK(map.is_prototype_map()); | 
|  | if (FLAG_trace_prototype_users) { | 
|  | PrintF("Invalidating prototype map %p 's cell\n", | 
|  | reinterpret_cast<void*>(map.ptr())); | 
|  | } | 
|  | Object maybe_cell = map.prototype_validity_cell(); | 
|  | if (maybe_cell.IsCell()) { | 
|  | // Just set the value; the cell will be replaced lazily. | 
|  | Cell cell = Cell::cast(maybe_cell); | 
|  | cell.set_value(Smi::FromInt(Map::kPrototypeChainInvalid)); | 
|  | } | 
|  | Object maybe_prototype_info = map.prototype_info(); | 
|  | if (maybe_prototype_info.IsPrototypeInfo()) { | 
|  | PrototypeInfo prototype_info = PrototypeInfo::cast(maybe_prototype_info); | 
|  | prototype_info.set_prototype_chain_enum_cache(Object()); | 
|  | } | 
|  | } | 
|  |  | 
|  | void InvalidatePrototypeChainsInternal(Map map) { | 
|  | // We handle linear prototype chains by looping, and multiple children | 
|  | // by recursion, in order to reduce the likelihood of running into stack | 
|  | // overflows. So, conceptually, the outer loop iterates the depth of the | 
|  | // prototype tree, and the inner loop iterates the breadth of a node. | 
|  | Map next_map; | 
|  | for (; !map.is_null(); map = next_map, next_map = Map()) { | 
|  | InvalidateOnePrototypeValidityCellInternal(map); | 
|  |  | 
|  | Object maybe_proto_info = map.prototype_info(); | 
|  | if (!maybe_proto_info.IsPrototypeInfo()) return; | 
|  | PrototypeInfo proto_info = PrototypeInfo::cast(maybe_proto_info); | 
|  | if (!proto_info.prototype_users().IsWeakArrayList()) { | 
|  | return; | 
|  | } | 
|  | WeakArrayList prototype_users = | 
|  | WeakArrayList::cast(proto_info.prototype_users()); | 
|  | // For now, only maps register themselves as users. | 
|  | for (int i = PrototypeUsers::kFirstIndex; i < prototype_users.length(); | 
|  | ++i) { | 
|  | HeapObject heap_object; | 
|  | if (prototype_users.Get(i)->GetHeapObjectIfWeak(&heap_object) && | 
|  | heap_object.IsMap()) { | 
|  | // Walk the prototype chain (backwards, towards leaf objects) if | 
|  | // necessary. | 
|  | if (next_map.is_null()) { | 
|  | next_map = Map::cast(heap_object); | 
|  | } else { | 
|  | InvalidatePrototypeChainsInternal(Map::cast(heap_object)); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | // static | 
|  | Map JSObject::InvalidatePrototypeChains(Map map) { | 
|  | DisallowHeapAllocation no_gc; | 
|  | InvalidatePrototypeChainsInternal(map); | 
|  | return map; | 
|  | } | 
|  |  | 
|  | // We also invalidate global objects validity cell when a new lexical | 
|  | // environment variable is added. This is necessary to ensure that | 
|  | // Load/StoreGlobalIC handlers that load/store from global object's prototype | 
|  | // get properly invalidated. | 
|  | // Note, that the normal Load/StoreICs that load/store through the global object | 
|  | // in the prototype chain are not affected by appearance of a new lexical | 
|  | // variable and therefore we don't propagate invalidation down. | 
|  | // static | 
|  | void JSObject::InvalidatePrototypeValidityCell(JSGlobalObject global) { | 
|  | DisallowHeapAllocation no_gc; | 
|  | InvalidateOnePrototypeValidityCellInternal(global.map()); | 
|  | } | 
|  |  | 
|  | Maybe<bool> JSObject::SetPrototype(Handle<JSObject> object, | 
|  | Handle<Object> value, bool from_javascript, | 
|  | ShouldThrow should_throw) { | 
|  | Isolate* isolate = object->GetIsolate(); | 
|  |  | 
|  | #ifdef DEBUG | 
|  | int size = object->Size(); | 
|  | #endif | 
|  |  | 
|  | if (from_javascript) { | 
|  | if (object->IsAccessCheckNeeded() && | 
|  | !isolate->MayAccess(handle(isolate->context(), isolate), object)) { | 
|  | isolate->ReportFailedAccessCheck(object); | 
|  | RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>()); | 
|  | RETURN_FAILURE(isolate, should_throw, | 
|  | NewTypeError(MessageTemplate::kNoAccess)); | 
|  | } | 
|  | } else { | 
|  | DCHECK(!object->IsAccessCheckNeeded()); | 
|  | } | 
|  |  | 
|  | // Silently ignore the change if value is not a JSObject or null. | 
|  | // SpiderMonkey behaves this way. | 
|  | if (!value->IsJSReceiver() && !value->IsNull(isolate)) return Just(true); | 
|  |  | 
|  | bool all_extensible = object->map().is_extensible(); | 
|  | Handle<JSObject> real_receiver = object; | 
|  | if (from_javascript) { | 
|  | // Find the first object in the chain whose prototype object is not | 
|  | // hidden. | 
|  | PrototypeIterator iter(isolate, real_receiver, kStartAtPrototype, | 
|  | PrototypeIterator::END_AT_NON_HIDDEN); | 
|  | while (!iter.IsAtEnd()) { | 
|  | // Casting to JSObject is fine because hidden prototypes are never | 
|  | // JSProxies. | 
|  | real_receiver = PrototypeIterator::GetCurrent<JSObject>(iter); | 
|  | iter.Advance(); | 
|  | all_extensible = all_extensible && real_receiver->map().is_extensible(); | 
|  | } | 
|  | } | 
|  | Handle<Map> map(real_receiver->map(), isolate); | 
|  |  | 
|  | // Nothing to do if prototype is already set. | 
|  | if (map->prototype() == *value) return Just(true); | 
|  |  | 
|  | bool immutable_proto = map->is_immutable_proto(); | 
|  | if (immutable_proto) { | 
|  | RETURN_FAILURE( | 
|  | isolate, should_throw, | 
|  | NewTypeError(MessageTemplate::kImmutablePrototypeSet, object)); | 
|  | } | 
|  |  | 
|  | // From 6.1.7.3 Invariants of the Essential Internal Methods | 
|  | // | 
|  | // [[SetPrototypeOf]] ( V ) | 
|  | // * ... | 
|  | // * If target is non-extensible, [[SetPrototypeOf]] must return false, | 
|  | //   unless V is the SameValue as the target's observed [[GetPrototypeOf]] | 
|  | //   value. | 
|  | if (!all_extensible) { | 
|  | RETURN_FAILURE(isolate, should_throw, | 
|  | NewTypeError(MessageTemplate::kNonExtensibleProto, object)); | 
|  | } | 
|  |  | 
|  | // Before we can set the prototype we need to be sure prototype cycles are | 
|  | // prevented.  It is sufficient to validate that the receiver is not in the | 
|  | // new prototype chain. | 
|  | if (value->IsJSReceiver()) { | 
|  | for (PrototypeIterator iter(isolate, JSReceiver::cast(*value), | 
|  | kStartAtReceiver); | 
|  | !iter.IsAtEnd(); iter.Advance()) { | 
|  | if (iter.GetCurrent<JSReceiver>() == *object) { | 
|  | // Cycle detected. | 
|  | RETURN_FAILURE(isolate, should_throw, | 
|  | NewTypeError(MessageTemplate::kCyclicProto)); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Set the new prototype of the object. | 
|  |  | 
|  | isolate->UpdateNoElementsProtectorOnSetPrototype(real_receiver); | 
|  |  | 
|  | Handle<Map> new_map = | 
|  | Map::TransitionToPrototype(isolate, map, Handle<HeapObject>::cast(value)); | 
|  | DCHECK(new_map->prototype() == *value); | 
|  | JSObject::MigrateToMap(isolate, real_receiver, new_map); | 
|  |  | 
|  | DCHECK(size == object->Size()); | 
|  | return Just(true); | 
|  | } | 
|  |  | 
|  | // static | 
|  | void JSObject::SetImmutableProto(Handle<JSObject> object) { | 
|  | Handle<Map> map(object->map(), object->GetIsolate()); | 
|  |  | 
|  | // Nothing to do if prototype is already set. | 
|  | if (map->is_immutable_proto()) return; | 
|  |  | 
|  | Handle<Map> new_map = | 
|  | Map::TransitionToImmutableProto(object->GetIsolate(), map); | 
|  | object->synchronized_set_map(*new_map); | 
|  | } | 
|  |  | 
|  | void JSObject::EnsureCanContainElements(Handle<JSObject> object, | 
|  | JavaScriptArguments* args, | 
|  | uint32_t arg_count, | 
|  | EnsureElementsMode mode) { | 
|  | return EnsureCanContainElements(object, args->first_slot(), arg_count, mode); | 
|  | } | 
|  |  | 
|  | void JSObject::ValidateElements(JSObject object) { | 
|  | #ifdef ENABLE_SLOW_DCHECKS | 
|  | if (FLAG_enable_slow_asserts) { | 
|  | object.GetElementsAccessor()->Validate(object); | 
|  | } | 
|  | #endif | 
|  | } | 
|  |  | 
|  | bool JSObject::WouldConvertToSlowElements(uint32_t index) { | 
|  | if (!HasFastElements()) return false; | 
|  | uint32_t capacity = static_cast<uint32_t>(elements().length()); | 
|  | uint32_t new_capacity; | 
|  | return ShouldConvertToSlowElements(*this, capacity, index, &new_capacity); | 
|  | } | 
|  |  | 
|  | static bool ShouldConvertToFastElements(JSObject object, | 
|  | NumberDictionary dictionary, | 
|  | uint32_t index, | 
|  | uint32_t* new_capacity) { | 
|  | // If properties with non-standard attributes or accessors were added, we | 
|  | // cannot go back to fast elements. | 
|  | if (dictionary.requires_slow_elements()) return false; | 
|  |  | 
|  | // Adding a property with this index will require slow elements. | 
|  | if (index >= static_cast<uint32_t>(Smi::kMaxValue)) return false; | 
|  |  | 
|  | if (object.IsJSArray()) { | 
|  | Object length = JSArray::cast(object).length(); | 
|  | if (!length.IsSmi()) return false; | 
|  | *new_capacity = static_cast<uint32_t>(Smi::ToInt(length)); | 
|  | } else if (object.IsJSArgumentsObject()) { | 
|  | return false; | 
|  | } else { | 
|  | *new_capacity = dictionary.max_number_key() + 1; | 
|  | } | 
|  | *new_capacity = std::max(index + 1, *new_capacity); | 
|  |  | 
|  | uint32_t dictionary_size = static_cast<uint32_t>(dictionary.Capacity()) * | 
|  | NumberDictionary::kEntrySize; | 
|  |  | 
|  | // Turn fast if the dictionary only saves 50% space. | 
|  | return 2 * dictionary_size >= *new_capacity; | 
|  | } | 
|  |  | 
|  | static ElementsKind BestFittingFastElementsKind(JSObject object) { | 
|  | if (!object.map().CanHaveFastTransitionableElementsKind()) { | 
|  | return HOLEY_ELEMENTS; | 
|  | } | 
|  | if (object.HasSloppyArgumentsElements()) { | 
|  | return FAST_SLOPPY_ARGUMENTS_ELEMENTS; | 
|  | } | 
|  | if (object.HasStringWrapperElements()) { | 
|  | return FAST_STRING_WRAPPER_ELEMENTS; | 
|  | } | 
|  | DCHECK(object.HasDictionaryElements()); | 
|  | NumberDictionary dictionary = object.element_dictionary(); | 
|  | ElementsKind kind = HOLEY_SMI_ELEMENTS; | 
|  | for (InternalIndex i : dictionary.IterateEntries()) { | 
|  | Object key = dictionary.KeyAt(i); | 
|  | if (key.IsNumber()) { | 
|  | Object value = dictionary.ValueAt(i); | 
|  | if (!value.IsNumber()) return HOLEY_ELEMENTS; | 
|  | if (!value.IsSmi()) { | 
|  | if (!FLAG_unbox_double_arrays) return HOLEY_ELEMENTS; | 
|  | kind = HOLEY_DOUBLE_ELEMENTS; | 
|  | } | 
|  | } | 
|  | } | 
|  | return kind; | 
|  | } | 
|  |  | 
|  | // static | 
|  | void JSObject::AddDataElement(Handle<JSObject> object, uint32_t index, | 
|  | Handle<Object> value, | 
|  | PropertyAttributes attributes) { | 
|  | Isolate* isolate = object->GetIsolate(); | 
|  |  | 
|  | DCHECK(object->map(isolate).is_extensible()); | 
|  |  | 
|  | uint32_t old_length = 0; | 
|  | uint32_t new_capacity = 0; | 
|  |  | 
|  | if (object->IsJSArray(isolate)) { | 
|  | CHECK(JSArray::cast(*object).length().ToArrayLength(&old_length)); | 
|  | } | 
|  |  | 
|  | ElementsKind kind = object->GetElementsKind(isolate); | 
|  | FixedArrayBase elements = object->elements(isolate); | 
|  | ElementsKind dictionary_kind = DICTIONARY_ELEMENTS; | 
|  | if (IsSloppyArgumentsElementsKind(kind)) { | 
|  | elements = SloppyArgumentsElements::cast(elements).arguments(isolate); | 
|  | dictionary_kind = SLOW_SLOPPY_ARGUMENTS_ELEMENTS; | 
|  | } else if (IsStringWrapperElementsKind(kind)) { | 
|  | dictionary_kind = SLOW_STRING_WRAPPER_ELEMENTS; | 
|  | } | 
|  |  | 
|  | if (attributes != NONE) { | 
|  | kind = dictionary_kind; | 
|  | } else if (elements.IsNumberDictionary(isolate)) { | 
|  | kind = ShouldConvertToFastElements( | 
|  | *object, NumberDictionary::cast(elements), index, &new_capacity) | 
|  | ? BestFittingFastElementsKind(*object) | 
|  | : dictionary_kind; | 
|  | } else if (ShouldConvertToSlowElements( | 
|  | *object, static_cast<uint32_t>(elements.length()), index, | 
|  | &new_capacity)) { | 
|  | kind = dictionary_kind; | 
|  | } | 
|  |  | 
|  | ElementsKind to = value->OptimalElementsKind(isolate); | 
|  | if (IsHoleyElementsKind(kind) || !object->IsJSArray(isolate) || | 
|  | index > old_length) { | 
|  | to = GetHoleyElementsKind(to); | 
|  | kind = GetHoleyElementsKind(kind); | 
|  | } | 
|  | to = GetMoreGeneralElementsKind(kind, to); | 
|  | ElementsAccessor* accessor = ElementsAccessor::ForKind(to); | 
|  | accessor->Add(object, index, value, attributes, new_capacity); | 
|  |  | 
|  | if (object->IsJSArray(isolate) && index >= old_length) { | 
|  | Handle<Object> new_length = | 
|  | isolate->factory()->NewNumberFromUint(index + 1); | 
|  | JSArray::cast(*object).set_length(*new_length); | 
|  | } | 
|  | } | 
|  |  | 
|  | template <AllocationSiteUpdateMode update_or_check> | 
|  | bool JSObject::UpdateAllocationSite(Handle<JSObject> object, | 
|  | ElementsKind to_kind) { | 
|  | if (!object->IsJSArray()) return false; | 
|  |  | 
|  | if (!Heap::InYoungGeneration(*object)) return false; | 
|  |  | 
|  | if (Heap::IsLargeObject(*object)) return false; | 
|  |  | 
|  | Handle<AllocationSite> site; | 
|  | { | 
|  | DisallowHeapAllocation no_allocation; | 
|  |  | 
|  | Heap* heap = object->GetHeap(); | 
|  | AllocationMemento memento = | 
|  | heap->FindAllocationMemento<Heap::kForRuntime>(object->map(), *object); | 
|  | if (memento.is_null()) return false; | 
|  |  | 
|  | // Walk through to the Allocation Site | 
|  | site = handle(memento.GetAllocationSite(), heap->isolate()); | 
|  | } | 
|  | return AllocationSite::DigestTransitionFeedback<update_or_check>(site, | 
|  | to_kind); | 
|  | } | 
|  |  | 
|  | template bool | 
|  | JSObject::UpdateAllocationSite<AllocationSiteUpdateMode::kCheckOnly>( | 
|  | Handle<JSObject> object, ElementsKind to_kind); | 
|  |  | 
|  | template bool JSObject::UpdateAllocationSite<AllocationSiteUpdateMode::kUpdate>( | 
|  | Handle<JSObject> object, ElementsKind to_kind); | 
|  |  | 
|  | void JSObject::TransitionElementsKind(Handle<JSObject> object, | 
|  | ElementsKind to_kind) { | 
|  | ElementsKind from_kind = object->GetElementsKind(); | 
|  |  | 
|  | if (IsHoleyElementsKind(from_kind)) { | 
|  | to_kind = GetHoleyElementsKind(to_kind); | 
|  | } | 
|  |  | 
|  | if (from_kind == to_kind) return; | 
|  |  | 
|  | // This method should never be called for any other case. | 
|  | DCHECK(IsFastElementsKind(from_kind) || | 
|  | IsNonextensibleElementsKind(from_kind)); | 
|  | DCHECK(IsFastElementsKind(to_kind) || IsNonextensibleElementsKind(to_kind)); | 
|  | DCHECK_NE(TERMINAL_FAST_ELEMENTS_KIND, from_kind); | 
|  |  | 
|  | UpdateAllocationSite(object, to_kind); | 
|  | Isolate* isolate = object->GetIsolate(); | 
|  | if (object->elements() == ReadOnlyRoots(isolate).empty_fixed_array() || | 
|  | IsDoubleElementsKind(from_kind) == IsDoubleElementsKind(to_kind)) { | 
|  | // No change is needed to the elements() buffer, the transition | 
|  | // only requires a map change. | 
|  | Handle<Map> new_map = GetElementsTransitionMap(object, to_kind); | 
|  | JSObject::MigrateToMap(isolate, object, new_map); | 
|  | if (FLAG_trace_elements_transitions) { | 
|  | Handle<FixedArrayBase> elms(object->elements(), isolate); | 
|  | PrintElementsTransition(stdout, object, from_kind, elms, to_kind, elms); | 
|  | } | 
|  | } else { | 
|  | DCHECK((IsSmiElementsKind(from_kind) && IsDoubleElementsKind(to_kind)) || | 
|  | (IsDoubleElementsKind(from_kind) && IsObjectElementsKind(to_kind))); | 
|  | uint32_t c = static_cast<uint32_t>(object->elements().length()); | 
|  | ElementsAccessor::ForKind(to_kind)->GrowCapacityAndConvert(object, c); | 
|  | } | 
|  | } | 
|  |  | 
|  | template <typename BackingStore> | 
|  | static int HoleyElementsUsage(JSObject object, BackingStore store) { | 
|  | Isolate* isolate = object.GetIsolate(); | 
|  | int limit = object.IsJSArray() ? Smi::ToInt(JSArray::cast(object).length()) | 
|  | : store.length(); | 
|  | int used = 0; | 
|  | for (int i = 0; i < limit; ++i) { | 
|  | if (!store.is_the_hole(isolate, i)) ++used; | 
|  | } | 
|  | return used; | 
|  | } | 
|  |  | 
|  | int JSObject::GetFastElementsUsage() { | 
|  | FixedArrayBase store = elements(); | 
|  | switch (GetElementsKind()) { | 
|  | case PACKED_SMI_ELEMENTS: | 
|  | case PACKED_DOUBLE_ELEMENTS: | 
|  | case PACKED_ELEMENTS: | 
|  | case PACKED_FROZEN_ELEMENTS: | 
|  | case PACKED_SEALED_ELEMENTS: | 
|  | case PACKED_NONEXTENSIBLE_ELEMENTS: | 
|  | return IsJSArray() ? Smi::ToInt(JSArray::cast(*this).length()) | 
|  | : store.length(); | 
|  | case FAST_SLOPPY_ARGUMENTS_ELEMENTS: | 
|  | store = SloppyArgumentsElements::cast(store).arguments(); | 
|  | V8_FALLTHROUGH; | 
|  | case HOLEY_SMI_ELEMENTS: | 
|  | case HOLEY_ELEMENTS: | 
|  | case HOLEY_FROZEN_ELEMENTS: | 
|  | case HOLEY_SEALED_ELEMENTS: | 
|  | case HOLEY_NONEXTENSIBLE_ELEMENTS: | 
|  | case FAST_STRING_WRAPPER_ELEMENTS: | 
|  | return HoleyElementsUsage(*this, FixedArray::cast(store)); | 
|  | case HOLEY_DOUBLE_ELEMENTS: | 
|  | if (elements().length() == 0) return 0; | 
|  | return HoleyElementsUsage(*this, FixedDoubleArray::cast(store)); | 
|  |  | 
|  | case SLOW_SLOPPY_ARGUMENTS_ELEMENTS: | 
|  | case SLOW_STRING_WRAPPER_ELEMENTS: | 
|  | case DICTIONARY_ELEMENTS: | 
|  | case NO_ELEMENTS: | 
|  | #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) case TYPE##_ELEMENTS: | 
|  |  | 
|  | TYPED_ARRAYS(TYPED_ARRAY_CASE) | 
|  | #undef TYPED_ARRAY_CASE | 
|  | UNREACHABLE(); | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | MaybeHandle<Object> JSObject::GetPropertyWithInterceptor(LookupIterator* it, | 
|  | bool* done) { | 
|  | DCHECK_EQ(LookupIterator::INTERCEPTOR, it->state()); | 
|  | return GetPropertyWithInterceptorInternal(it, it->GetInterceptor(), done); | 
|  | } | 
|  |  | 
|  | Maybe<bool> JSObject::HasRealNamedProperty(Handle<JSObject> object, | 
|  | Handle<Name> name) { | 
|  | Isolate* isolate = object->GetIsolate(); | 
|  | LookupIterator::Key key(isolate, name); | 
|  | LookupIterator it(isolate, object, key, LookupIterator::OWN_SKIP_INTERCEPTOR); | 
|  | return HasProperty(&it); | 
|  | } | 
|  |  | 
|  | Maybe<bool> JSObject::HasRealElementProperty(Handle<JSObject> object, | 
|  | uint32_t index) { | 
|  | Isolate* isolate = object->GetIsolate(); | 
|  | LookupIterator it(isolate, object, index, object, | 
|  | LookupIterator::OWN_SKIP_INTERCEPTOR); | 
|  | return HasProperty(&it); | 
|  | } | 
|  |  | 
|  | Maybe<bool> JSObject::HasRealNamedCallbackProperty(Handle<JSObject> object, | 
|  | Handle<Name> name) { | 
|  | Isolate* isolate = object->GetIsolate(); | 
|  | LookupIterator::Key key(isolate, name); | 
|  | LookupIterator it(isolate, object, key, LookupIterator::OWN_SKIP_INTERCEPTOR); | 
|  | Maybe<PropertyAttributes> maybe_result = GetPropertyAttributes(&it); | 
|  | return maybe_result.IsJust() ? Just(it.state() == LookupIterator::ACCESSOR) | 
|  | : Nothing<bool>(); | 
|  | } | 
|  |  | 
|  | bool JSObject::IsApiWrapper() { | 
|  | // These object types can carry information relevant for embedders. The | 
|  | // *_API_* types are generated through templates which can have embedder | 
|  | // fields. The other types have their embedder fields added at compile time. | 
|  | auto instance_type = map().instance_type(); | 
|  | return instance_type == JS_API_OBJECT_TYPE || | 
|  | instance_type == JS_ARRAY_BUFFER_TYPE || | 
|  | instance_type == JS_DATA_VIEW_TYPE || | 
|  | instance_type == JS_GLOBAL_OBJECT_TYPE || | 
|  | instance_type == JS_GLOBAL_PROXY_TYPE || | 
|  | instance_type == JS_SPECIAL_API_OBJECT_TYPE || | 
|  | instance_type == JS_TYPED_ARRAY_TYPE; | 
|  | } | 
|  |  | 
|  | bool JSObject::IsDroppableApiWrapper() { | 
|  | auto instance_type = map().instance_type(); | 
|  | return instance_type == JS_API_OBJECT_TYPE || | 
|  | instance_type == JS_SPECIAL_API_OBJECT_TYPE; | 
|  | } | 
|  |  | 
|  | bool JSGlobalProxy::IsDetached() const { | 
|  | return native_context().IsNull(GetIsolate()); | 
|  | } | 
|  |  | 
|  | void JSGlobalObject::InvalidatePropertyCell(Handle<JSGlobalObject> global, | 
|  | Handle<Name> name) { | 
|  | // Regardless of whether the property is there or not invalidate | 
|  | // Load/StoreGlobalICs that load/store through global object's prototype. | 
|  | JSObject::InvalidatePrototypeValidityCell(*global); | 
|  |  | 
|  | DCHECK(!global->HasFastProperties()); | 
|  | auto dictionary = handle(global->global_dictionary(), global->GetIsolate()); | 
|  | InternalIndex entry = dictionary->FindEntry(global->GetIsolate(), name); | 
|  | if (entry.is_not_found()) return; | 
|  | PropertyCell::InvalidateAndReplaceEntry(global->GetIsolate(), dictionary, | 
|  | entry); | 
|  | } | 
|  |  | 
|  | // static | 
|  | MaybeHandle<JSDate> JSDate::New(Handle<JSFunction> constructor, | 
|  | Handle<JSReceiver> new_target, double tv) { | 
|  | Isolate* const isolate = constructor->GetIsolate(); | 
|  | Handle<JSObject> result; | 
|  | ASSIGN_RETURN_ON_EXCEPTION( | 
|  | isolate, result, | 
|  | JSObject::New(constructor, new_target, Handle<AllocationSite>::null()), | 
|  | JSDate); | 
|  | if (-DateCache::kMaxTimeInMs <= tv && tv <= DateCache::kMaxTimeInMs) { | 
|  | tv = DoubleToInteger(tv) + 0.0; | 
|  | } else { | 
|  | tv = std::numeric_limits<double>::quiet_NaN(); | 
|  | } | 
|  | Handle<Object> value = isolate->factory()->NewNumber(tv); | 
|  | Handle<JSDate>::cast(result)->SetValue(*value, std::isnan(tv)); | 
|  | return Handle<JSDate>::cast(result); | 
|  | } | 
|  |  | 
|  | // static | 
|  | double JSDate::CurrentTimeValue(Isolate* isolate) { | 
|  | if (FLAG_log_internal_timer_events) LOG(isolate, CurrentTimeEvent()); | 
|  |  | 
|  | // According to ECMA-262, section 15.9.1, page 117, the precision of | 
|  | // the number in a Date object representing a particular instant in | 
|  | // time is milliseconds. Therefore, we floor the result of getting | 
|  | // the OS time. | 
|  | return std::floor(V8::GetCurrentPlatform()->CurrentClockTimeMillis()); | 
|  | } | 
|  |  | 
|  | // static | 
|  | Address JSDate::GetField(Isolate* isolate, Address raw_object, | 
|  | Address smi_index) { | 
|  | // Called through CallCFunction. | 
|  | DisallowHeapAllocation no_gc; | 
|  | DisallowHandleAllocation no_handles; | 
|  | DisallowJavascriptExecution no_js(isolate); | 
|  |  | 
|  | Object object(raw_object); | 
|  | Smi index(smi_index); | 
|  | return JSDate::cast(object) | 
|  | .DoGetField(isolate, static_cast<FieldIndex>(index.value())) | 
|  | .ptr(); | 
|  | } | 
|  |  | 
|  | Object JSDate::DoGetField(Isolate* isolate, FieldIndex index) { | 
|  | DCHECK_NE(index, kDateValue); | 
|  |  | 
|  | DateCache* date_cache = isolate->date_cache(); | 
|  |  | 
|  | if (index < kFirstUncachedField) { | 
|  | Object stamp = cache_stamp(); | 
|  | if (stamp != date_cache->stamp() && stamp.IsSmi()) { | 
|  | // Since the stamp is not NaN, the value is also not NaN. | 
|  | int64_t local_time_ms = | 
|  | date_cache->ToLocal(static_cast<int64_t>(value().Number())); | 
|  | SetCachedFields(local_time_ms, date_cache); | 
|  | } | 
|  | switch (index) { | 
|  | case kYear: | 
|  | return year(); | 
|  | case kMonth: | 
|  | return month(); | 
|  | case kDay: | 
|  | return day(); | 
|  | case kWeekday: | 
|  | return weekday(); | 
|  | case kHour: | 
|  | return hour(); | 
|  | case kMinute: | 
|  | return min(); | 
|  | case kSecond: | 
|  | return sec(); | 
|  | default: | 
|  | UNREACHABLE(); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (index >= kFirstUTCField) { | 
|  | return GetUTCField(index, value().Number(), date_cache); | 
|  | } | 
|  |  | 
|  | double time = value().Number(); | 
|  | if (std::isnan(time)) return GetReadOnlyRoots().nan_value(); | 
|  |  | 
|  | int64_t local_time_ms = date_cache->ToLocal(static_cast<int64_t>(time)); | 
|  | int days = DateCache::DaysFromTime(local_time_ms); | 
|  |  | 
|  | if (index == kDays) return Smi::FromInt(days); | 
|  |  | 
|  | int time_in_day_ms = DateCache::TimeInDay(local_time_ms, days); | 
|  | if (index == kMillisecond) return Smi::FromInt(time_in_day_ms % 1000); | 
|  | DCHECK_EQ(index, kTimeInDay); | 
|  | return Smi::FromInt(time_in_day_ms); | 
|  | } | 
|  |  | 
|  | Object JSDate::GetUTCField(FieldIndex index, double value, | 
|  | DateCache* date_cache) { | 
|  | DCHECK_GE(index, kFirstUTCField); | 
|  |  | 
|  | if (std::isnan(value)) return GetReadOnlyRoots().nan_value(); | 
|  |  | 
|  | int64_t time_ms = static_cast<int64_t>(value); | 
|  |  | 
|  | if (index == kTimezoneOffset) { | 
|  | return Smi::FromInt(date_cache->TimezoneOffset(time_ms)); | 
|  | } | 
|  |  | 
|  | int days = DateCache::DaysFromTime(time_ms); | 
|  |  | 
|  | if (index == kWeekdayUTC) return Smi::FromInt(date_cache->Weekday(days)); | 
|  |  | 
|  | if (index <= kDayUTC) { | 
|  | int year, month, day; | 
|  | date_cache->YearMonthDayFromDays(days, &year, &month, &day); | 
|  | if (index == kYearUTC) return Smi::FromInt(year); | 
|  | if (index == kMonthUTC) return Smi::FromInt(month); | 
|  | DCHECK_EQ(index, kDayUTC); | 
|  | return Smi::FromInt(day); | 
|  | } | 
|  |  | 
|  | int time_in_day_ms = DateCache::TimeInDay(time_ms, days); | 
|  | switch (index) { | 
|  | case kHourUTC: | 
|  | return Smi::FromInt(time_in_day_ms / (60 * 60 * 1000)); | 
|  | case kMinuteUTC: | 
|  | return Smi::FromInt((time_in_day_ms / (60 * 1000)) % 60); | 
|  | case kSecondUTC: | 
|  | return Smi::FromInt((time_in_day_ms / 1000) % 60); | 
|  | case kMillisecondUTC: | 
|  | return Smi::FromInt(time_in_day_ms % 1000); | 
|  | case kDaysUTC: | 
|  | return Smi::FromInt(days); | 
|  | case kTimeInDayUTC: | 
|  | return Smi::FromInt(time_in_day_ms); | 
|  | default: | 
|  | UNREACHABLE(); | 
|  | } | 
|  |  | 
|  | UNREACHABLE(); | 
|  | } | 
|  |  | 
|  | // static | 
|  | Handle<Object> JSDate::SetValue(Handle<JSDate> date, double v) { | 
|  | Isolate* const isolate = date->GetIsolate(); | 
|  | Handle<Object> value = isolate->factory()->NewNumber(v); | 
|  | bool value_is_nan = std::isnan(v); | 
|  | date->SetValue(*value, value_is_nan); | 
|  | return value; | 
|  | } | 
|  |  | 
|  | void JSDate::SetValue(Object value, bool is_value_nan) { | 
|  | set_value(value); | 
|  | if (is_value_nan) { | 
|  | HeapNumber nan = GetReadOnlyRoots().nan_value(); | 
|  | set_cache_stamp(nan, SKIP_WRITE_BARRIER); | 
|  | set_year(nan, SKIP_WRITE_BARRIER); | 
|  | set_month(nan, SKIP_WRITE_BARRIER); | 
|  | set_day(nan, SKIP_WRITE_BARRIER); | 
|  | set_hour(nan, SKIP_WRITE_BARRIER); | 
|  | set_min(nan, SKIP_WRITE_BARRIER); | 
|  | set_sec(nan, SKIP_WRITE_BARRIER); | 
|  | set_weekday(nan, SKIP_WRITE_BARRIER); | 
|  | } else { | 
|  | set_cache_stamp(Smi::FromInt(DateCache::kInvalidStamp), SKIP_WRITE_BARRIER); | 
|  | } | 
|  | } | 
|  |  | 
|  | void JSDate::SetCachedFields(int64_t local_time_ms, DateCache* date_cache) { | 
|  | int days = DateCache::DaysFromTime(local_time_ms); | 
|  | int time_in_day_ms = DateCache::TimeInDay(local_time_ms, days); | 
|  | int year, month, day; | 
|  | date_cache->YearMonthDayFromDays(days, &year, &month, &day); | 
|  | int weekday = date_cache->Weekday(days); | 
|  | int hour = time_in_day_ms / (60 * 60 * 1000); | 
|  | int min = (time_in_day_ms / (60 * 1000)) % 60; | 
|  | int sec = (time_in_day_ms / 1000) % 60; | 
|  | set_cache_stamp(date_cache->stamp()); | 
|  | set_year(Smi::FromInt(year), SKIP_WRITE_BARRIER); | 
|  | set_month(Smi::FromInt(month), SKIP_WRITE_BARRIER); | 
|  | set_day(Smi::FromInt(day), SKIP_WRITE_BARRIER); | 
|  | set_weekday(Smi::FromInt(weekday), SKIP_WRITE_BARRIER); | 
|  | set_hour(Smi::FromInt(hour), SKIP_WRITE_BARRIER); | 
|  | set_min(Smi::FromInt(min), SKIP_WRITE_BARRIER); | 
|  | set_sec(Smi::FromInt(sec), SKIP_WRITE_BARRIER); | 
|  | } | 
|  |  | 
|  | // static | 
|  | void JSMessageObject::EnsureSourcePositionsAvailable( | 
|  | Isolate* isolate, Handle<JSMessageObject> message) { | 
|  | if (!message->DidEnsureSourcePositionsAvailable()) { | 
|  | DCHECK_EQ(message->start_position(), -1); | 
|  | DCHECK_GE(message->bytecode_offset().value(), kFunctionEntryBytecodeOffset); | 
|  | Handle<SharedFunctionInfo> shared_info( | 
|  | SharedFunctionInfo::cast(message->shared_info()), isolate); | 
|  | SharedFunctionInfo::EnsureSourcePositionsAvailable(isolate, shared_info); | 
|  | DCHECK(shared_info->HasBytecodeArray()); | 
|  | int position = shared_info->abstract_code().SourcePosition( | 
|  | message->bytecode_offset().value()); | 
|  | DCHECK_GE(position, 0); | 
|  | message->set_start_position(position); | 
|  | message->set_end_position(position + 1); | 
|  | message->set_shared_info(ReadOnlyRoots(isolate).undefined_value()); | 
|  | } | 
|  | } | 
|  |  | 
|  | int JSMessageObject::GetLineNumber() const { | 
|  | DCHECK(DidEnsureSourcePositionsAvailable()); | 
|  | if (start_position() == -1) return Message::kNoLineNumberInfo; | 
|  |  | 
|  | Handle<Script> the_script(script(), GetIsolate()); | 
|  |  | 
|  | Script::PositionInfo info; | 
|  | const Script::OffsetFlag offset_flag = Script::WITH_OFFSET; | 
|  | if (!Script::GetPositionInfo(the_script, start_position(), &info, | 
|  | offset_flag)) { | 
|  | return Message::kNoLineNumberInfo; | 
|  | } | 
|  |  | 
|  | return info.line + 1; | 
|  | } | 
|  |  | 
|  | int JSMessageObject::GetColumnNumber() const { | 
|  | DCHECK(DidEnsureSourcePositionsAvailable()); | 
|  | if (start_position() == -1) return -1; | 
|  |  | 
|  | Handle<Script> the_script(script(), GetIsolate()); | 
|  |  | 
|  | Script::PositionInfo info; | 
|  | const Script::OffsetFlag offset_flag = Script::WITH_OFFSET; | 
|  | if (!Script::GetPositionInfo(the_script, start_position(), &info, | 
|  | offset_flag)) { | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | return info.column;  // Note: No '+1' in contrast to GetLineNumber. | 
|  | } | 
|  |  | 
|  | Handle<String> JSMessageObject::GetSourceLine() const { | 
|  | Isolate* isolate = GetIsolate(); | 
|  | Handle<Script> the_script(script(), isolate); | 
|  |  | 
|  | if (the_script->type() == Script::TYPE_WASM) { | 
|  | return isolate->factory()->empty_string(); | 
|  | } | 
|  |  | 
|  | Script::PositionInfo info; | 
|  | const Script::OffsetFlag offset_flag = Script::WITH_OFFSET; | 
|  | DCHECK(DidEnsureSourcePositionsAvailable()); | 
|  | if (!Script::GetPositionInfo(the_script, start_position(), &info, | 
|  | offset_flag)) { | 
|  | return isolate->factory()->empty_string(); | 
|  | } | 
|  |  | 
|  | Handle<String> src = handle(String::cast(the_script->source()), isolate); | 
|  | return isolate->factory()->NewSubString(src, info.line_start, info.line_end); | 
|  | } | 
|  |  | 
|  | }  // namespace internal | 
|  | }  // namespace v8 |