2025-07-03 12:53:49 +04:00

295 lines
9.6 KiB
C++

#include "il2cpp-config.h"
#include "gc/gc_wrapper.h"
#include "gc/GarbageCollector.h"
#include "vm/Array.h"
#include "vm/Class.h"
#include "vm/Exception.h"
#include "vm/Object.h"
#include "vm/Profiler.h"
#include "il2cpp-class-internals.h"
#include "il2cpp-object-internals.h"
#include <memory>
namespace il2cpp
{
namespace vm
{
Il2CppArray* Array::Clone(Il2CppArray* arr)
{
Il2CppClass *typeInfo = arr->klass;
const uint32_t elem_size = il2cpp::vm::Array::GetElementSize(typeInfo);
if (arr->bounds == NULL)
{
il2cpp_array_size_t len = il2cpp::vm::Array::GetLength(arr);
Il2CppArray *clone = (Il2CppArray*)il2cpp::vm::Array::NewFull(typeInfo, &len, NULL);
memcpy(il2cpp::vm::Array::GetFirstElementAddress(clone), il2cpp::vm::Array::GetFirstElementAddress(arr), elem_size * len);
gc::GarbageCollector::SetWriteBarrier((void**)il2cpp::vm::Array::GetFirstElementAddress(clone), elem_size * len);
return clone;
}
il2cpp_array_size_t size = elem_size;
std::vector<il2cpp_array_size_t> lengths(typeInfo->rank);
std::vector<il2cpp_array_size_t> lowerBounds(typeInfo->rank);
for (int i = 0; i < typeInfo->rank; ++i)
{
lengths[i] = arr->bounds[i].length;
size *= arr->bounds[i].length;
lowerBounds[i] = arr->bounds[i].lower_bound;
}
Il2CppArray* clone = il2cpp::vm::Array::NewFull(typeInfo, &lengths[0], &lowerBounds[0]);
memcpy(il2cpp::vm::Array::GetFirstElementAddress(clone), il2cpp::vm::Array::GetFirstElementAddress(arr), size);
gc::GarbageCollector::SetWriteBarrier((void**)il2cpp::vm::Array::GetFirstElementAddress(clone), size);
return clone;
}
int32_t Array::GetElementSize(const Il2CppClass *klass)
{
IL2CPP_ASSERT(klass->rank);
return klass->element_size;
}
uint32_t Array::GetLength(Il2CppArray* array)
{
return ARRAY_LENGTH_AS_INT32(array->max_length);
}
uint32_t Array::GetByteLength(Il2CppArray* array)
{
Il2CppClass *klass;
il2cpp_array_size_t length;
int i;
klass = array->klass;
if (array->bounds == NULL)
length = array->max_length;
else
{
length = 1;
for (i = 0; i < klass->rank; ++i)
length *= array->bounds[i].length;
}
return ARRAY_LENGTH_AS_INT32(length * GetElementSize(klass));
}
Il2CppArray* Array::New(Il2CppClass *elementTypeInfo, il2cpp_array_size_t length)
{
return NewSpecific(Class::GetArrayClass(elementTypeInfo, 1), length);
}
static void RaiseOverflowException()
{
vm::Exception::Raise(vm::Exception::GetOverflowException("Arithmetic operation resulted in an overflow."));
}
Il2CppArray* Array::NewSpecific(Il2CppClass *klass, il2cpp_array_size_t n)
{
Il2CppObject *o;
Il2CppArray *ao;
uint32_t elem_size;
il2cpp_array_size_t byte_len;
Class::Init(klass);
IL2CPP_ASSERT(klass->rank);
IL2CPP_ASSERT(klass->initialized);
IL2CPP_ASSERT(klass->element_class->initialized);
IL2CPP_ASSERT(klass->byval_arg.type == IL2CPP_TYPE_SZARRAY);
IL2CPP_NOT_IMPLEMENTED_NO_ASSERT(Array::NewSpecific, "Not checking for overflow");
IL2CPP_NOT_IMPLEMENTED_NO_ASSERT(Array::NewSpecific, "Handle allocations with a GC descriptor");
if (n > IL2CPP_ARRAY_MAX_INDEX)
{
RaiseOverflowException();
return NULL;
}
elem_size = il2cpp_array_element_size(klass);
//if (CHECK_MUL_OVERFLOW_UN (n, elem_size)) {
// mono_gc_out_of_memory (MONO_ARRAY_MAX_SIZE);
// return NULL;
//}
byte_len = n * elem_size;
//if (CHECK_ADD_OVERFLOW_UN (byte_len, sizeof (MonoArray))) {
// mono_gc_out_of_memory (MONO_ARRAY_MAX_SIZE);
// return NULL;
//}
byte_len += kIl2CppSizeOfArray;
if (!klass->has_references)
{
o = Object::AllocatePtrFree(byte_len, klass);
#if NEED_TO_ZERO_PTRFREE
((Il2CppArray*)o)->bounds = NULL;
memset((char*)o + sizeof(Il2CppObject), 0, byte_len - sizeof(Il2CppObject));
#endif
}
else if (klass->element_class->byval_arg.valuetype &&
((GC_descr)klass->element_class->gc_desc & GC_DS_TAGS) == GC_DS_BITMAP)
{
o = (Il2CppObject*)GC_gcj_vector_malloc(byte_len, klass);
}
#if IL2CPP_HAS_GC_DESCRIPTORS
else if (klass->gc_desc != GC_NO_DESCRIPTOR)
{
o = Object::AllocateSpec(byte_len, klass);
}
#endif
else
{
o = Object::Allocate(byte_len, klass);
}
ao = (Il2CppArray*)o;
ao->max_length = n;
#if IL2CPP_ENABLE_PROFILER
if (Profiler::ProfileAllocations())
Profiler::Allocation(o, klass);
#endif
return ao;
}
Il2CppArray* Array::NewFull(Il2CppClass *array_class, il2cpp_array_size_t *lengths, il2cpp_array_size_t *lower_bounds)
{
il2cpp_array_size_t byte_len, len, bounds_size;
Il2CppObject *o;
Il2CppArray *array;
int i;
Class::Init(array_class);
IL2CPP_ASSERT(array_class->rank);
IL2CPP_ASSERT(array_class->initialized);
IL2CPP_ASSERT(array_class->element_class->initialized);
IL2CPP_NOT_IMPLEMENTED_NO_ASSERT(Array::NewFull, "IGNORING non-zero based arrays!");
IL2CPP_NOT_IMPLEMENTED_NO_ASSERT(Array::NewFull, "Handle allocations with a GC descriptor");
byte_len = il2cpp_array_element_size(array_class);
len = 1;
/* A single dimensional array with a 0 lower bound is the same as an szarray */
if (array_class->rank == 1 && ((array_class->byval_arg.type == IL2CPP_TYPE_SZARRAY) || (lower_bounds && lower_bounds[0] == 0)))
{
/* A single dimensional array with a 0 lower bound should be an szarray */
/* but the caller asked for an IL2CPP_TYPE_ARRAY, which insn't correct */
IL2CPP_ASSERT(array_class->byval_arg.type == IL2CPP_TYPE_SZARRAY);
len = lengths[0];
if (len > IL2CPP_ARRAY_MAX_INDEX) //MONO_ARRAY_MAX_INDEX
RaiseOverflowException();
bounds_size = 0;
}
else
{
IL2CPP_ASSERT(array_class->byval_arg.type == IL2CPP_TYPE_ARRAY);
bounds_size = sizeof(Il2CppArrayBounds) * array_class->rank;
for (i = 0; i < array_class->rank; ++i)
{
if (lengths[i] > IL2CPP_ARRAY_MAX_INDEX) //MONO_ARRAY_MAX_INDEX
RaiseOverflowException();
//if (CHECK_MUL_OVERFLOW_UN (len, lengths [i]))
// mono_gc_out_of_memory (MONO_ARRAY_MAX_SIZE);
len *= lengths[i];
}
}
//if (CHECK_MUL_OVERFLOW_UN (byte_len, len))
// mono_gc_out_of_memory (MONO_ARRAY_MAX_SIZE);
byte_len *= len;
//if (CHECK_ADD_OVERFLOW_UN (byte_len, sizeof (MonoArray)))
// mono_gc_out_of_memory (MONO_ARRAY_MAX_SIZE);
byte_len += kIl2CppSizeOfArray;
if (bounds_size)
{
/* align */
//if (CHECK_ADD_OVERFLOW_UN (byte_len, 3))
// mono_gc_out_of_memory (MONO_ARRAY_MAX_SIZE);
byte_len = (byte_len + (IL2CPP_SIZEOF_VOID_P - 1)) & ~(IL2CPP_SIZEOF_VOID_P - 1);
//if (CHECK_ADD_OVERFLOW_UN (byte_len, bounds_size))
// mono_gc_out_of_memory (MONO_ARRAY_MAX_SIZE);
byte_len += bounds_size;
}
/*
* Following three lines almost taken from mono_object_new ():
* they need to be kept in sync.
*/
if (!array_class->has_references)
{
o = Object::AllocatePtrFree(byte_len, array_class);
#if NEED_TO_ZERO_PTRFREE
memset((char*)o + sizeof(Il2CppObject), 0, byte_len - sizeof(Il2CppObject));
#endif
}
#if IL2CPP_HAS_GC_DESCRIPTORS
else if (array_class->gc_desc != GC_NO_DESCRIPTOR)
{
o = Object::AllocateSpec(byte_len, array_class);
}
#endif
else
{
o = Object::Allocate(byte_len, array_class);
}
array = (Il2CppArray*)o;
array->max_length = len;
if (bounds_size)
{
Il2CppArrayBounds *bounds = (Il2CppArrayBounds*)((char*)array + byte_len - bounds_size);
array->bounds = bounds;
for (i = 0; i < array_class->rank; ++i)
{
bounds[i].length = lengths[i];
if (lower_bounds)
bounds[i].lower_bound = ARRAY_LENGTH_AS_INT32(lower_bounds[i]);
}
}
#if IL2CPP_ENABLE_PROFILER
if (Profiler::ProfileAllocations())
Profiler::Allocation(o, array_class);
#endif
return array;
}
char* Array::GetFirstElementAddress(Il2CppArray *array)
{
return reinterpret_cast<char*>(array) + kIl2CppSizeOfArray;
}
il2cpp_array_size_t Array::IndexFromIndices(Il2CppArray* thisPtr, int32_t const * indices)
{
int32_t i;
il2cpp_array_size_t pos;
Il2CppClass* ac;
ac = thisPtr->klass;
pos = indices[0] - thisPtr->bounds[0].lower_bound;
for (i = 1; i < ac->rank; i++)
pos = pos * thisPtr->bounds[i].length + indices[i] -
thisPtr->bounds[i].lower_bound;
return pos;
}
} /* namespace vm */
} /* namespace il2cpp */
LIBIL2CPP_CODEGEN_API int32_t
il2cpp_array_element_size(Il2CppClass *ac)
{
return il2cpp::vm::Array::GetElementSize(ac);
}