#include "src/ast/ast.h"
#include "src/base/logging.h"
#include "src/common/globals.h"
#include "src/execution/arguments-inl.h"
#include "src/execution/isolate-inl.h"
#include "src/handles/maybe-handles.h"
#include "src/objects/allocation-site-scopes-inl.h"
#include "src/objects/casting.h"
#include "src/objects/hash-table-inl.h"
#include "src/objects/heap-number-inl.h"
#include "src/objects/heap-object.h"
#include "src/objects/js-regexp-inl.h"
#include "src/objects/literal-objects-inl.h"
#include "src/objects/lookup.h"
#include "src/objects/objects.h"
#include "src/objects/property-descriptor-object.h"
#include "src/objects/property-descriptor.h"
#include "src/objects/property-details.h"
#include "src/runtime/runtime.h"
namespace v8 {
namespace internal {
namespace {
bool IsUninitializedLiteralSite(Tagged<Object> literal_site) {
return literal_site == Smi::zero();
}
bool HasBoilerplate(DirectHandle<Object> literal_site) {
return !IsSmi(*literal_site);
}
void PreInitializeLiteralSite(DirectHandle<FeedbackVector> vector,
FeedbackSlot slot) {
vector->SynchronizedSet(slot, Smi::FromInt(1));
}
template <class ContextObject>
class JSObjectWalkVisitor {
public:
explicit JSObjectWalkVisitor(ContextObject* site_context)
: site_context_(site_context) {}
V8_WARN_UNUSED_RESULT MaybeHandle<JSObject> StructureWalk(
Handle<JSObject> object);
protected:
V8_WARN_UNUSED_RESULT inline MaybeHandle<JSObject> VisitElementOrProperty(
DirectHandle<JSObject> object, Handle<JSObject> value) {
if (!IsJSArray(*value)) {
return StructureWalk(value);
}
DirectHandle<AllocationSite> current_site = site_context()->EnterNewScope();
MaybeHandle<JSObject> copy_of_value = StructureWalk(value);
site_context()->ExitScope(current_site, value);
return copy_of_value;
}
inline ContextObject* site_context() { return site_context_; }
inline Isolate* isolate() { return site_context()->isolate(); }
private:
ContextObject* site_context_;
};
template <class ContextObject>
MaybeHandle<JSObject> JSObjectWalkVisitor<ContextObject>::StructureWalk(
Handle<JSObject> object) {
Isolate* isolate = this->isolate();
bool copying = ContextObject::kCopying;
{
StackLimitCheck check(isolate);
if (check.HasOverflowed()) {
isolate->StackOverflow();
return MaybeHandle<JSObject>();
}
}
if (object->map(isolate)->is_deprecated()) {
base::MutexGuard mutex_guard(isolate->boilerplate_migration_access());
JSObject::MigrateInstance(isolate, object);
}
Handle<JSObject> copy;
if (copying) {
DCHECK(!IsJSFunction(*object, isolate));
DirectHandle<AllocationSite> site_to_pass;
if (site_context()->ShouldCreateMemento(object)) {
site_to_pass = site_context()->current();
}
copy = isolate->factory()->CopyJSObjectWithAllocationSite(object,
site_to_pass);
} else {
copy = object;
}
DCHECK(copying || copy.is_identical_to(object));
HandleScope scope(isolate);
if (!IsJSArray(*copy, isolate)) {
if (copy->HasFastProperties(isolate)) {
DirectHandle<DescriptorArray> descriptors(
copy->map(isolate)->instance_descriptors(isolate), isolate);
for (InternalIndex i : copy->map(isolate)->IterateOwnDescriptors()) {
PropertyDetails details = descriptors->GetDetails(i);
DCHECK_EQ(PropertyLocation::kField, details.location());
DCHECK_EQ(PropertyKind::kData, details.kind());
FieldIndex index = FieldIndex::ForPropertyIndex(
copy->map(isolate), details.field_index(),
details.representation());
Tagged<Object> raw = copy->RawFastPropertyAt(isolate, index);
if (IsJSObject(raw, isolate)) {
Handle<JSObject> value(Cast<JSObject>(raw), isolate);
ASSIGN_RETURN_ON_EXCEPTION(isolate, value,
VisitElementOrProperty(copy, value));
if (copying) copy->FastPropertyAtPut(index, *value);
} else if (copying && details.representation().IsDouble()) {
uint64_t double_value = Cast<HeapNumber>(raw)->value_as_bits();
auto value = isolate->factory()->NewHeapNumberFromBits(double_value);
copy->FastPropertyAtPut(index, *value);
}
}
} else {
if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
DirectHandle<SwissNameDictionary> dict(
copy->property_dictionary_swiss(isolate), isolate);
for (InternalIndex i : dict->IterateEntries()) {
Tagged<Object> raw = dict->ValueAt(i);
if (!IsJSObject(raw, isolate)) continue;
DCHECK(IsName(dict->KeyAt(i)));
Handle<JSObject> value(Cast<JSObject>(raw), isolate);
ASSIGN_RETURN_ON_EXCEPTION(isolate, value,
VisitElementOrProperty(copy, value));
if (copying) dict->ValueAtPut(i, *value);
}
} else {
DirectHandle<NameDictionary> dict(copy->property_dictionary(isolate),
isolate);
for (InternalIndex i : dict->IterateEntries()) {
Tagged<Object> raw = dict->ValueAt(isolate, i);
if (!IsJSObject(raw, isolate)) continue;
DCHECK(IsName(dict->KeyAt(isolate, i)));
Handle<JSObject> value(Cast<JSObject>(raw), isolate);
ASSIGN_RETURN_ON_EXCEPTION(isolate, value,
VisitElementOrProperty(copy, value));
if (copying) dict->ValueAtPut(i, *value);
}
}
}
if (copy->elements(isolate)->length() == 0) return copy;
}
switch (copy->GetElementsKind(isolate)) {
case PACKED_ELEMENTS:
case PACKED_FROZEN_ELEMENTS:
case PACKED_SEALED_ELEMENTS:
case PACKED_NONEXTENSIBLE_ELEMENTS:
case HOLEY_FROZEN_ELEMENTS:
case HOLEY_SEALED_ELEMENTS:
case HOLEY_NONEXTENSIBLE_ELEMENTS:
case HOLEY_ELEMENTS:
case SHARED_ARRAY_ELEMENTS: {
DirectHandle<FixedArray> elements(
Cast<FixedArray>(copy->elements(isolate)), isolate);
if (elements->map() == ReadOnlyRoots(isolate).fixed_cow_array_map()) {
#ifdef DEBUG
for (int i = 0; i < elements->length(); i++) {
DCHECK(!IsJSObject(elements->get(i)));
}
#endif
} else {
for (int i = 0; i < elements->length(); i++) {
Tagged<Object> raw = elements->get(i);
if (!IsJSObject(raw, isolate)) continue;
Handle<JSObject> value(Cast<JSObject>(raw), isolate);
ASSIGN_RETURN_ON_EXCEPTION(isolate, value,
VisitElementOrProperty(copy, value));
if (copying) elements->set(i, *value);
}
}
break;
}
case DICTIONARY_ELEMENTS: {
DirectHandle<NumberDictionary> element_dictionary(
copy->element_dictionary(isolate), isolate);
for (InternalIndex i : element_dictionary->IterateEntries()) {
Tagged<Object> raw = element_dictionary->ValueAt(isolate, i);
if (!IsJSObject(raw, isolate)) continue;
Handle<JSObject> value(Cast<JSObject>(raw), isolate);
ASSIGN_RETURN_ON_EXCEPTION(isolate, value,
VisitElementOrProperty(copy, value));
if (copying) element_dictionary->ValueAtPut(i, *value);
}
break;
}
case FAST_SLOPPY_ARGUMENTS_ELEMENTS:
case SLOW_SLOPPY_ARGUMENTS_ELEMENTS:
UNIMPLEMENTED();
break;
case FAST_STRING_WRAPPER_ELEMENTS:
case SLOW_STRING_WRAPPER_ELEMENTS:
case WASM_ARRAY_ELEMENTS:
UNREACHABLE();
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) case TYPE##_ELEMENTS:
TYPED_ARRAYS(TYPED_ARRAY_CASE)
RAB_GSAB_TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
UNREACHABLE();
case PACKED_SMI_ELEMENTS:
case HOLEY_SMI_ELEMENTS:
case PACKED_DOUBLE_ELEMENTS:
case HOLEY_DOUBLE_ELEMENTS:
case NO_ELEMENTS:
break;
}
return copy;
}
class DeprecationUpdateContext {
public:
explicit DeprecationUpdateContext(Isolate* isolate) { isolate_ = isolate; }
Isolate* isolate() { return isolate_; }
bool ShouldCreateMemento(DirectHandle<JSObject> object) { return false; }
inline void ExitScope(DirectHandle<AllocationSite> scope_site,
DirectHandle<JSObject> object) {}
DirectHandle<AllocationSite> EnterNewScope() {
return DirectHandle<AllocationSite>();
}
DirectHandle<AllocationSite> current() { UNREACHABLE(); }
static const bool kCopying = false;
private:
Isolate* isolate_;
};
class AllocationSiteCreationContext : public AllocationSiteContext {
public:
explicit AllocationSiteCreationContext(Isolate* isolate)
: AllocationSiteContext(isolate) {}
Handle<AllocationSite> EnterNewScope() {
Handle<AllocationSite> scope_site;
if (top().is_null()) {
InitializeTraversal(isolate()->factory()->NewAllocationSite(true));
scope_site = Handle<AllocationSite>(*top(), isolate());
if (v8_flags.trace_creation_allocation_sites) {
PrintF("*** Creating top level %s AllocationSite %p\n", "Fat",
reinterpret_cast<void*>(scope_site->ptr()));
}
} else {
DCHECK(!current().is_null());
scope_site = isolate()->factory()->NewAllocationSite(false);
if (v8_flags.trace_creation_allocation_sites) {
PrintF(
"*** Creating nested %s AllocationSite (top, current, new) (%p, "
"%p, "
"%p)\n",
"Slim", reinterpret_cast<void*>(top()->ptr()),
reinterpret_cast<void*>(current()->ptr()),
reinterpret_cast<void*>(scope_site->ptr()));
}
current()->set_nested_site(*scope_site);
update_current_site(*scope_site);
}
DCHECK(!scope_site.is_null());
return scope_site;
}
void ExitScope(DirectHandle<AllocationSite> scope_site,
DirectHandle<JSObject> object) {
if (object.is_null()) return;
scope_site->set_boilerplate(*object, kReleaseStore);
if (v8_flags.trace_creation_allocation_sites) {
bool top_level =
!scope_site.is_null() && top().is_identical_to(scope_site);
if (top_level) {
PrintF("*** Setting AllocationSite %p transition_info %p\n",
reinterpret_cast<void*>(scope_site->ptr()),
reinterpret_cast<void*>(object->ptr()));
} else {
PrintF("*** Setting AllocationSite (%p, %p) transition_info %p\n",
reinterpret_cast<void*>(top()->ptr()),
reinterpret_cast<void*>(scope_site->ptr()),
reinterpret_cast<void*>(object->ptr()));
}
}
}
static const bool kCopying = false;
};
MaybeDirectHandle<JSObject> DeepWalk(Handle<JSObject> object,
DeprecationUpdateContext* site_context) {
JSObjectWalkVisitor<DeprecationUpdateContext> v(site_context);
MaybeDirectHandle<JSObject> result = v.StructureWalk(object);
DirectHandle<JSObject> for_assert;
DCHECK(!result.ToHandle(&for_assert) || for_assert.is_identical_to(object));
return result;
}
MaybeDirectHandle<JSObject> DeepWalk(
Handle<JSObject> object, AllocationSiteCreationContext* site_context) {
JSObjectWalkVisitor<AllocationSiteCreationContext> v(site_context);
MaybeDirectHandle<JSObject> result = v.StructureWalk(object);
DirectHandle<JSObject> for_assert;
DCHECK(!result.ToHandle(&for_assert) || for_assert.is_identical_to(object));
return result;
}
MaybeDirectHandle<JSObject> DeepCopy(Handle<JSObject> object,
AllocationSiteUsageContext* site_context) {
JSObjectWalkVisitor<AllocationSiteUsageContext> v(site_context);
MaybeDirectHandle<JSObject> copy = v.StructureWalk(object);
DirectHandle<JSObject> for_assert;
DCHECK(!copy.ToHandle(&for_assert) || !for_assert.is_identical_to(object));
return copy;
}
Handle<JSObject> CreateObjectLiteral(
Isolate* isolate,
DirectHandle<ObjectBoilerplateDescription> object_boilerplate_description,
int flags, AllocationType allocation);
Handle<JSObject> CreateArrayLiteral(
Isolate* isolate,
DirectHandle<ArrayBoilerplateDescription> array_boilerplate_description,
AllocationType allocation);
struct ObjectLiteralHelper {
static inline Handle<JSObject> Create(Isolate* isolate,
Handle<HeapObject> description,
int flags, AllocationType allocation) {
auto object_boilerplate_description =
Cast<ObjectBoilerplateDescription>(description);
return CreateObjectLiteral(isolate, object_boilerplate_description, flags,
allocation);
}
};
struct ArrayLiteralHelper {
static inline Handle<JSObject> Create(Isolate* isolate,
Handle<HeapObject> description,
int flags_not_used,
AllocationType allocation) {
auto array_boilerplate_description =
Cast<ArrayBoilerplateDescription>(description);
return CreateArrayLiteral(isolate, array_boilerplate_description,
allocation);
}
};
Handle<JSObject> CreateObjectLiteral(
Isolate* isolate,
DirectHandle<ObjectBoilerplateDescription> object_boilerplate_description,
int flags, AllocationType allocation) {
DirectHandle<NativeContext> native_context = isolate->native_context();
bool use_fast_elements = (flags & ObjectLiteral::kFastElements) != 0;
bool has_null_prototype = (flags & ObjectLiteral::kHasNullPrototype) != 0;
int number_of_properties =
object_boilerplate_description->backing_store_size();
DirectHandle<Map> map =
has_null_prototype
? direct_handle(native_context->slow_object_with_null_prototype_map(),
isolate)
: isolate->factory()->ObjectLiteralMapFromCache(native_context,
number_of_properties);
Handle<JSObject> boilerplate =
isolate->factory()->NewFastOrSlowJSObjectFromMap(
map, number_of_properties, allocation);
if (!use_fast_elements) JSObject::NormalizeElements(isolate, boilerplate);
int length = object_boilerplate_description->boilerplate_properties_count();
for (int index = 0; index < length; index++) {
DirectHandle<Object> key(object_boilerplate_description->name(index),
isolate);
Handle<Object> value(object_boilerplate_description->value(index), isolate);
if (IsHeapObject(*value)) {
if (IsArrayBoilerplateDescription(Cast<HeapObject>(*value), isolate)) {
auto array_boilerplate = Cast<ArrayBoilerplateDescription>(value);
value = CreateArrayLiteral(isolate, array_boilerplate, allocation);
} else if (IsObjectBoilerplateDescription(Cast<HeapObject>(*value),
isolate)) {
auto object_boilerplate = Cast<ObjectBoilerplateDescription>(value);
value = CreateObjectLiteral(isolate, object_boilerplate,
object_boilerplate->flags(), allocation);
}
}
uint32_t element_index = 0;
if (Object::ToArrayIndex(*key, &element_index)) {
JSObject::SetOwnElementIgnoreAttributes(boilerplate, element_index, value,
NONE)
.Check();
} else {
DirectHandle<String> name = Cast<String>(key);
DCHECK(!name->AsArrayIndex(&element_index));
JSObject::SetOwnPropertyIgnoreAttributes(boilerplate, name, value, NONE)
.Check();
}
}
return boilerplate;
}
Handle<JSObject> CreateArrayLiteral(
Isolate* isolate,
DirectHandle<ArrayBoilerplateDescription> array_boilerplate_description,
AllocationType allocation) {
ElementsKind constant_elements_kind =
array_boilerplate_description->elements_kind();
Handle<FixedArrayBase> constant_elements_values(
array_boilerplate_description->constant_elements(), isolate);
Handle<FixedArrayBase> copied_elements_values;
if (IsDoubleElementsKind(constant_elements_kind)) {
copied_elements_values = isolate->factory()->CopyFixedDoubleArray(
Cast<FixedDoubleArray>(constant_elements_values));
} else {
DCHECK(IsSmiOrObjectElementsKind(constant_elements_kind));
const bool is_cow = (constant_elements_values->map() ==
ReadOnlyRoots(isolate).fixed_cow_array_map());
if (is_cow) {
copied_elements_values = constant_elements_values;
if (DEBUG_BOOL) {
auto fixed_array_values = Cast<FixedArray>(copied_elements_values);
for (int i = 0; i < fixed_array_values->length(); i++) {
DCHECK(!IsFixedArray(fixed_array_values->get(i)));
}
}
} else {
Handle<FixedArray> fixed_array_values =
Cast<FixedArray>(constant_elements_values);
Handle<FixedArray> fixed_array_values_copy =
isolate->factory()->CopyFixedArray(fixed_array_values);
copied_elements_values = fixed_array_values_copy;
for (int i = 0; i < fixed_array_values->length(); i++) {
Tagged<Object> value = fixed_array_values_copy->get(i);
Tagged<HeapObject> value_heap_object;
if (!value.GetHeapObject(isolate, &value_heap_object)) continue;
if (IsAnyHole(value_heap_object)) continue;
if (IsArrayBoilerplateDescription(value_heap_object, isolate)) {
HandleScope sub_scope(isolate);
DirectHandle<ArrayBoilerplateDescription> boilerplate(
Cast<ArrayBoilerplateDescription>(value_heap_object), isolate);
DirectHandle<JSObject> result =
CreateArrayLiteral(isolate, boilerplate, allocation);
fixed_array_values_copy->set(i, *result);
} else if (IsObjectBoilerplateDescription(value_heap_object, isolate)) {
HandleScope sub_scope(isolate);
DirectHandle<ObjectBoilerplateDescription> boilerplate(
Cast<ObjectBoilerplateDescription>(value_heap_object), isolate);
DirectHandle<JSObject> result = CreateObjectLiteral(
isolate, boilerplate, boilerplate->flags(), allocation);
fixed_array_values_copy->set(i, *result);
}
}
}
}
return isolate->factory()->NewJSArrayWithElements(
copied_elements_values, constant_elements_kind,
copied_elements_values->length(), allocation);
}
template <typename LiteralHelper>
MaybeDirectHandle<JSObject> CreateLiteralWithoutAllocationSite(
Isolate* isolate, Handle<HeapObject> description, int flags) {
Handle<JSObject> literal = LiteralHelper::Create(isolate, description, flags,
AllocationType::kYoung);
DeprecationUpdateContext update_context(isolate);
RETURN_ON_EXCEPTION(isolate, DeepWalk(literal, &update_context));
return literal;
}
template <typename LiteralHelper>
MaybeDirectHandle<JSObject> CreateLiteral(Isolate* isolate,
Handle<HeapObject> maybe_vector,
int literals_index,
Handle<HeapObject> description,
int flags) {
if (!IsFeedbackVector(*maybe_vector)) {
DCHECK(IsUndefined(*maybe_vector));
return CreateLiteralWithoutAllocationSite<LiteralHelper>(
isolate, description, flags);
}
auto vector = Cast<FeedbackVector>(maybe_vector);
FeedbackSlot literals_slot(FeedbackVector::ToSlot(literals_index));
CHECK(literals_slot.ToInt() < vector->length());
Handle<Object> literal_site(Cast<Object>(vector->Get(literals_slot)),
isolate);
Handle<AllocationSite> site;
Handle<JSObject> boilerplate;
if (HasBoilerplate(literal_site)) {
site = Cast<AllocationSite>(literal_site);
boilerplate = Handle<JSObject>(site->boilerplate(), isolate);
} else {
bool needs_initial_allocation_site =
(flags & AggregateLiteral::kNeedsInitialAllocationSite) != 0;
if (!needs_initial_allocation_site &&
IsUninitializedLiteralSite(*literal_site)) {
PreInitializeLiteralSite(vector, literals_slot);
return CreateLiteralWithoutAllocationSite<LiteralHelper>(
isolate, description, flags);
} else {
boilerplate = LiteralHelper::Create(isolate, description, flags,
AllocationType::kOld);
}
AllocationSiteCreationContext creation_context(isolate);
site = creation_context.EnterNewScope();
RETURN_ON_EXCEPTION(isolate, DeepWalk(boilerplate, &creation_context));
creation_context.ExitScope(site, boilerplate);
vector->SynchronizedSet(literals_slot, *site);
}
static_assert(static_cast<int>(ObjectLiteral::kDisableMementos) ==
static_cast<int>(ArrayLiteral::kDisableMementos));
bool enable_mementos = (flags & ObjectLiteral::kDisableMementos) == 0;
AllocationSiteUsageContext usage_context(isolate, site, enable_mementos);
usage_context.EnterNewScope();
MaybeDirectHandle<JSObject> copy = DeepCopy(boilerplate, &usage_context);
usage_context.ExitScope(site, boilerplate);
return copy;
}
}
RUNTIME_FUNCTION(Runtime_CreateObjectLiteral) {
HandleScope scope(isolate);
DCHECK_EQ(4, args.length());
Handle<HeapObject> maybe_vector = args.at<HeapObject>(0);
int literals_index = args.tagged_index_value_at(1);
Handle<ObjectBoilerplateDescription> description =
args.at<ObjectBoilerplateDescription>(2);
int flags = args.smi_value_at(3);
RETURN_RESULT_OR_FAILURE(
isolate, CreateLiteral<ObjectLiteralHelper>(
isolate, maybe_vector, literals_index, description, flags));
}
static DirectHandle<Object> InstantiateIfSharedFunctionInfo(
DirectHandle<Context> context, Isolate* isolate, DirectHandle<Object> value,
DirectHandle<ClosureFeedbackCellArray> feedback_cell_array,
int& current_slot) {
if (DirectHandle<SharedFunctionInfo> shared;
TryCast<SharedFunctionInfo>(value, &shared)) {
DirectHandle<FeedbackCell> feedback_cell(
feedback_cell_array->get(current_slot), isolate);
value = Factory::JSFunctionBuilder{isolate, shared, context}
.set_feedback_cell(feedback_cell)
.set_allocation_type(AllocationType::kYoung)
.Build();
++current_slot;
}
return value;
}
static MaybeDirectHandle<Object> SetPrototypePropertiesSlow(
Isolate* isolate, DirectHandle<Context> context, DirectHandle<JSAny> obj,
Handle<ObjectBoilerplateDescription> object_boilerplate_description,
DirectHandle<ClosureFeedbackCellArray> feedback_cell_array,
int& current_slot, int start_index = 0) {
MaybeDirectHandle<Object> result;
int length = object_boilerplate_description->boilerplate_properties_count();
for (int index = start_index; index < length; index++) {
DirectHandle<Object> proto;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, proto,
Runtime::GetObjectProperty(isolate, obj,
isolate->factory()->prototype_string()));
DirectHandle<Object> key(object_boilerplate_description->name(index),
isolate);
DirectHandle<Object> value(object_boilerplate_description->value(index),
isolate);
value = InstantiateIfSharedFunctionInfo(context, isolate, value,
feedback_cell_array, current_slot);
RETURN_ON_EXCEPTION(
isolate, Runtime::SetObjectProperty(isolate, Cast<JSAny>(proto), key,
value, StoreOrigin::kNamed));
result = value;
}
return result.ToHandleChecked();
}
static bool IsDefaultFunctionPrototype(DirectHandle<JSObject> js_proto,
Isolate* isolate) {
Tagged<Map> proto_map = js_proto->map();
if (!proto_map->is_extensible()) {
return false;
}
if (!proto_map->is_dictionary_map()) {
return false;
}
if (js_proto->property_dictionary()->NumberOfElements() != 1) {
return false;
}
if (js_proto->property_dictionary()
->FindEntry(isolate, isolate->factory()->constructor_string())
.is_not_found()) {
return false;
}
if (proto_map->prototype()->map() !=
*isolate->object_function_prototype_map()) {
return false;
}
return true;
}
RUNTIME_FUNCTION(Runtime_SetPrototypeProperties) {
HandleScope scope(isolate);
DCHECK_EQ(4, args.length());
DirectHandle<Context> context(isolate->context(), isolate);
DirectHandle<JSAny> obj = args.at<JSAny>(0);
Handle<ObjectBoilerplateDescription> object_boilerplate_description =
args.at<ObjectBoilerplateDescription>(1);
DirectHandle<ClosureFeedbackCellArray> feedback_cell_array =
args.at<ClosureFeedbackCellArray>(2);
int current_slot = args.smi_value_at(3);
if (!IsJSFunction(*obj)) {
RETURN_RESULT_OR_FAILURE(
isolate, SetPrototypePropertiesSlow(isolate, context, obj,
object_boilerplate_description,
feedback_cell_array, current_slot));
}
DirectHandle<JSFunction> acc_fun = Cast<JSFunction>(obj);
if (!acc_fun->has_prototype_slot()) {
RETURN_RESULT_OR_FAILURE(
isolate, SetPrototypePropertiesSlow(isolate, context, obj,
object_boilerplate_description,
feedback_cell_array, current_slot));
}
DirectHandle<Object> prototype =
JSFunction::GetFunctionPrototype(isolate, acc_fun);
DCHECK_EQ(*prototype,
*Runtime::GetObjectProperty(isolate, obj,
isolate->factory()->prototype_string())
.ToHandleChecked());
if (IsNull(*prototype)) {
RETURN_RESULT_OR_FAILURE(
isolate, SetPrototypePropertiesSlow(isolate, context, obj,
object_boilerplate_description,
feedback_cell_array, current_slot));
}
DirectHandle<JSObject> js_proto;
if (!TryCast<JSObject>(prototype, &js_proto)) {
RETURN_RESULT_OR_FAILURE(
isolate, SetPrototypePropertiesSlow(isolate, context, obj,
object_boilerplate_description,
feedback_cell_array, current_slot));
}
if (IsSpecialReceiverMap(js_proto->map())) {
RETURN_RESULT_OR_FAILURE(
isolate, SetPrototypePropertiesSlow(isolate, context, obj,
object_boilerplate_description,
feedback_cell_array, current_slot));
}
if (!JSObject::IsExtensible(isolate, js_proto)) {
RETURN_RESULT_OR_FAILURE(
isolate, SetPrototypePropertiesSlow(isolate, context, obj,
object_boilerplate_description,
feedback_cell_array, current_slot));
}
bool is_default_func_prototype =
IsDefaultFunctionPrototype(js_proto, isolate);
MaybeDirectHandle<Object> result;
int length = object_boilerplate_description->boilerplate_properties_count();
if (is_default_func_prototype) {
for (int index = 0; index < length; index++) {
DirectHandle<Object> key(object_boilerplate_description->name(index),
isolate);
DirectHandle<Object> value(object_boilerplate_description->value(index),
isolate);
value = InstantiateIfSharedFunctionInfo(
context, isolate, value, feedback_cell_array, current_slot);
DirectHandle<String> name = Cast<String>(key);
DCHECK(!name->IsArrayIndex());
DCHECK(!IsTheHole(*value));
LookupIterator it(isolate, js_proto, name, LookupIterator::OWN);
Object::TransitionAndWriteDataProperty(&it, value, NONE, Just(kDontThrow),
StoreOrigin::kNamed)
.Check();
result = value;
}
} else {
for (int index = 0; index < length; index++) {
PropertyDescriptor desc;
DirectHandle<Object> key(object_boilerplate_description->name(index),
isolate);
DirectHandle<Object> value(object_boilerplate_description->value(index),
isolate);
CHECK(IsName(*key));
PropertyKey lookup_key(isolate, key);
LookupIterator it(isolate, js_proto, lookup_key,
LookupIterator::PROTOTYPE_CHAIN);
LookupIterator::State it_state = it.state();
if (it_state != LookupIterator::NOT_FOUND &&
(it_state != LookupIterator::DATA || it.IsReadOnly())) {
RETURN_RESULT_OR_FAILURE(
isolate, SetPrototypePropertiesSlow(
isolate, context, obj, object_boilerplate_description,
feedback_cell_array, current_slot, index));
}
DCHECK(!IsTheHole(*value));
value = InstantiateIfSharedFunctionInfo(
context, isolate, value, feedback_cell_array, current_slot);
if (it_state == LookupIterator::DATA &&
it.HolderIsReceiverOrHiddenPrototype()) {
Object::SetDataProperty(&it, value).Check();
} else {
Object::TransitionAndWriteDataProperty(
&it, value, NONE, Just(kDontThrow), StoreOrigin::kNamed)
.Check();
}
result = value;
}
}
return *result.ToHandleChecked();
}
RUNTIME_FUNCTION(Runtime_CreateArrayLiteral) {
HandleScope scope(isolate);
DCHECK_EQ(4, args.length());
Handle<HeapObject> maybe_vector = args.at<HeapObject>(0);
int literals_index = args.tagged_index_value_at(1);
Handle<ArrayBoilerplateDescription> elements =
args.at<ArrayBoilerplateDescription>(2);
int flags = args.smi_value_at(3);
RETURN_RESULT_OR_FAILURE(
isolate, CreateLiteral<ArrayLiteralHelper>(
isolate, maybe_vector, literals_index, elements, flags));
}
RUNTIME_FUNCTION(Runtime_CreateRegExpLiteral) {
HandleScope scope(isolate);
DCHECK_EQ(4, args.length());
Handle<HeapObject> maybe_vector = args.at<HeapObject>(0);
int index = args.tagged_index_value_at(1);
DirectHandle<String> pattern = args.at<String>(2);
int flags = args.smi_value_at(3);
if (IsUndefined(*maybe_vector)) {
RETURN_RESULT_OR_FAILURE(
isolate, JSRegExp::New(isolate, pattern, JSRegExp::Flags(flags)));
}
auto vector = Cast<FeedbackVector>(maybe_vector);
FeedbackSlot literal_slot(FeedbackVector::ToSlot(index));
DirectHandle<Object> literal_site(Cast<Object>(vector->Get(literal_slot)),
isolate);
CHECK(!HasBoilerplate(literal_site));
DirectHandle<JSRegExp> regexp_instance;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, regexp_instance,
JSRegExp::New(isolate, pattern, JSRegExp::Flags(flags)));
if (IsUninitializedLiteralSite(*literal_site)) {
PreInitializeLiteralSite(vector, literal_slot);
return *regexp_instance;
}
DirectHandle<RegExpData> data(regexp_instance->data(isolate), isolate);
DirectHandle<String> source(Cast<String>(regexp_instance->source()), isolate);
DirectHandle<RegExpBoilerplateDescription> boilerplate =
isolate->factory()->NewRegExpBoilerplateDescription(
data, source,
Smi::FromInt(static_cast<int>(regexp_instance->flags())));
vector->SynchronizedSet(literal_slot, *boilerplate);
DCHECK(HasBoilerplate(
direct_handle(Cast<Object>(vector->Get(literal_slot)), isolate)));
return *regexp_instance;
}
}
}