current commit
261007.21 · deps: update V8 to 15.5.35.20
// Copyright 2015 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/api/api-natives.h"

#include "src/api/api-inl.h"
#include "src/common/globals.h"
#include "src/common/message-template.h"
#include "src/execution/isolate-inl.h"
#include "src/execution/protectors-inl.h"
#include "src/flags/flags.h"
#include "src/heap/heap-inl.h"
#include "src/logging/runtime-call-stats-scope.h"
#include "src/objects/api-callbacks.h"
#include "src/objects/lookup.h"
#include "src/objects/templates.h"

namespace v8 {
namespace internal {

namespace {

class V8_NODISCARD InvokeScope {
 public:
  explicit InvokeScope(Isolate* isolate)
      : isolate_(isolate), save_context_(isolate) {}
  ~InvokeScope() {
    bool has_exception = isolate_->has_exception();
    if (has_exception) {
      isolate_->ReportPendingMessages();
    } else {
      isolate_->clear_pending_message();
    }
  }

 private:
  Isolate* isolate_;
  SaveContext save_context_;
};

MaybeHandle<JSObject> InstantiateObject(Isolate* isolate,
                                        DirectHandle<ObjectTemplateInfo> data,
                                        DirectHandle<JSReceiver> new_target,
                                        bool is_prototype);

MaybeHandle<JSFunction> InstantiateFunction(
    Isolate* isolate, DirectHandle<NativeContext> native_context,
    DirectHandle<FunctionTemplateInfo> data,
    MaybeDirectHandle<Name> maybe_name = {});

MaybeHandle<JSFunction> InstantiateFunction(
    Isolate* isolate, DirectHandle<FunctionTemplateInfo> data,
    MaybeDirectHandle<Name> maybe_name = {}) {
  return InstantiateFunction(isolate, isolate->native_context(), data,
                             maybe_name);
}

MaybeDirectHandle<Object> Instantiate(Isolate* isolate,
                                      DirectHandle<Object> data,
                                      MaybeDirectHandle<Name> maybe_name = {}) {
  if (IsFunctionTemplateInfo(*data)) {
    return InstantiateFunction(isolate, Cast<FunctionTemplateInfo>(data),
                               maybe_name);
  } else if (IsObjectTemplateInfo(*data)) {
    return InstantiateObject(isolate, Cast<ObjectTemplateInfo>(data), {},
                             false);
  } else {
    return data;
  }
}

MaybeDirectHandle<Object> DefineAccessorProperty(
    Isolate* isolate, DirectHandle<JSObject> object, DirectHandle<Name> name,
    DirectHandle<Object> getter, DirectHandle<Object> setter,
    PropertyAttributes attributes) {
  DCHECK_IMPLIES(IsFunctionTemplateInfo(*getter),
                 Cast<FunctionTemplateInfo>(*getter)->is_cacheable());
  DCHECK_IMPLIES(IsFunctionTemplateInfo(*setter),
                 Cast<FunctionTemplateInfo>(*setter)->is_cacheable());
  if (IsFunctionTemplateInfo(*getter) &&
      Cast<FunctionTemplateInfo>(*getter)->BreakAtEntry(isolate)) {
    ASSIGN_RETURN_ON_EXCEPTION(
        isolate, getter,
        InstantiateFunction(isolate, Cast<FunctionTemplateInfo>(getter)));
    DirectHandle<Code> trampoline = BUILTIN_CODE(isolate, DebugBreakTrampoline);
    Cast<JSFunction>(getter)->UpdateCode(isolate, *trampoline);
  }
  if (IsFunctionTemplateInfo(*setter) &&
      Cast<FunctionTemplateInfo>(*setter)->BreakAtEntry(isolate)) {
    ASSIGN_RETURN_ON_EXCEPTION(
        isolate, setter,
        InstantiateFunction(isolate, Cast<FunctionTemplateInfo>(setter)));
    DirectHandle<Code> trampoline = BUILTIN_CODE(isolate, DebugBreakTrampoline);
    Cast<JSFunction>(setter)->UpdateCode(isolate, *trampoline);
  }
  RETURN_ON_EXCEPTION(isolate, JSObject::DefineOwnAccessorIgnoreAttributes(
                                   object, name, getter, setter, attributes));
  return object;
}

MaybeDirectHandle<Object> DefineDataProperty(Isolate* isolate,
                                             DirectHandle<JSObject> object,
                                             DirectHandle<Name> name,
                                             DirectHandle<Object> prop_data,
                                             PropertyAttributes attributes) {
  DirectHandle<Object> value;
  ASSIGN_RETURN_ON_EXCEPTION(isolate, value,
                             Instantiate(isolate, prop_data, name));

  PropertyKey key(isolate, name);
  LookupIterator it(isolate, object, key, LookupIterator::OWN_SKIP_INTERCEPTOR);

#ifdef DEBUG
  Maybe<PropertyAttributes> maybe = JSReceiver::GetPropertyAttributes(&it);
  DCHECK(maybe.IsJust());
  if (it.IsFound()) {
    THROW_NEW_ERROR(
        isolate,
        NewTypeError(MessageTemplate::kDuplicateTemplateProperty, name));
  }
#endif

  MAYBE_RETURN_NULL(Object::AddDataProperty(&it, value, attributes,
                                            Just(ShouldThrow::kThrowOnError),
                                            StoreOrigin::kNamed));
  return value;
}

void DisableAccessChecks(Isolate* isolate, DirectHandle<JSObject> object) {
  DirectHandle<Map> old_map(object->map(), isolate);
  // Copy map so it won't interfere constructor's initial map.
  DirectHandle<Map> new_map =
      Map::Copy(isolate, old_map, "DisableAccessChecks");
  new_map->set_is_access_check_needed(false);
  JSObject::MigrateToMap(isolate, object, new_map);
}

void EnableAccessChecks(Isolate* isolate, DirectHandle<JSObject> object) {
  DirectHandle<Map> old_map(object->map(), isolate);
  // Copy map so it won't interfere constructor's initial map.
  DirectHandle<Map> new_map = Map::Copy(isolate, old_map, "EnableAccessChecks");
  new_map->set_is_access_check_needed(true);
  new_map->set_may_have_interesting_properties(true);
  JSObject::MigrateToMap(isolate, object, new_map);
}

class V8_NODISCARD AccessCheckDisableScope {
 public:
  AccessCheckDisableScope(Isolate* isolate, DirectHandle<JSObject> obj)
      : isolate_(isolate),
        disabled_(obj->map()->is_access_check_needed()),
        obj_(obj) {
    if (disabled_) {
      DisableAccessChecks(isolate_, obj_);
    }
  }
  ~AccessCheckDisableScope() {
    if (disabled_) {
      EnableAccessChecks(isolate_, obj_);
    }
  }

