/
githubmirror
/
node
Обзор
Документация
Войти
/
githubmirror
/
node
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
deps/v8/src/runtime/runtime-literals.cc
951 строка
37 KB
Michaël Zasso
deps: update V8 to 14.6.202.33
24 апр 2026, 19:01
Не верифицирован
24 апр 2026, 19:01
f1e0b83
Код
Авторство
О чём код?
// Copyright 2014 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/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) { // Dont create allocation sites for nested object literals 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) { // JSFunction objects are not allowed to be in normal boilerplates at all. 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); // Deep copy own properties. Arrays only have 1 property "length". 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); } } } // Assume non-arrays don't end up having elements. if (copy->elements(isolate)->length() == 0) return copy; } // Deep copy own elements. 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 // Typed elements cannot be created using an object literal. UNREACHABLE(); case PACKED_SMI_ELEMENTS: case HOLEY_SMI_ELEMENTS: case PACKED_DOUBLE_ELEMENTS: case HOLEY_DOUBLE_ELEMENTS: case NO_ELEMENTS: // No contained objects, nothing to do. 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_; }; // AllocationSiteCreationContext aids in the creation of AllocationSites to // accompany object literals. class AllocationSiteCreationContext : public AllocationSiteContext { public: explicit AllocationSiteCreationContext(Isolate* isolate) : AllocationSiteContext(isolate) {} Handle<AllocationSite> EnterNewScope() { Handle<AllocationSite> scope_site; if (top().is_null()) { // We are creating the top level AllocationSite as opposed to a nested // AllocationSite. 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; // In case we have function literals, we want the object to be in // slow properties mode for now. We don't go in the map cache because // maps with constant functions can't be shared if the functions are // not the same (which is the common case). int number_of_properties = object_boilerplate_description->backing_store_size(); // Ignoring number_of_properties for force dictionary map with // __proto__:null. 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); // Normalize the elements of the boilerplate to save space if needed. if (!use_fast_elements) JSObject::NormalizeElements(isolate, boilerplate); // Add the constant properties to the boilerplate. int length = object_boilerplate_description->boilerplate_properties_count(); // TODO(verwaest): Support tracking representations in the boilerplate. 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)) { // Array index (uint32). 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); // Create the JSArray. 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 { // Eagerly create AllocationSites for literals that contain an Array. 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); } // Install AllocationSite objects. 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; // Copy the existing boilerplate. AllocationSiteUsageContext usage_context(isolate, site, enable_mementos); usage_context.EnterNewScope(); MaybeDirectHandle<JSObject> copy = DeepCopy(boilerplate, &usage_context); usage_context.ExitScope(site, boilerplate); return copy; } DirectHandle<Object> InstantiateIfSharedFunctionInfo( DirectHandle<Context> context, Isolate* isolate, DirectHandle<JSObject> js_proto, DirectHandle<Object> value, DirectHandle<ClosureFeedbackCellArray> feedback_cell_array, Handle<ObjectBoilerplateDescription> object_boilerplate_description, int start_slot, int& current_slot) { DirectHandle<SharedFunctionInfo> shared; if (!TryCast<SharedFunctionInfo>(value, &shared)) { return value; } if (!v8_flags.proto_assign_seq_lazy_func_opt || !base::IsInRange(current_slot, 0, kMaxUInt16)) { 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; } DirectHandle<Map> proto_map = direct_handle(js_proto->map(), isolate); if (Tagged<PrototypeSharedClosureInfo> closure_info; proto_map->TryGetPrototypeSharedClosureInfo(&closure_info)) { // We already have closure infos on this prototype, this means we // already called SetPrototypeProperties on it and some closures were // set up. We can only take the lazy closure path if the context // is the same. if (closure_info->context() == *context && *object_boilerplate_description == closure_info->boilerplate_description()) { // fast path shared->set_feedback_slot(current_slot); } else { // not lazy allocation 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(); } } else { // We do not have closure_info auto val = *isolate->factory()->NewPrototypeSharedClosureInfo( object_boilerplate_description, context, feedback_cell_array); proto_map->SetPrototypeSharedClosureInfo(val); shared->set_feedback_slot(current_slot); } ++current_slot; return value; } } // namespace 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 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); 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(); } 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) { // Object function prototype's map. Tagged<Map> proto_map = js_proto->map(); // Check that given function.prototype object has a default initial state: // it's extensible. if (!proto_map->is_extensible()) { return false; } // it's in dictionary mode. if (!proto_map->is_dictionary_map()) { return false; } // it has exactly one "constructor" property installed. 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; } // its prototype is the original and unmodified Object.prototype object. 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); // acc JS Object 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); int start_slot = current_slot; // Proxy and any non-function not welcome 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); // It should now be safe to perform a fast merge 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, js_proto, value, feedback_cell_array, object_boilerplate_description, start_slot, current_slot); DirectHandle<String> name = Cast<String>(key); DCHECK(!name->IsArrayIndex()); DCHECK(!IsTheHole(*value)); LookupIterator it(isolate, js_proto, name, LookupIterator::OWN); if (IsSharedFunctionInfo(*value)) { DirectHandle<AccessorInfo> accessor_info = isolate->factory()->lazy_closure_accessor(); JSObject::SetAccessor(js_proto, name, accessor_info, PropertyAttributes::NONE) .Check(); } else { Object::TransitionAndWriteDataProperty( &it, value, NONE, Just(kDontThrow), StoreOrigin::kNamed) .Check(); } result = value; } } else { // Make sure None of the keys we are writing to are setters/getters // TODO(rherouart): if prototype is empty we can skip these checks 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)); if (it_state == LookupIterator::DATA && it.HolderIsReceiverOrHiddenPrototype()) { DirectHandle<SharedFunctionInfo> shared; if (TryCast<SharedFunctionInfo>(value, &shared)) { // If we were to set an existing property to a SharedFunctionInfo, // there would be the risk of it being returned from IC without being // instantiated. 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++; } Object::SetDataProperty(&it, value).Check(); } else { value = InstantiateIfSharedFunctionInfo( context, isolate, js_proto, value, feedback_cell_array, object_boilerplate_description, start_slot, current_slot); if (IsSharedFunctionInfo(*value)) { DirectHandle<AccessorInfo> accessor_info = isolate->factory()->lazy_closure_accessor(); JSObject::SetAccessor(js_proto, Cast<Name>(key), accessor_info, PropertyAttributes::NONE) .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)) { // We don't have a vector; don't create a boilerplate, simply construct a // plain JSRegExp instance and return it. 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); // This function must not be called when a boilerplate already exists (if it // exists, callers should instead copy the boilerplate into a new JSRegExp // instance). CHECK(!HasBoilerplate(literal_site)); DirectHandle<JSRegExp> regexp_instance; ASSIGN_RETURN_FAILURE_ON_EXCEPTION( isolate, regexp_instance, JSRegExp::New(isolate, pattern, JSRegExp::Flags(flags))); // JSRegExp literal sites are initialized in a two-step process: // Uninitialized-Preinitialized, and Preinitialized-Initialized. 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; } } // namespace internal } // namespace v8