#include "src/execution/arguments-inl.h"
#include "src/execution/isolate-inl.h"
#include "src/execution/protectors-inl.h"
#include "src/heap/factory.h"
#include "src/heap/heap-inl.h"
#include "src/objects/allocation-site-inl.h"
#include "src/objects/elements.h"
#include "src/objects/js-array-inl.h"
namespace v8 {
namespace internal {
RUNTIME_FUNCTION(Runtime_TransitionElementsKind) {
HandleScope scope(isolate);
DCHECK_EQ(2, args.length());
DirectHandle<JSObject> object = args.at<JSObject>(0);
DirectHandle<Map> to_map = args.at<Map>(1);
ElementsKind to_kind = to_map->elements_kind();
ElementsAccessor::ForKind(to_kind)->TransitionElementsKind(isolate, object,
to_map);
return *object;
}
RUNTIME_FUNCTION(Runtime_TransitionElementsKindWithKind) {
HandleScope scope(isolate);
DCHECK_EQ(2, args.length());
DirectHandle<JSObject> object = args.at<JSObject>(0);
ElementsKind to_kind = static_cast<ElementsKind>(args.smi_value_at(1));
JSObject::TransitionElementsKind(isolate, object, to_kind);
return *object;
}
RUNTIME_FUNCTION(Runtime_NewArray) {
HandleScope scope(isolate);
DCHECK_LE(3, args.length());
int const argc = args.length() - 3;
JavaScriptArguments argv(argc, args.address_of_arg_at(0));
DirectHandle<JSFunction> constructor = args.at<JSFunction>(argc);
DirectHandle<JSReceiver> new_target = args.at<JSReceiver>(argc + 1);
Handle<HeapObject> type_info = args.at<HeapObject>(argc + 2);
DirectHandle<AllocationSite> site = IsAllocationSite(*type_info)
? Cast<AllocationSite>(type_info)
: Handle<AllocationSite>::null();
Factory* factory = isolate->factory();
DCHECK(IsConstructor(*new_target));
bool holey = false;
bool can_use_type_feedback = !site.is_null();
bool can_inline_array_constructor = true;
if (argv.length() == 1) {
DirectHandle<Object> arg0 = argv.at<Object>(0);
if (IsNumber(*arg0)) {
uint32_t length;
if (!Object::ToArrayLength(*arg0, &length)) {
can_use_type_feedback = false;
} else if (JSArray::SetLengthWouldNormalize(isolate->heap(), length)) {
can_use_type_feedback = false;
} else if (length != 0) {
holey = true;
if (length >= JSArray::kInitialMaxFastElementArray) {
can_inline_array_constructor = false;
}
}
} else {
can_use_type_feedback = false;
}
}
DirectHandle<Map> initial_map;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, initial_map,
JSFunction::GetDerivedMap(isolate, constructor, new_target));
ElementsKind initial_kind = can_use_type_feedback
? site->GetElementsKind()
: initial_map->elements_kind();
ElementsKind to_kind =
holey ? GetHoleyElementsKind(initial_kind) : initial_kind;
if (argv.length() > 1 || (argv.length() == 1 && !IsNumber(argv[0]))) {
to_kind = JSObject::GetTransitionedElementsKind(
isolate, initial_kind, FullObjectSlot(argv.address_of_arg_at(0)),
argv.length(), ALLOW_CONVERTED_DOUBLE_ELEMENTS);
}
if (to_kind != initial_kind) {
if (!site.is_null()) site->SetElementsKind(to_kind);
}
initial_map = Map::AsElementsKind(isolate, initial_map, to_kind);
DirectHandle<AllocationSite> allocation_site;
if (AllocationSite::ShouldTrack(to_kind)) {
allocation_site = site;
}
DirectHandle<JSArray> array = Cast<JSArray>(factory->NewJSObjectFromMap(
initial_map, AllocationType::kYoung, allocation_site));
factory->NewJSArrayStorage(
array, 0, 0, ArrayStorageAllocationMode::DONT_INITIALIZE_ARRAY_ELEMENTS);
ElementsKind old_kind = array->GetElementsKind();
RETURN_FAILURE_ON_EXCEPTION(
isolate, ArrayConstructInitializeElements(isolate, array, &argv));
if (!site.is_null() &&
(old_kind != array->GetElementsKind() || !can_use_type_feedback ||
!can_inline_array_constructor)) {
site->SetSpeculationDisabled();
}
return *array;
}
RUNTIME_FUNCTION(Runtime_NormalizeElements) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
DirectHandle<JSObject> array = args.at<JSObject>(0);
CHECK(!array->HasTypedArrayOrRabGsabTypedArrayElements());
CHECK(!IsJSGlobalProxy(*array));
JSObject::NormalizeElements(isolate, array);
return *array;
}
RUNTIME_FUNCTION(Runtime_GrowArrayElements) {
HandleScope scope(isolate);
DCHECK_EQ(2, args.length());
DirectHandle<JSObject> object = args.at<JSObject>(0);
DirectHandle<Object> key = args.at(1);
ElementsKind kind = object->GetElementsKind();
CHECK(IsFastElementsKind(kind));
uint32_t index;
if (IsSmi(*key)) {
int value = Smi::ToInt(*key);
if (value < 0) return Smi::zero();
index = static_cast<uint32_t>(value);
} else {
CHECK(IsHeapNumber(*key));
double value = Cast<HeapNumber>(*key)->value();
if (value < 0 || value > std::numeric_limits<uint32_t>::max()) {
return Smi::zero();
}
index = static_cast<uint32_t>(value);
}
uint32_t capacity = static_cast<uint32_t>(object->elements()->length());
if (index >= capacity) {
bool has_grown;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, has_grown,
object->GetElementsAccessor()->GrowCapacity(isolate, object, index));
if (!has_grown) {
return Smi::zero();
}
}
return object->elements();
}
RUNTIME_FUNCTION(Runtime_ArrayIsArray) {
HandleScope shs(isolate);
DCHECK_EQ(1, args.length());
DirectHandle<Object> object = args.at(0);
Maybe<bool> result = Object::IsArray(object);
MAYBE_RETURN(result, ReadOnlyRoots(isolate).exception());
return isolate->heap()->ToBoolean(result.FromJust());
}
RUNTIME_FUNCTION(Runtime_IsArray) {
SealHandleScope shs(isolate);
DCHECK_EQ(1, args.length());
Tagged<Object> obj = args[0];
return isolate->heap()->ToBoolean(IsJSArray(obj));
}
RUNTIME_FUNCTION(Runtime_ArraySpeciesConstructor) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
DirectHandle<JSAny> original_array = args.at<JSAny>(0);
RETURN_RESULT_OR_FAILURE(
isolate, Object::ArraySpeciesConstructor(isolate, original_array));
}
RUNTIME_FUNCTION(Runtime_ArrayIncludes_Slow) {
HandleScope shs(isolate);
DCHECK_EQ(3, args.length());
DirectHandle<Object> search_element = args.at(1);
Handle<Object> from_index = args.at(2);
DirectHandle<JSReceiver> object;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, object,
Object::ToObject(isolate, Handle<Object>(args[0], isolate)));
int64_t len;
{
if (object->map()->instance_type() == JS_ARRAY_TYPE) {
uint32_t len32 = 0;
bool success =
Object::ToArrayLength(Cast<JSArray>(*object)->length(), &len32);
DCHECK(success);
USE(success);
len = len32;
} else {
DirectHandle<Object> len_;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, len_,
Object::GetProperty(isolate, object,
isolate->factory()->length_string()));
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, len_,
Object::ToLength(isolate, len_));
len = static_cast<int64_t>(Object::NumberValue(*len_));
DCHECK_EQ(len, Object::NumberValue(*len_));
}
}
if (len == 0) return ReadOnlyRoots(isolate).false_value();
int64_t index = 0;
if (!IsUndefined(*from_index, isolate)) {
double start_from;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, start_from, Object::IntegerValue(isolate, from_index));
if (start_from >= len) return ReadOnlyRoots(isolate).false_value();
if (V8_LIKELY(std::isfinite(start_from))) {
if (start_from < 0) {
index = static_cast<int64_t>(std::max<double>(start_from + len, 0));
} else {
index = start_from;
}
}
DCHECK_GE(index, 0);
}
if (!IsSpecialReceiverMap(object->map()) &&
len <= JSObject::kMaxElementCount &&
JSObject::PrototypeHasNoElements(isolate, Cast<JSObject>(*object))) {
DirectHandle<JSObject> obj = Cast<JSObject>(object);
ElementsAccessor* elements = obj->GetElementsAccessor();
Maybe<bool> result =
elements->IncludesValue(isolate, obj, search_element, index, len);
MAYBE_RETURN(result, ReadOnlyRoots(isolate).exception());
return *isolate->factory()->ToBoolean(result.FromJust());
}
for (; index < len; ++index) {
HandleScope iteration_hs(isolate);
DirectHandle<Object> element_k;
{
PropertyKey key(isolate, static_cast<double>(index));
LookupIterator it(isolate, object, key);
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, element_k,
Object::GetProperty(&it));
}
if (Object::SameValueZero(*search_element, *element_k)) {
return ReadOnlyRoots(isolate).true_value();
}
}
return ReadOnlyRoots(isolate).false_value();
}
RUNTIME_FUNCTION(Runtime_ArrayIndexOf) {
HandleScope hs(isolate);
DCHECK_EQ(3, args.length());
DirectHandle<Object> search_element = args.at(1);
Handle<Object> from_index = args.at(2);
DirectHandle<JSReceiver> object;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, object,
Object::ToObject(isolate, args.at(0), "Array.prototype.indexOf"));
int64_t len;
{
if (IsJSArray(*object)) {
uint32_t len32 = 0;
bool success =
Object::ToArrayLength(Cast<JSArray>(*object)->length(), &len32);
DCHECK(success);
USE(success);
len = len32;
} else {
DirectHandle<Object> len_;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, len_,
Object::GetProperty(isolate, object,
isolate->factory()->length_string()));
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, len_,
Object::ToLength(isolate, len_));
len = static_cast<int64_t>(Object::NumberValue(*len_));
DCHECK_EQ(len, Object::NumberValue(*len_));
}
}
if (len == 0) return Smi::FromInt(-1);
int64_t start_from;
{
double fp;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, fp, Object::IntegerValue(isolate, from_index));
if (fp > len) return Smi::FromInt(-1);
if (V8_LIKELY(fp >=
static_cast<double>(std::numeric_limits<int64_t>::min()))) {
DCHECK(fp < static_cast<double>(std::numeric_limits<int64_t>::max()));
start_from = static_cast<int64_t>(fp);
} else {
start_from = std::numeric_limits<int64_t>::min();
}
}
int64_t index;
if (start_from >= 0) {
index = start_from;
} else {
index = len + start_from;
if (index < 0) {
index = 0;
}
}
if (!IsSpecialReceiverMap(object->map()) && len <= kMaxUInt32 &&
JSObject::PrototypeHasNoElements(isolate, Cast<JSObject>(*object))) {
DirectHandle<JSObject> obj = Cast<JSObject>(object);
ElementsAccessor* elements = obj->GetElementsAccessor();
Maybe<int64_t> result = elements->IndexOfValue(isolate, obj, search_element,
static_cast<uint32_t>(index),
static_cast<uint32_t>(len));
MAYBE_RETURN(result, ReadOnlyRoots(isolate).exception());
return *isolate->factory()->NewNumberFromInt64(result.FromJust());
}
for (; index < len; ++index) {
HandleScope iteration_hs(isolate);
DirectHandle<Object> element_k;
{
PropertyKey key(isolate, static_cast<double>(index));
LookupIterator it(isolate, object, key);
Maybe<bool> present = JSReceiver::HasProperty(&it);
MAYBE_RETURN(present, ReadOnlyRoots(isolate).exception());
if (!present.FromJust()) continue;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, element_k,
Object::GetProperty(&it));
if (Object::StrictEquals(*search_element, *element_k)) {
return *isolate->factory()->NewNumberFromInt64(index);
}
}
}
return Smi::FromInt(-1);
}
}
}