 private:
  Isolate* isolate_;
  const bool disabled_;
  DirectHandle<JSObject> obj_;
};

Tagged<Object> GetIntrinsic(Isolate* isolate, v8::Intrinsic intrinsic) {
  DirectHandle<Context> native_context = isolate->native_context();
  DCHECK(!native_context.is_null());
  switch (intrinsic) {
#define GET_INTRINSIC_VALUE(name, iname) \
  case v8::k##name:                      \
    return native_context->iname();
    V8_INTRINSICS_LIST(GET_INTRINSIC_VALUE)
#undef GET_INTRINSIC_VALUE
  }
  return Tagged<Object>();
}

template <typename TemplateInfoT>
MaybeHandle<JSObject> ConfigureInstance(Isolate* isolate, Handle<JSObject> obj,
                                        DirectHandle<TemplateInfoT> data) {
  RCS_SCOPE(isolate, RuntimeCallCounterId::kConfigureInstance);
  HandleScope scope(isolate);
  // Disable access checks while instantiating the object.
  AccessCheckDisableScope access_check_scope(isolate, obj);

  // Walk the inheritance chain and copy all accessors to current object.
  int max_number_of_properties = 0;
  Tagged<TemplateInfoT> info = *data;
  while (!info.is_null()) {
    Tagged<Object> props = info->property_accessors();
    if (!IsUndefined(props)) {
      max_number_of_properties += Cast<ArrayList>(props)->length();
    }
    info = info->GetParent(isolate);
  }

  if (max_number_of_properties > 0) {
    int valid_descriptors = 0;
    // Use a temporary FixedArray to accumulate unique accessors.
    DirectHandle<FixedArray> array =
        isolate->factory()->NewFixedArray(max_number_of_properties);

    // TODO(leszeks): Avoid creating unnecessary handles for cases where we
    // don't need to append anything.
    for (DirectHandle<TemplateInfoT> temp(*data, isolate); !(*temp).is_null();
         temp = direct_handle(temp->GetParent(isolate), isolate)) {
      // Accumulate accessors.
      Tagged<Object> maybe_properties = temp->property_accessors();
      if (!IsUndefined(maybe_properties)) {
        valid_descriptors = AccessorInfo::AppendUnique(
            isolate, direct_handle(maybe_properties, isolate), array,
            valid_descriptors);
      }
    }

    // Install accumulated accessors.
    for (int i = 0; i < valid_descriptors; i++) {
      DirectHandle<AccessorInfo> accessor(Cast<AccessorInfo>(array->get(i)),
                                          isolate);
      DirectHandle<Name> name(Cast<Name>(accessor->name()), isolate);
      JSObject::SetAccessor(obj, name, accessor,
                            accessor->initial_property_attributes())
          .Assert();
    }
  }

  Tagged<Object> maybe_property_list = data->property_list();
  if (IsUndefined(maybe_property_list)) return obj;
  DirectHandle<ArrayList> properties(Cast<ArrayList>(maybe_property_list),
                                     isolate);

  // The properties list consists of tuples describing each property:
  //  - <name, property details, property value> (data property),
  //  - <name, property details, getter, setter> (accessor property),
  //  - <name, intrinsic marker, property details, property value>
  //    (intrinsic data property).
  // See ApiNatives::AddDataProperty()/AddAccessorProperty() for details.
  int properties_length = properties->length();
  if (properties_length == 0) return obj;

  int properties_count = 0;
  for (int i = 0; i < properties_length;) {
    auto name = direct_handle(Cast<Name>(properties->get(i++)), isolate);
    Tagged<Object> bit = properties->get(i++);
    if (IsSmi(bit)) {
      PropertyDetails details(Cast<Smi>(bit));
      PropertyAttributes attributes = details.attributes();
      PropertyKind kind = details.kind();

      if (kind == PropertyKind::kData) {
        auto prop_data = handle(properties->get(i++), isolate);
        RETURN_ON_EXCEPTION(isolate, DefineDataProperty(isolate, obj, name,
                                                        prop_data, attributes));
      } else {
        auto getter = direct_handle(properties->get(i++), isolate);
        auto setter = direct_handle(properties->get(i++), isolate);
        RETURN_ON_EXCEPTION(
            isolate, DefineAccessorProperty(isolate, obj, name, getter, setter,
                                            attributes));
      }
    } else {
      // Intrinsic data property --- Get appropriate value from the current
      // context.
      PropertyDetails details(Cast<Smi>(properties->get(i++)));
      PropertyAttributes attributes = details.attributes();
      DCHECK_EQ(PropertyKind::kData, details.kind());

      v8::Intrinsic intrinsic =
          static_cast<v8::Intrinsic>(Smi::ToInt(properties->get(i++)));
      auto prop_data = handle(GetIntrinsic(isolate, intrinsic), isolate);

      RETURN_ON_EXCEPTION(isolate, DefineDataProperty(isolate, obj, name,
                                                      prop_data, attributes));
    }
    DCHECK_LE(i, properties_length);
    properties_count++;
  }
  CHECK_EQ(properties_count, data->number_of_properties());
  return obj;
}

bool IsSimpleInstantiation(Isolate* isolate, Tagged<ObjectTemplateInfo> info,
                           Tagged<JSReceiver> new_target) {
  DisallowGarbageCollection no_gc;

  if (!IsJSFunction(new_target)) return false;
  Tagged<JSFunction> fun = Cast<JSFunction>(new_target);
  if (!fun->shared()->IsApiFunction()) return false;
  if (fun->shared()->api_func_data() != info->constructor()) return false;
  if (info->immutable_proto()) return false;
  return fun->native_context() == isolate->raw_native_context();
}

MaybeHandle<JSObject> InstantiateObject(Isolate* isolate,
                                        DirectHandle<ObjectTemplateInfo> info,
                                        DirectHandle<JSReceiver> new_target,
                                        bool is_prototype) {
  RCS_SCOPE(isolate, RuntimeCallCounterId::kInstantiateObject);
  DirectHandle<JSFunction> constructor;
  bool should_cache = info->is_cacheable();
  if (!new_target.is_null()) {
    if (IsSimpleInstantiation(isolate, *info, *new_target)) {
      constructor = Cast<JSFunction>(new_target);
    } else {
      // Disable caching for subclass instantiation.
      should_cache = false;
    }
  }
  // Fast path.
  Handle<JSObject> result;
  if (should_cache) {
    if (TemplateInfo::ProbeInstantiationsCache<JSObject>(
            isolate, isolate->native_context(), info,
            TemplateInfo::CachingMode::kLimited)
            .ToHandle(&result)) {
      return isolate->factory()->CopyJSObject(result);
    }
  }

  if (constructor.is_null()) {
    Tagged<Object> maybe_constructor_info = info->constructor();
    if (IsUndefined(maybe_constructor_info)) {
      constructor = isolate->object_function();
    } else {
      // Enter a new scope.  Recursion could otherwise create a lot of handles.
      HandleScope scope(isolate);
      DirectHandle<FunctionTemplateInfo> cons_templ(
          Cast<FunctionTemplateInfo>(maybe_constructor_info), isolate);
      DirectHandle<JSFunction> tmp_constructor;
      ASSIGN_RETURN_ON_EXCEPTION(isolate, tmp_constructor,
                                 InstantiateFunction(isolate, cons_templ));
      constructor = scope.CloseAndEscape(tmp_constructor);
    }

    if (new_target.is_null()) new_target = constructor;
  }

  const auto new_js_object_type =
      constructor->has_initial_map() &&
              IsJSApiWrapperObjectMap(constructor->initial_map())
          ? NewJSObjectType::kMaybeEmbedderFieldsAndApiWrapper
          : NewJSObjectType::kMaybeEmbedderFieldsAndNoApiWrapper;
  Handle<JSObject> object;
  ASSIGN_RETURN_ON_EXCEPTION(
      isolate, object,
      JSObject::New(constructor, new_target, {}, new_js_object_type));

  if (is_prototype) JSObject::OptimizeAsPrototype(object);

  ASSIGN_RETURN_ON_EXCEPTION(isolate, result,
                             ConfigureInstance(isolate, object, info));
  if (info->immutable_proto()) {
    JSObject::SetImmutableProto(isolate, object);
  }
  if (!is_prototype) {
    // Keep prototypes in slow-mode. Let them be lazily turned fast later on.
    // TODO(dcarney): is this necessary?
    JSObject::MigrateSlowToFast(result, 0, "ApiNatives::InstantiateObject");
    // Don't cache prototypes.
    if (should_cache) {
      TemplateInfo::CacheTemplateInstantiation(
          isolate, isolate->native_context(), info,
          TemplateInfo::CachingMode::kLimited, result);
      result = isolate->factory()->CopyJSObject(result);
    }
  }

  return result;
}

namespace {
MaybeDirectHandle<Object> GetInstancePrototype(
    Isolate* isolate, DirectHandle<Object> function_template) {
  // Enter a new scope.  Recursion could otherwise create a lot of handles.
  HandleScope scope(isolate);
  DirectHandle<JSFunction> parent_instance;
  ASSIGN_RETURN_ON_EXCEPTION(
      isolate, parent_instance,
      InstantiateFunction(isolate,
                          Cast<FunctionTemplateInfo>(function_template)));
  Handle<Object> instance_prototype;
  // TODO(cbruni): decide what to do here.
  ASSIGN_RETURN_ON_EXCEPTION(
      isolate, instance_prototype,
      JSReceiver::GetProperty(isolate, parent_instance,
                              isolate->factory()->prototype_string()));
  return scope.CloseAndEscape(instance_prototype);
}
}  // namespace

MaybeHandle<JSFunction> InstantiateFunction(
    Isolate* isolate, DirectHandle<NativeContext> native_context,
    DirectHandle<FunctionTemplateInfo> info,
    MaybeDirectHandle<Name> maybe_name) {
  RCS_SCOPE(isolate, RuntimeCallCounterId::kInstantiateFunction);
  bool should_cache = info->is_cacheable();
  if (should_cache) {
    Handle<JSObject> result;
    if (TemplateInfo::ProbeInstantiationsCache<JSObject>(
            isolate, native_context, info,
            TemplateInfo::CachingMode::kUnlimited)
            .ToHandle(&result)) {
      return Cast<JSFunction>(result);
    }
  }
  DirectHandle<Object> prototype;
  if (!info->remove_prototype()) {
    DirectHandle<Object> prototype_templ(info->GetPrototypeTemplate(), isolate);
    if (IsUndefined(*prototype_templ)) {
      DirectHandle<Object> protoype_provider_templ(
          info->GetPrototypeProviderTemplate(), isolate);
      if (IsUndefined(*protoype_provider_templ)) {
        prototype = isolate->factory()->NewJSObject(
            direct_handle(native_context->object_function(), isolate));
      } else {
        ASSIGN_RETURN_ON_EXCEPTION(
            isolate, prototype,
            GetInstancePrototype(isolate, protoype_provider_templ));
      }
    } else {
      ASSIGN_RETURN_ON_EXCEPTION(
          isolate, prototype,
          InstantiateObject(isolate, Cast<ObjectTemplateInfo>(prototype_templ),
                            DirectHandle<JSReceiver>(), true));
    }
    DirectHandle<Object> parent(info->GetParentTemplate(), isolate);
    if (!IsUndefined(*parent)) {
      DirectHandle<Object> parent_prototype;
      ASSIGN_RETURN_ON_EXCEPTION(isolate, parent_prototype,
                                 GetInstancePrototype(isolate, parent));
      DirectHandle<JSPrototype> checked_parent_prototype;
      CHECK(TryCast(parent_prototype, &checked_parent_prototype));
      JSObject::ForceSetPrototype(isolate, Cast<JSObject>(prototype),
                                  checked_parent_prototype);
    }
  }
  InstanceType function_type = JS_SPECIAL_API_OBJECT_TYPE;
  if (!info->needs_access_check() &&
      IsUndefined(info->GetNamedPropertyHandler()) &&
      IsUndefined(info->GetIndexedPropertyHandler())) {
    function_type = JS_API_OBJECT_TYPE;
    DCHECK(InstanceTypeChecker::IsJSApiObject(function_type));
  }

  Handle<JSFunction> function = ApiNatives::CreateApiFunction(
      isolate, native_context, info, prototype, function_type, maybe_name);
  if (should_cache) {
    // Cache the function.
    TemplateInfo::CacheTemplateInstantiation(
        isolate, native_context, info, TemplateInfo::CachingMode::kUnlimited,
        function);
  }
  MaybeDirectHandle<JSObject> result =
      ConfigureInstance(isolate, function, info);
  if (result.is_null()) {
    // Uncache on error.
    TemplateInfo::UncacheTemplateInstantiation(
        isolate, native_context, info, TemplateInfo::CachingMode::kUnlimited);
    return {};
  }
  info->set_published(true);
  return function;
}

void AddPropertyToPropertyList(Isolate* isolate,
                               DirectHandle<TemplateInfoWithProperties> info,
                               base::Vector<DirectHandle<Object>> data) {
  Tagged<Object> maybe_list = info->property_list();
  DirectHandle<ArrayList> list;
  if (IsUndefined(maybe_list)) {
    list = ArrayList::New(isolate, static_cast<int>(data.size()),
                          AllocationType::kOld);
  } else {
    list = direct_handle(Cast<ArrayList>(maybe_list), isolate);
  }
  info->set_number_of_properties(info->number_of_properties() + 1);
  for (DirectHandle<Object> value : data) {
    if (value.is_null()) {
      value = Cast<Object>(isolate->factory()->undefined_value());
    }
    list = ArrayList::Add(isolate, list, value);
  }
  info->set_property_list(*list);
}

}  // namespace

// static
DirectHandle<FunctionTemplateInfo>
ApiNatives::CreateAccessorFunctionTemplateInfo(
    Isolate* i_isolate, FunctionCallback callback, int length,
    SideEffectType side_effect_type) {
  // TODO(v8:5962): move FunctionTemplateNew() from api.cc here.
  auto isolate = reinterpret_cast<v8::Isolate*>(i_isolate);
  Local<FunctionTemplate> func_template = FunctionTemplate::New(
      isolate, callback, v8::Local<Value>{}, v8::Local<v8::Signature>{}, length,
      v8::ConstructorBehavior::kThrow, side_effect_type);
  return Utils::OpenDirectHandle(*func_template);
}

MaybeHandle<JSFunction> ApiNatives::InstantiateFunction(
    Isolate* isolate, DirectHandle<NativeContext> native_context,
    DirectHandle<FunctionTemplateInfo> data,
    MaybeDirectHandle<Name> maybe_name) {
  InvokeScope invoke_scope(isolate);
  return ::i::InstantiateFunction(isolate, native_context, data, maybe_name);
}

MaybeHandle<JSFunction> ApiNatives::InstantiateFunction(
    Isolate* isolate, DirectHandle<FunctionTemplateInfo> data,
    MaybeDirectHandle<Name> maybe_name) {
  InvokeScope invoke_scope(isolate);
  return ::i::InstantiateFunction(isolate, data, maybe_name);
}

MaybeHandle<JSObject> ApiNatives::InstantiateObject(
    Isolate* isolate, DirectHandle<ObjectTemplateInfo> data,
    DirectHandle<JSReceiver> new_target) {
  InvokeScope invoke_scope(isolate);
  return ::i::InstantiateObject(isolate, data, new_target, false);
}

MaybeHandle<JSObject> ApiNatives::InstantiateRemoteObject(
    DirectHandle<ObjectTemplateInfo> data) {
  Isolate* isolate = Isolate::Current();
  InvokeScope invoke_scope(isolate);

  DirectHandle<FunctionTemplateInfo> constructor(
      Cast<FunctionTemplateInfo>(data->constructor()), isolate);
  DirectHandle<Map> object_map = isolate->factory()->NewContextlessMap(
      JS_SPECIAL_API_OBJECT_TYPE,
      JSSpecialObject::kHeaderSize +
          data->embedder_field_count() * kEmbedderDataSlotSize,
      TERMINAL_FAST_ELEMENTS_KIND);
  object_map->SetConstructor(*constructor);
  object_map->set_is_access_check_needed(true);
  object_map->set_may_have_interesting_properties(true);

  Handle<JSObject> object = isolate->factory()->NewJSObjectFromMap(
      object_map, AllocationType::kYoung, DirectHandle<AllocationSite>::null(),
      NewJSObjectType::kMaybeEmbedderFieldsAndApiWrapper);
  JSObject::ForceSetPrototype(isolate, object,
                              isolate->factory()->null_value());

  return object;
}

void ApiNatives::AddDataProperty(Isolate* isolate,
                                 DirectHandle<TemplateInfoWithProperties> info,
                                 DirectHandle<Name> name,
                                 DirectHandle<Object> value,
                                 PropertyAttributes attributes) {
  if (IsString(*name)) {
    name = isolate->factory()->InternalizeString(Cast<String>(name));
  }
  PropertyDetails details(PropertyKind::kData, attributes,
                          PropertyConstness::kMutable);
  DirectHandle<Object> data[] = {name, direct_handle(details.AsSmi(), isolate),
                                 value};
  AddPropertyToPropertyList(isolate, info, base::VectorOf(data));
}

void ApiNatives::AddDataProperty(Isolate* isolate,
                                 DirectHandle<TemplateInfoWithProperties> info,
                                 DirectHandle<Name> name,
                                 v8::Intrinsic intrinsic,
                                 PropertyAttributes attributes) {
  if (IsString(*name)) {
    name = isolate->factory()->InternalizeString(Cast<String>(name));
  }
  auto value = direct_handle(Smi::FromInt(intrinsic), isolate);
  auto intrinsic_marker = isolate->factory()->true_value();
  PropertyDetails details(PropertyKind::kData, attributes,
                          PropertyConstness::kMutable);
  DirectHandle<Object> data[] = {
      name, intrinsic_marker, direct_handle(details.AsSmi(), isolate), value};
  AddPropertyToPropertyList(isolate, info, base::VectorOf(data));
}

void ApiNatives::AddAccessorProperty(
    Isolate* isolate, DirectHandle<TemplateInfoWithProperties> info,
    DirectHandle<Name> name, DirectHandle<FunctionTemplateInfo> getter,
    DirectHandle<FunctionTemplateInfo> setter, PropertyAttributes attributes) {
  if (IsString(*name)) {
    name = isolate->factory()->InternalizeString(Cast<String>(name));
  }
  if (!getter.is_null()) getter->set_published(true);
  if (!setter.is_null()) setter->set_published(true);
  PropertyDetails details(PropertyKind::kAccessor, attributes,
                          PropertyConstness::kMutable);
  DirectHandle<Object> data[] = {name, direct_handle(details.AsSmi(), isolate),
                                 getter, setter};
  AddPropertyToPropertyList(isolate, info, base::VectorOf(data));
}

void ApiNatives::AddNativeDataProperty(
    Isolate* isolate, DirectHandle<TemplateInfoWithProperties> info,
    DirectHandle<AccessorInfo> property) {
  Tagged<Object> maybe_list = info->property_accessors();
  DirectHandle<ArrayList> list;
  if (IsUndefined(maybe_list)) {
    list = ArrayList::New(isolate, 1, AllocationType::kOld);
  } else {
    list = direct_handle(Cast<ArrayList>(maybe_list), isolate);
  }
  list = ArrayList::Add(isolate, list, property);
  info->set_property_accessors(*list);
}

Handle<JSFunction> ApiNatives::CreateApiFunction(
    Isolate* isolate, DirectHandle<NativeContext> native_context,
    DirectHandle<FunctionTemplateInfo> obj, DirectHandle<Object> prototype,
    InstanceType type, MaybeDirectHandle<Name> maybe_name) {
  RCS_SCOPE(isolate, RuntimeCallCounterId::kCreateApiFunction);
  DirectHandle<SharedFunctionInfo> shared =
      FunctionTemplateInfo::GetOrCreateSharedFunctionInfo(isolate, obj,
                                                          maybe_name);
  // To simplify things, API functions always have shared name.
  DCHECK(shared->HasSharedName());

  Handle<JSFunction> result =
      Factory::JSFunctionBuilder{isolate, shared, native_context}.Build();

  if (obj->remove_prototype()) {
    DCHECK(prototype.is_null());
    DCHECK(result->shared()->IsApiFunction());
    DCHECK(!IsConstructor(*result));
    DCHECK(!result->has_prototype_slot());
    return result;
  }

  // Down from here is only valid for API functions that can be used as a
  // constructor (don't set the "remove prototype" flag).
  DCHECK(result->has_prototype_slot());

  if (obj->read_only_prototype()) {
    result->set_map(isolate,
                    *isolate->sloppy_function_with_readonly_prototype_map());
  }

  if (IsTheHole(*prototype)) {
    prototype = isolate->factory()->NewFunctionPrototype(result);
  } else if (IsUndefined(obj->GetPrototypeProviderTemplate())) {
    JSObject::AddProperty(isolate, Cast<JSObject>(prototype),
                          isolate->factory()->constructor_string(), result,
                          DONT_ENUM);
  }

  int embedder_field_count = 0;
  bool immutable_proto = false;
  if (!IsUndefined(obj->GetInstanceTemplate())) {
    DirectHandle<ObjectTemplateInfo> GetInstanceTemplate(
        Cast<ObjectTemplateInfo>(obj->GetInstanceTemplate()), isolate);
    embedder_field_count = GetInstanceTemplate->embedder_field_count();
    immutable_proto = GetInstanceTemplate->immutable_proto();
  }

  // JSFunction requires information about the prototype slot.
  DCHECK(!InstanceTypeChecker::IsJSFunction(type));
  int instance_size = JSObject::GetHeaderSize(type) +
                      kEmbedderDataSlotSize * embedder_field_count;

  DirectHandle<JSInterceptorMap> map =
      Cast<JSInterceptorMap>(isolate->factory()->NewContextfulMap(
          native_context, ExtendedMapKind::kJSInterceptorMap, type,
          instance_size, TERMINAL_FAST_ELEMENTS_KIND));
  map->init_flags_and_clear_extended_padding();

  // Complete initialization of map's interceptor fields and set interceptor
  // related bits.
  {
    Tagged<UnionOf<Undefined, InterceptorInfo>> maybe_interceptor =
        obj->GetNamedPropertyHandler();
    if (!IsUndefined(maybe_interceptor)) {
      map->set_has_named_interceptor(true);
      map->set_may_have_interesting_properties(true);
      map->set_named_interceptor(Cast<InterceptorInfo>(maybe_interceptor));
    } else {
      map->set_named_interceptor(
          ReadOnlyRoots(isolate).noop_named_interceptor_info());
    }
    maybe_interceptor = obj->GetIndexedPropertyHandler();
    if (!IsUndefined(maybe_interceptor)) {
      map->set_has_indexed_interceptor(true);
      auto info = Cast<InterceptorInfo>(maybe_interceptor);
      map->set_indexed_interceptor(info);
      if (v8_flags.fast_api_iterable_to_list &&
          info->has_indexed_iterable_to_list()) {
        map->set_supports_fast_iterable_to_list(true);
      }
    } else {
      map->set_indexed_interceptor(
          ReadOnlyRoots(isolate).noop_indexed_interceptor_info());
    }
    // Initialize with invalidated cell, it'll be healed lazily upon the
    // first usage.
    map->set_fast_case_validity_cell(
        ReadOnlyRoots(isolate).invalid_prototype_validity_cell());
  }

  // Mark as undetectable if needed.
  if (obj->undetectable()) {
    // We only allow callable undetectable receivers here, since this whole
    // undetectable business is only to support document.all, which is both
    // undetectable and callable. If we ever see the need to have an object
    // that is undetectable but not callable, we need to update the types.h
    // to allow encoding this.
    CHECK(!IsUndefined(obj->GetInstanceCallHandler()));

    if (Protectors::IsNoUndetectableObjectsIntact(isolate)) {
      Protectors::InvalidateNoUndetectableObjects(isolate);
    }
    map->set_is_undetectable(true);
  }

  // Mark as needs_access_check if needed.
  if (obj->needs_access_check()) {
    map->set_is_access_check_needed(true);
    map->set_may_have_interesting_properties(true);
  }

  // Mark instance as callable in the map.
  if (!IsUndefined(obj->GetInstanceCallHandler())) {
    map->set_is_callable(true);
    map->set_is_constructor(!obj->undetectable());
  }

  if (immutable_proto) map->set_is_immutable_proto(true);

  JSFunction::SetInitialMap(isolate, result, map, Cast<JSObject>(prototype));
  if (map->supports_fast_iterable_to_list()) {
    // %CheckFastIterableToListPrototype() relies on the constness tracking
    // which requires the prototype to be in fast mode.
    DirectHandle<Map> proto_map(Cast<JSObject>(prototype)->map(), isolate);
    Map::SetShouldBeFastPrototypeMap(proto_map, true, isolate);
  }

  return result;
}

}  // namespace internal
}  // namespace v8