blob: cae2d8297e94df326e29ed66d5e4ccb4b557e552 [file] [log] [blame]
// 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-arguments-inl.h"
#include "src/arguments.h"
#include "src/bootstrapper.h"
#include "src/compiler.h"
#include "src/counters.h"
#include "src/date.h"
#include "src/elements.h"
#include "src/field-type.h"
#include "src/handles-inl.h"
#include "src/heap/heap-inl.h"
#include "src/ic/ic.h"
#include "src/isolate.h"
#include "src/layout-descriptor.h"
#include "src/log.h"
#include "src/lookup.h"
#include "src/maybe-handles.h"
#include "src/objects-inl.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/fixed-array.h"
#include "src/objects/heap-number.h"
#include "src/objects/js-array-buffer.h"
#include "src/objects/js-array-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"
#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"
#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/prototype-info.h"
#include "src/objects/shared-function-info.h"
#include "src/ostreams.h"
#include "src/property-descriptor.h"
#include "src/property.h"
#include "src/prototype.h"
#include "src/string-builder-inl.h"
#include "src/string-stream.h"
#include "src/transitions.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.
LookupIterator it = LookupIterator::PropertyOrElement(
object->GetIsolate(), object, name, object, 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) {
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();
}
}
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);
}
// 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(Handle<JSObject>::cast(target));
}
Isolate* isolate = target->GetIsolate();
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(), isolate);
int length = map->NumberOfOwnDescriptors();
bool stable = true;
for (int i = 0; i < length; i++) {
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) {
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::ForDescriptor(*map, i);
prop_value = JSObject::FastPropertyAt(from, representation, index);
}
} else {
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate, prop_value,
JSReceiver::GetProperty(isolate, from, next_key), Nothing<bool>());
stable = from->map() == *map;
}
} 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(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) {
LookupIterator it(target, next_key, target);
Maybe<bool> result =
Object::SetProperty(&it, prop_value, StoreOrigin::kNamed,
Just(ShouldThrow::kThrowOnError));
if (result.IsNothing()) return result;
if (stable) stable = from->map() == *map;
} else {
if (excluded_properties != nullptr &&
HasExcludedProperty(excluded_properties, next_key)) {
continue;
}
// 4a ii 2. Perform ? CreateDataProperty(target, nextKey, propValue).
bool success;
LookupIterator it = LookupIterator::PropertyOrElement(
isolate, target, next_key, &success, LookupIterator::OWN);
CHECK(success);
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>());
// 4. Repeat for each element nextKey of keys in List order,
for (int j = 0; j < keys->length(); ++j) {
Handle<Object> next_key(keys->get(j), 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).
bool success;
LookupIterator it = LookupIterator::PropertyOrElement(
isolate, target, next_key, &success, LookupIterator::OWN);
CHECK(success);
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 (IsJSValue()) {
Object value = JSValue::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();
Object maybe_constructor = map()->GetConstructor();
if (maybe_constructor->IsJSFunction()) {
JSFunction constructor = JSFunction::cast(maybe_constructor);
if (constructor->shared()->IsApiFunction()) {
maybe_constructor = constructor->shared()->get_api_func_data();
}
}
if (maybe_constructor->IsFunctionTemplateInfo()) {
FunctionTemplateInfo info = FunctionTemplateInfo::cast(maybe_constructor);
if (info->class_name()->IsString()) return String::cast(info->class_name());
}
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));
}
} else if (maybe_constructor->IsFunctionTemplateInfo()) {
FunctionTemplateInfo info = FunctionTemplateInfo::cast(maybe_constructor);
if (info->class_name()->IsString()) {
return std::make_pair(
MaybeHandle<JSFunction>(),
handle(String::cast(info->class_name()), isolate));
}
}
}
Handle<Object> maybe_tag = JSReceiver::GetDataProperty(
receiver, isolate->factory()->to_string_tag_symbol());
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(receiver, isolate->factory()->constructor_string(), start,
LookupIterator::PROTOTYPE_CHAIN_SKIP_INTERCEPTOR);
Handle<Object> maybe_constructor = JSReceiver::GetDataProperty(&it);
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()) {
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;
}
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 (properties->IsNameDictionary()) {
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());
#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,
int entry) {
DCHECK(!object->HasFastProperties());
Isolate* isolate = object->GetIsolate();
if (object->IsJSGlobalObject()) {
// If we have a global object, invalidate the cell and swap in a new one.
Handle<GlobalDictionary> dictionary(
JSGlobalObject::cast(*object)->global_dictionary(), isolate);
DCHECK_NE(GlobalDictionary::kNotFound, entry);
auto cell = PropertyCell::InvalidateEntry(isolate, dictionary, entry);
cell->set_value(ReadOnlyRoots(isolate).the_hole_value());
cell->set_property_details(
PropertyDetails::Empty(PropertyCellType::kUninitialized));
} else {
Handle<NameDictionary> dictionary(object->property_dictionary(), isolate);
DCHECK_NE(NameDictionary::kNotFound, entry);
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, name, object, LookupIterator::OWN);
return DeleteProperty(&it, language_mode);
}
Maybe<bool> JSReceiver::DeletePropertyOrElement(Handle<JSReceiver> object,
Handle<Name> name,
LanguageMode language_mode) {
LookupIterator it = LookupIterator::PropertyOrElement(
object->GetIsolate(), object, name, 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).
bool success = false;
LookupIterator it = LookupIterator::PropertyOrElement(
isolate, props, next_key, &success, LookupIterator::OWN);
DCHECK(success);
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).
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) {
bool success = false;
DCHECK(key->IsName() || key->IsNumber()); // |key| is a PropertyKey...
LookupIterator it = LookupIterator::PropertyOrElement(
isolate, object, key, &success, LookupIterator::OWN);
DCHECK(success); // ...so creating a LookupIterator can't fail.
// 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()) {
result = args.CallIndexedGetter(interceptor, it->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() && 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()) {
result = args.CallIndexedQuery(interceptor, it->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()) {
result = args.CallIndexedGetter(interceptor, it->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()) {
// 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->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()) {
result = !args.CallIndexedDefiner(interceptor, it->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, &current), 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, &current, 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 it = LookupIterator::PropertyOrElement(isolate, object, 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) {
bool success = false;
DCHECK(key->IsName() || key->IsNumber()); // |key| is a PropertyKey...
LookupIterator it = LookupIterator::PropertyOrElement(
isolate, object, key, &success, LookupIterator::OWN);
DCHECK(success); // ...so creating a LookupIterator can't fail.
return GetOwnPropertyDescriptor(&it, desc);
}
namespace {
Maybe<bool> GetPropertyDescriptorWithInterceptor(LookupIterator* it,
PropertyDescriptor* desc) {
if (it->state() == LookupIterator::ACCESS_CHECK) {
if (it->HasAccess()) {
it->Next();
} else if (!JSObject::AllCanRead(it) ||
it->state() != LookupIterator::INTERCEPTOR) {
it->Restart();
return Just(false);
}
}
if (it->state() != LookupIterator::INTERCEPTOR) return Just(false);
Isolate* isolate = it->isolate();
Handle<InterceptorInfo> interceptor = it->GetInterceptor();
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()) {
result = args.CallIndexedDescriptor(interceptor, it->index());
} else {
result = args.CallNamedDescriptor(interceptor, it->name());
}
if (!result.is_null()) {
// Request successfully intercepted, try to set the property
// descriptor.
Utils::ApiCheck(
PropertyDescriptor::ToPropertyDescriptor(isolate, result, desc),
it->IsElement() ? "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());
// 6a. Set D.[[Get]] to the value of X's [[Get]] attribute.
desc->set_get(
AccessorPair::GetComponent(isolate, accessors, ACCESSOR_GETTER));
// 6b. Set D.[[Set]] to the value of X's [[Set]] attribute.
desc->set_set(
AccessorPair::GetComponent(isolate, 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, &current_desc);
MAYBE_RETURN(owned, Nothing<bool>());
if (owned.FromJust()) {
PropertyDescriptor desc =
PropertyDescriptor::IsAccessorDescriptor(&current_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, &current_desc);
MAYBE_RETURN(owned, Nothing<bool>());
if (owned.FromJust()) {
if (current_desc.configurable()) return Just(false);
if (level == FROZEN &&
PropertyDescriptor::IsDataDescriptor(&current_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(), isolate);
int number_of_own_descriptors = map->NumberOfOwnDescriptors();
int number_of_own_elements =
object->GetElementsAccessor()->GetCapacity(*object, object->elements());
Handle<FixedArray> values_or_entries = isolate->factory()->NewFixedArray(
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>());
}
bool stable = object->map() == *map;
for (int index = 0; index < number_of_own_descriptors; index++) {
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) {
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::ForDescriptor(*map, index);
prop_value =
JSObject::FastPropertyAt(object, representation, field_index);
}
} else {
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate, prop_value,
JSReceiver::GetProperty(isolate, object, next_key),
Nothing<bool>());
stable = object->map() == *map;
}
} 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(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);
}
Handle<FixedArray> JSReceiver::GetOwnElementIndices(Isolate* isolate,
Handle<JSReceiver> receiver,
Handle<JSObject> object) {
KeyAccumulator accumulator(isolate, KeyCollectionMode::kOwnOnly,
ALL_PROPERTIES);
accumulator.CollectOwnElementIndices(receiver, object);
Handle<FixedArray> keys =
accumulator.GetKeys(GetKeysConversion::kKeepNumbers);
DCHECK(keys->ContainsSortedNumbers());
return keys;
}
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::HasComplexElements() {
if (IsJSProxy()) return true;
JSObject this_object = JSObject::cast(*this);
if (this_object->HasIndexedInterceptor()) {
return true;
}
if (!this_object->HasDictionaryElements()) return false;
return this_object->element_dictionary()->HasComplexElements();
}
// 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);
Handle<JSObject> result = isolate->factory()->NewJSObjectFromMap(
initial_map, AllocationType::kYoung, site);
if (initial_map->is_dictionary_map()) {
Handle<NameDictionary> dictionary =
NameDictionary::New(isolate, NameDictionary::kInitialCapacity);
result->SetProperties(*dictionary);
}
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.
Handle<JSObject> object;
if (map->is_dictionary_map()) {
object = isolate->factory()->NewSlowJSObjectFromMap(map);
} else {
object = isolate->factory()->NewJSObjectFromMap(map);
}
return object;
}
void JSObject::EnsureWritableFastElements(Handle<JSObject> object) {
DCHECK(object->HasSmiOrObjectElements() ||
object->HasFastStringWrapperElements() ||
object->HasFrozenOrSealedElements());
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::kSize;
case JS_ASYNC_FUNCTION_OBJECT_TYPE:
return JSAsyncFunctionObject::kSize;
case JS_ASYNC_GENERATOR_OBJECT_TYPE:
return JSAsyncGeneratorObject::kSize;
case JS_ASYNC_FROM_SYNC_ITERATOR_TYPE:
return JSAsyncFromSyncIterator::kSize;
case JS_GLOBAL_PROXY_TYPE:
return JSGlobalProxy::kSize;
case JS_GLOBAL_OBJECT_TYPE:
return JSGlobalObject::kSize;
case JS_BOUND_FUNCTION_TYPE:
return JSBoundFunction::kSize;
case JS_FUNCTION_TYPE:
return JSFunction::GetHeaderSize(function_has_prototype_slot);
case JS_VALUE_TYPE:
return JSValue::kSize;
case JS_DATE_TYPE:
return JSDate::kSize;
case JS_ARRAY_TYPE:
return JSArray::kSize;
case JS_ARRAY_BUFFER_TYPE:
return JSArrayBuffer::kHeaderSize;
case JS_ARRAY_ITERATOR_TYPE:
return JSArrayIterator::kSize;
case JS_TYPED_ARRAY_TYPE:
return JSTypedArray::kHeaderSize;
case JS_DATA_VIEW_TYPE:
return JSDataView::kHeaderSize;
case JS_SET_TYPE:
return JSSet::kSize;
case JS_MAP_TYPE:
return JSMap::kSize;
case JS_SET_KEY_VALUE_ITERATOR_TYPE:
case JS_SET_VALUE_ITERATOR_TYPE:
return JSSetIterator::kSize;
case JS_MAP_KEY_ITERATOR_TYPE:
case JS_MAP_KEY_VALUE_ITERATOR_TYPE:
case JS_MAP_VALUE_ITERATOR_TYPE:
return JSMapIterator::kSize;
case WEAK_CELL_TYPE:
return WeakCell::kSize;
case JS_WEAK_REF_TYPE:
return JSWeakRef::kSize;
case JS_FINALIZATION_GROUP_TYPE:
return JSFinalizationGroup::kSize;
case JS_FINALIZATION_GROUP_CLEANUP_ITERATOR_TYPE:
return JSFinalizationGroupCleanupIterator::kSize;
case JS_WEAK_MAP_TYPE:
return JSWeakMap::kSize;
case JS_WEAK_SET_TYPE:
return JSWeakSet::kSize;
case JS_PROMISE_TYPE:
return JSPromise::kSize;
case JS_REGEXP_TYPE:
return JSRegExp::kSize;
case JS_REGEXP_STRING_ITERATOR_TYPE:
return JSRegExpStringIterator::kSize;
case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
return JSObject::kHeaderSize;
case JS_MESSAGE_OBJECT_TYPE:
return JSMessageObject::kSize;
case JS_ARGUMENTS_TYPE:
return JSObject::kHeaderSize;
case JS_ERROR_TYPE:
return JSObject::kHeaderSize;
case JS_STRING_ITERATOR_TYPE:
return JSStringIterator::kSize;
case JS_MODULE_NAMESPACE_TYPE:
return JSModuleNamespace::kHeaderSize;
#ifdef V8_INTL_SUPPORT
case JS_INTL_V8_BREAK_ITERATOR_TYPE:
return JSV8BreakIterator::kSize;
case JS_INTL_COLLATOR_TYPE:
return JSCollator::kSize;
case JS_INTL_DATE_TIME_FORMAT_TYPE:
return JSDateTimeFormat::kSize;
case JS_INTL_LIST_FORMAT_TYPE:
return JSListFormat::kSize;
case JS_INTL_LOCALE_TYPE:
return JSLocale::kSize;
case JS_INTL_NUMBER_FORMAT_TYPE:
return JSNumberFormat::kSize;
case JS_INTL_PLURAL_RULES_TYPE:
return JSPluralRules::kSize;
case JS_INTL_RELATIVE_TIME_FORMAT_TYPE:
return JSRelativeTimeFormat::kSize;
case JS_INTL_SEGMENT_ITERATOR_TYPE:
return JSSegmentIterator::kSize;
case JS_INTL_SEGMENTER_TYPE:
return JSSegmenter::kSize;
#endif // V8_INTL_SUPPORT
case WASM_GLOBAL_TYPE:
return WasmGlobalObject::kSize;
case WASM_INSTANCE_TYPE:
return WasmInstanceObject::kSize;
case WASM_MEMORY_TYPE:
return WasmMemoryObject::kSize;
case WASM_MODULE_TYPE:
return WasmModuleObject::kSize;
case WASM_TABLE_TYPE:
return WasmTableObject::kSize;
case WASM_EXCEPTION_TYPE:
return WasmExceptionObject::kSize;
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);
int entry = dictionary->FindEntry(ReadOnlyRoots(isolate), name, hash);
if (entry == GlobalDictionary::kNotFound) {
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(isolate)
? 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 {
Handle<NameDictionary> dictionary(object->property_dictionary(), isolate);
int entry = dictionary->FindEntry(isolate, name);
if (entry == NameDictionary::kNotFound) {
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(isolate, 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_REGEXP_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);