#include "il2cpp-config.h"

#include "icalls/mscorlib/System.Runtime.InteropServices/Marshal.h"

#include "il2cpp-class-internals.h"
#include "il2cpp-tabledefs.h"

#include "gc/GarbageCollector.h"
#include "metadata/FieldLayout.h"
#include "os/Atomic.h"
#include "os/MarshalStringAlloc.h"
#include "vm/Array.h"
#include "vm/CCW.h"
#include "vm/Class.h"
#include "vm/Exception.h"
#include "vm/Field.h"
#include "vm/Image.h"
#include "vm/LastError.h"
#include "vm/MarshalAlloc.h"
#include "vm/Object.h"
#include "vm/PlatformInvoke.h"
#include "vm/RCW.h"
#include "vm/String.h"
#include "vm/Type.h"
#include "utils/MarshalingUtils.h"
#include "utils/StringUtils.h"
#include <string>
#include <deque>
#include <algorithm>

namespace il2cpp
{
namespace icalls
{
namespace mscorlib
{
namespace System
{
namespace Runtime
{
namespace InteropServices
{
    int32_t Marshal::AddRefInternal(intptr_t pUnk)
    {
        return static_cast<Il2CppIUnknown*>((void*)pUnk)->AddRef();
    }

    intptr_t Marshal::AllocCoTaskMem(int32_t size)
    {
        intptr_t result;
        result = (intptr_t)vm::MarshalAlloc::Allocate(size);
        return result;
    }

    intptr_t Marshal::AllocHGlobal(intptr_t size)
    {
        intptr_t result;
        result = (intptr_t)vm::MarshalAlloc::AllocateHGlobal((size_t)size);
        return result;
    }

    void Marshal::copy_from_unmanaged(intptr_t source, int startIndex, Il2CppArray * destination, int length)
    {
        uint32_t element_size = (uint32_t)il2cpp_array_element_size(destination->klass);
        memcpy(il2cpp_array_addr_with_size(destination, element_size, startIndex), reinterpret_cast<void*>(source), length * element_size);
    }

    void Marshal::copy_to_unmanaged(Il2CppArray * source, int32_t startIndex, intptr_t destination, int32_t length)
    {
        uint32_t element_size = (uint32_t)il2cpp_array_element_size(source->klass);
        memcpy(reinterpret_cast<void*>(destination), il2cpp_array_addr_with_size(source, element_size, startIndex), length * element_size);
    }

    void Marshal::FreeBSTR(intptr_t ptr)
    {
        vm::PlatformInvoke::MarshalFreeBString(reinterpret_cast<Il2CppChar*>(ptr));
    }

    void Marshal::FreeCoTaskMem(intptr_t ptr)
    {
        vm::MarshalAlloc::Free(reinterpret_cast<void*>(ptr));
    }

    void Marshal::FreeHGlobal(intptr_t hglobal)
    {
        vm::MarshalAlloc::FreeHGlobal(reinterpret_cast<void*>(hglobal));
    }

    bool Marshal::IsComObject(Il2CppObject* o)
    {
        if (o == NULL)
            vm::Exception::Raise(vm::Exception::GetArgumentNullException("o"));

        return o->klass->is_import_or_windows_runtime;
    }

    intptr_t Marshal::GetCCW(Il2CppObject* o, Il2CppReflectionType* T)
    {
        if (o == NULL)
            vm::Exception::Raise(vm::Exception::GetArgumentNullException("o"));

        if (T == NULL)
            vm::Exception::Raise(vm::Exception::GetArgumentNullException("T"));

        Il2CppClass* klass = vm::Class::FromIl2CppType(T->type);

        if (!vm::Class::IsInterface(klass))
        {
            DECLARE_NATIVE_C_STRING_AS_STRING_VIEW_OF_IL2CPP_CHARS(parameter, IL2CPP_NATIVE_STRING("T"));
            DECLARE_NATIVE_C_STRING_AS_STRING_VIEW_OF_IL2CPP_CHARS(message, IL2CPP_NATIVE_STRING("The T parameter is not an interface."));
            vm::Exception::Raise(vm::Exception::GetArgumentException(parameter, message));
        }

        if (vm::Class::IsGeneric(klass))
        {
            DECLARE_NATIVE_C_STRING_AS_STRING_VIEW_OF_IL2CPP_CHARS(parameter, IL2CPP_NATIVE_STRING("T"));
            DECLARE_NATIVE_C_STRING_AS_STRING_VIEW_OF_IL2CPP_CHARS(message, IL2CPP_NATIVE_STRING("The T parameter is a generic type."));
            vm::Exception::Raise(vm::Exception::GetArgumentException(parameter, message));
        }

        const Il2CppInteropData* interopData = klass->interopData;
        if (interopData == NULL || interopData->guid == NULL)
        {
            DECLARE_NATIVE_C_STRING_AS_STRING_VIEW_OF_IL2CPP_CHARS(parameter, IL2CPP_NATIVE_STRING("T"));
            DECLARE_NATIVE_C_STRING_AS_STRING_VIEW_OF_IL2CPP_CHARS(message, IL2CPP_NATIVE_STRING("The specified type must be visible from COM."));
            vm::Exception::Raise(vm::Exception::GetArgumentException(parameter, message));
        }

        return reinterpret_cast<intptr_t>(vm::CCW::GetOrCreate(o, *interopData->guid));
    }

    int32_t Marshal::GetComSlotForMethodInfoInternal(mscorlib_System_Reflection_MemberInfo * m)
    {
        NOT_SUPPORTED_IL2CPP(Marshal::GetComSlotForMethodInfoInternal, "Not implemented.");
        return 0;
    }

    Il2CppDelegate* Marshal::GetDelegateForFunctionPointerInternal(intptr_t ptr, Il2CppReflectionType* t)
    {
        Il2CppClass* delegateType = il2cpp::vm::Class::FromIl2CppType(t->type);
        return vm::PlatformInvoke::MarshalFunctionPointerToDelegate(reinterpret_cast<void*>(ptr), delegateType);
    }

    intptr_t Marshal::GetFunctionPointerForDelegateInternal(Il2CppDelegate* d)
    {
        return vm::PlatformInvoke::MarshalDelegate(d);
    }

    intptr_t Marshal::GetIDispatchForObjectInternal(Il2CppObject* o)
    {
        NOT_SUPPORTED_IL2CPP(Marshal::GetIDispatchForObjectInternal, "Not implemented.");
        return 0;
    }

    intptr_t Marshal::GetIUnknownForObjectInternal(Il2CppObject* o)
    {
        NOT_SUPPORTED_IL2CPP(Marshal::GetIUnknownForObjectInternal, "This icall is not supported by il2cpp. Use the il2cpp_codegen_com_get_iunknown_for_object intrinsic instead.");
        return 0;
    }

    Il2CppObject* Marshal::GetObjectForCCW(intptr_t pUnk)
    {
        if (pUnk == 0)
            return NULL;

        return vm::RCW::GetOrCreateFromIUnknown(reinterpret_cast<Il2CppIUnknown*>(pUnk), il2cpp_defaults.il2cpp_com_object_class);
    }

    Il2CppString* Marshal::PtrToStringBSTR(intptr_t ptr)
    {
        if (ptr == 0)
            vm::Exception::Raise(vm::Exception::GetArgumentNullException("ptr"));

        return vm::PlatformInvoke::MarshalCppBStringToCSharpStringResult(reinterpret_cast<const Il2CppChar*>(ptr));
    }

    Il2CppString* Marshal::PtrToStringAnsi_mscorlib_System_String_mscorlib_System_IntPtr(intptr_t ptr)
    {
        char* value = (char*)ptr;
        if (value == NULL)
            return NULL;
        return vm::String::New(value);
    }

    Il2CppString* Marshal::PtrToStringAnsi_mscorlib_System_String_mscorlib_System_IntPtr_mscorlib_System_Int32(intptr_t ptr, int32_t len)
    {
        char* value = (char*)ptr;
        if (value == NULL)
            vm::Exception::Raise(vm::Exception::GetArgumentNullException("ptr"));

        return vm::String::NewLen(value, len);
    }

    Il2CppString* Marshal::PtrToStringUni_mscorlib_System_String_mscorlib_System_IntPtr(intptr_t ptr)
    {
        Il2CppChar* value = reinterpret_cast<Il2CppChar*>(ptr);
        if (value == NULL)
            return NULL;

        int32_t len = 0;
        Il2CppChar* t = value;

        while (*t++)
            len++;

        return vm::String::NewUtf16(value, len);
    }

    Il2CppString* Marshal::PtrToStringUni_mscorlib_System_String_mscorlib_System_IntPtr_mscorlib_System_Int32(intptr_t ptr, int32_t len)
    {
        Il2CppChar* value = reinterpret_cast<Il2CppChar*>(ptr);
        if (value == NULL)
            vm::Exception::Raise(vm::Exception::GetArgumentNullException("ptr"));

        return vm::String::NewUtf16(value, len);
    }

    Il2CppObject* Marshal::PtrToStructure(intptr_t ptr, Il2CppReflectionType* structureType)
    {
        if (ptr == 0)
            return NULL;

        if (structureType == NULL)
            vm::Exception::Raise(vm::Exception::GetArgumentNullException("structureType"));

        Il2CppClass* type = vm::Class::FromIl2CppType(structureType->type);

        Il2CppTypeEnum typeType = structureType->type->type;

        if (typeType == IL2CPP_TYPE_STRING || typeType == IL2CPP_TYPE_SZARRAY || (typeType == IL2CPP_TYPE_CLASS && !vm::Class::HasDefaultConstructor(type)))
        {
            vm::Exception::Raise(vm::Exception::GetMissingMethodException("No parameterless constructor defined for this object."));
        }

        if (type->interopData != NULL && type->interopData->pinvokeMarshalFromNativeFunction != NULL)
        {
            Il2CppObject* result = vm::Object::New(type);

            if (typeType == IL2CPP_TYPE_CLASS)
            {
                typedef void (*Constructor)(Il2CppObject*);
                Constructor ctor = reinterpret_cast<Constructor>(vm::Class::GetMethodFromName(type, ".ctor", 0)->virtualMethodPointer);
                ctor(result);
                utils::MarshalingUtils::MarshalStructFromNative(reinterpret_cast<void*>(ptr), result, type->interopData);
            }
            else
            {
                utils::MarshalingUtils::MarshalStructFromNative(reinterpret_cast<void*>(ptr), vm::Object::Unbox(result), type->interopData);
            }

            return result;
        }

        // If there's no custom marshal function, it means it's either a primitive, or invalid argument
        if (type->native_size != -1)
        {
            // We may also need to throw a NotSupportedException for an ArgIterator.
            if (structureType->type->type == IL2CPP_TYPE_VOID)
                vm::Exception::Raise(vm::Exception::GetNotSupportedException("Cannot dynamically create an instance of System.Void."));

            // PtrToStructure is supposed to throw on enums
            if (!type->enumtype)
            {
                Il2CppObject* result = vm::Object::New(type);
                memcpy(vm::Object::Unbox(result), reinterpret_cast<void*>(ptr), type->native_size);
                return result;
            }
        }

        // If we got this far, throw an exception

        if (type->generic_class != NULL || type->is_generic)
            vm::Exception::Raise(vm::Exception::GetArgumentException("structure", "The specified object must not be an instance of a generic type."));

        vm::Exception::Raise(vm::Exception::GetArgumentException("structure", "The specified structure must be blittable or have layout information."));
        return NULL;
    }

    void Marshal::PtrToStructureObject(intptr_t ptr, Il2CppObject* structure)
    {
        if (ptr == 0)
            return;

        if (structure == NULL)
            vm::Exception::Raise(vm::Exception::GetArgumentNullException("structure"));

        Il2CppClass* type = structure->klass;

        // This is only legal for classes.
        if (type->byval_arg.type != IL2CPP_TYPE_CLASS)
        {
            vm::Exception::Raise(vm::Exception::GetArgumentException("structure", "The specified structure must be an instance of a formattable class."));
        }

        if (type->interopData != NULL && type->interopData->pinvokeMarshalFromNativeFunction != NULL)
        {
            utils::MarshalingUtils::MarshalStructFromNative(reinterpret_cast<void*>(ptr), structure, type->interopData);
            return;
        }

        if (type->generic_class || type->is_generic)
            vm::Exception::Raise(vm::Exception::GetArgumentException("structure", "The specified object must not be an instance of a generic type."));

        vm::Exception::Raise(vm::Exception::GetArgumentException("structure", "The specified structure must be blittable or have layout information."));
    }

    int32_t Marshal::QueryInterfaceInternal(intptr_t pUnk, mscorlib_System_Guid * iid, intptr_t* ppv)
    {
        Il2CppIUnknown* unknown = reinterpret_cast<Il2CppIUnknown*>(pUnk);
        return unknown->QueryInterface(reinterpret_cast<Il2CppGuid&>(*iid), reinterpret_cast<void**>(ppv));
    }

    template<typename T>
    static inline T ReadValue(intptr_t ptr, int32_t offset)
    {
        return *reinterpret_cast<T*>(reinterpret_cast<uint8_t*>(ptr) + offset);
    }

    uint8_t Marshal::ReadByte(intptr_t ptr, int32_t ofs)
    {
        return ReadValue<uint8_t>(ptr, ofs);
    }

    int16_t Marshal::ReadInt16(intptr_t ptr, int32_t ofs)
    {
        return ReadValue<int16_t>(ptr, ofs);
    }

    int32_t Marshal::ReadInt32(intptr_t ptr, int32_t ofs)
    {
        return ReadValue<int32_t>(ptr, ofs);
    }

    int64_t Marshal::ReadInt64(intptr_t ptr, int32_t ofs)
    {
        return ReadValue<int64_t>(ptr, ofs);
    }

    intptr_t Marshal::ReadIntPtr(intptr_t ptr, int32_t ofs)
    {
        return ReadValue<intptr_t>(ptr, ofs);
    }

    int32_t Marshal::ReleaseInternal(intptr_t pUnk)
    {
        return reinterpret_cast<Il2CppIUnknown*>(pUnk)->Release();
    }

    int32_t Marshal::ReleaseComObjectInternal(Il2CppObject* co)
    {
        // There's a check in mscorlib before calling this internal icall, so assert instead of full check is OK here.
        IL2CPP_ASSERT(co->klass->is_import_or_windows_runtime);

        int32_t newRefCount = os::Atomic::Decrement(&static_cast<Il2CppComObject*>(co)->refCount);
        if (newRefCount == 0)
        {
            // We can't really release the COM object directly, because it might have additional
            // fields that cache different interfaces. So let's just call its finalizer here.
            // In order to deal with the fact that this may get called from different threads
            // at the same time, we (atomically) register a NULL finalizer, and if another finalizer
            // was already registered, we call it. If there was no finalizer registered, it means
            // that we lost the race and we should just carry on.
            gc::GarbageCollector::FinalizerCallback oldFinalizer = gc::GarbageCollector::RegisterFinalizerWithCallback(co, NULL);
            if (oldFinalizer != NULL)
                oldFinalizer(co, NULL);
        }

        return newRefCount;
    }

    int Marshal::SizeOf(Il2CppReflectionType* rtype)
    {
        if (rtype == NULL)
            vm::Exception::Raise(vm::Exception::GetArgumentNullException("t"));

        Il2CppClass* typeInfo = vm::Class::FromIl2CppType(rtype->type);

        if (typeInfo->native_size != -1)
        {
            // SizeOf "cannot" get size of enums and strings
            if (!typeInfo->enumtype && rtype->type->type != IL2CPP_TYPE_STRING)
                return typeInfo->native_size;
        }

        // If we get to here, type does not have a native size
        // So let's figure what kind of exception to throw

        if (typeInfo->generic_class != NULL || typeInfo->is_generic)
            vm::Exception::Raise(vm::Exception::GetArgumentException("t", "The t parameter is a generic type."));

        std::string exceptionMessage = utils::StringUtils::Printf("Type \'%s\' cannot be marshaled as an unmanaged structure; no meaningful size or offset can be computed.",
            vm::Type::GetName(rtype->type, IL2CPP_TYPE_NAME_FORMAT_FULL_NAME).c_str());

        vm::Exception::Raise(vm::Exception::GetArgumentException(NULL, exceptionMessage.c_str()));
        return 0;
    }

    intptr_t Marshal::StringToBSTR(Il2CppString* s)
    {
        return reinterpret_cast<intptr_t>(vm::PlatformInvoke::MarshalCSharpStringToCppBString(s));
    }

    intptr_t Marshal::StringToHGlobalAnsi(Il2CppChar* s, int32_t length)
    {
        if (s == NULL)
            return 0;

        std::string str = il2cpp::utils::StringUtils::Utf16ToUtf8(s);
        char *cstr = (char*)vm::MarshalAlloc::AllocateHGlobal(str.size() + 1);
        strcpy(cstr, str.c_str());
        return reinterpret_cast<intptr_t>(cstr);
    }

    intptr_t Marshal::StringToHGlobalUni(Il2CppChar* s, int32_t length)
    {
        if (s == NULL)
            return 0;

        size_t bytes = (length + 1) * 2;
        Il2CppChar* cstr = static_cast<Il2CppChar*>(vm::MarshalAlloc::AllocateHGlobal(bytes));
        memcpy(cstr, s, bytes);
        return reinterpret_cast<intptr_t>(cstr);
    }

    void Marshal::StructureToPtr(Il2CppObject* structure, intptr_t ptr, bool deleteOld)
    {
        if (structure == NULL)
            vm::Exception::Raise(vm::Exception::GetArgumentNullException("structure"));

        if (ptr == 0)
            vm::Exception::Raise(vm::Exception::GetArgumentNullException("ptr"));

        Il2CppClass* type = structure->klass;

        if (type->interopData != NULL && type->interopData->pinvokeMarshalToNativeFunction != NULL)
        {
            if (deleteOld)
                utils::MarshalingUtils::MarshalFreeStruct(reinterpret_cast<void*>(ptr), type->interopData);

            void* objectPtr = (type->byval_arg.type == IL2CPP_TYPE_CLASS) ? structure : vm::Object::Unbox(structure);
            utils::MarshalingUtils::MarshalStructToNative(objectPtr, reinterpret_cast<void*>(ptr), type->interopData);
            return;
        }

        // If there's no custom marshal function, it means it's either a primitive, or invalid argument

        if (type->native_size != -1)
        {
            // StructureToPtr is supposed to throw on strings and enums
            if (!type->enumtype && type->byval_arg.type != IL2CPP_TYPE_STRING)
            {
                memcpy(reinterpret_cast<void*>(ptr), vm::Object::Unbox(structure), type->native_size);
                return;
            }
        }

        // If we got this far, throw an exception
        Il2CppException* exception;

        if (type->generic_class != NULL || type->is_generic)
        {
            exception = vm::Exception::GetArgumentException("structure", "The specified object must not be an instance of a generic type.");
        }
        else
        {
            exception = vm::Exception::GetArgumentException("structure", "The specified structure must be blittable or have layout information.");
        }

        vm::Exception::Raise(exception);
    }

    template<typename T>
    static inline void WriteValue(intptr_t ptr, int32_t offset, T value)
    {
        *reinterpret_cast<T*>(reinterpret_cast<uint8_t*>(ptr) + offset) = value;
    }

    void Marshal::WriteByte(intptr_t ptr, int32_t ofs, uint8_t val)
    {
        WriteValue(ptr, ofs, val);
    }

    void Marshal::WriteInt16(intptr_t ptr, int32_t ofs, int16_t val)
    {
        WriteValue(ptr, ofs, val);
    }

    void Marshal::WriteInt32(intptr_t ptr, int32_t ofs, int32_t val)
    {
        WriteValue(ptr, ofs, val);
    }

    void Marshal::WriteInt64(intptr_t ptr, int32_t ofs, int64_t val)
    {
        WriteValue(ptr, ofs, val);
    }

    void Marshal::WriteIntPtr(intptr_t ptr, int32_t ofs, intptr_t val)
    {
        WriteValue(ptr, ofs, val);
    }

    void Marshal::DestroyStructure(intptr_t ptr, Il2CppReflectionType* structureType)
    {
        if (ptr == 0)
            vm::Exception::Raise(vm::Exception::GetArgumentNullException("ptr"));

        if (structureType == NULL)
            vm::Exception::Raise(vm::Exception::GetArgumentNullException("structureType"));

        Il2CppClass* type = vm::Class::FromIl2CppType(structureType->type);

        // If cleanup function exists, it will call it and return true
        // In that case, we're done.
        if (utils::MarshalingUtils::MarshalFreeStruct(reinterpret_cast<void*>(ptr), type->interopData))
            return;

        if (type->is_generic || type->generic_class != NULL)
        {
            vm::Exception::Raise(vm::Exception::GetArgumentException("structureType", "The specified type must not be a generic type definition."));
        }

        // Enums are blittable, but they don't have layout information, therefore Marshal.DestroyStructure is supposed to throw
        if (!type->enumtype)
        {
            // Char and Boolean are not blittable, but they should not raise this exception, as we can call DestroyStructure on them without problems.
            if (type->is_blittable || structureType->type->type == IL2CPP_TYPE_CHAR || structureType->type->type == IL2CPP_TYPE_BOOLEAN)
                return;
        }

        // If we got this far, throw an exception
        vm::Exception::Raise(vm::Exception::GetArgumentException("structureType", "The specified type must be blittable or have layout information."));
    }

    int32_t Marshal::GetLastWin32Error()
    {
        return vm::LastError::GetLastError();
    }

    void Marshal::SetLastWin32Error(uint32_t error)
    {
        vm::LastError::SetLastError(error);
    }

    static size_t RoundUpToMultiple(size_t numToRound, size_t multiple)
    {
        if (multiple == 0)
            return numToRound;

        size_t remainder = numToRound % multiple;
        if (remainder == 0)
            return numToRound;
        return numToRound + multiple - remainder;
    }

    intptr_t Marshal::OffsetOf(Il2CppReflectionType* t, Il2CppString* fieldName)
    {
        std::string fieldNameToFind = utils::StringUtils::Utf16ToUtf8(fieldName->chars);
        Il2CppClass* type = vm::Class::FromIl2CppType(t->type);

        FieldInfo* field = vm::Class::GetFieldFromName(type, fieldNameToFind.c_str());
        if (field == NULL || (vm::Field::GetFlags(field) & FIELD_ATTRIBUTE_STATIC))
        {
            std::string message;
            message = "Field '" + fieldNameToFind + "' is not a marshaled member of the type '" + type->name + "'";
            vm::Exception::Raise(vm::Exception::GetArgumentException("fieldName", message.c_str()));
        }

        // Order the base classes so the most base class is first.
        std::deque<Il2CppClass*> inheritanceHierarchy;
        while (type)
        {
            inheritanceHierarchy.push_front(type);
            type = type->parent;
        }

        bool fieldFound = false;

        size_t offset = 0;
        FieldInfo* previousField = NULL;
        for (std::deque<Il2CppClass*>::iterator it = inheritanceHierarchy.begin(); it < inheritanceHierarchy.end(); ++it)
        {
            type = *it;
            void* iter = NULL;
            int layout = vm::Class::GetFlags(type) & TYPE_ATTRIBUTE_LAYOUT_MASK;
            bool layoutIsExplicit = layout == TYPE_ATTRIBUTE_EXPLICIT_LAYOUT;

            while ((field = vm::Class::GetFields(type, &iter)))
            {
                if (vm::Field::GetFlags(field) & FIELD_ATTRIBUTE_STATIC)
                    continue;

                if (layoutIsExplicit)
                {
                    offset = field->offset - sizeof(Il2CppObject);
                }
                else if (previousField != NULL) // Determine how much the previous field added to the offset.
                {
                    if (!vm::Type::IsStruct(previousField->type))
                    {
                        size_t managedOffset = field->offset - previousField->offset;
                        if (managedOffset != 0) // overlapping fields have a zero offset
                        {
                            offset += vm::Class::GetFieldMarshaledSize(previousField);
                        }
                    }
                    else
                    {
                        offset += vm::Class::FromIl2CppType(previousField->type)->native_size;
                    }

                    if (offset != 0)
                    {
                        int marshaledFieldAlignment = vm::Class::GetFieldMarshaledAlignment(field);
                        offset = RoundUpToMultiple(offset, type->packingSize == 0 ? marshaledFieldAlignment : std::min((int)type->packingSize, marshaledFieldAlignment));
                    }
                }
                else
                {
                    // If this is the first field, it might have a non-zero offset, as it could be in a type
                    // with an explicit layout. So calculate it offset based on the distance it is from the
                    // end of the object header.
                    offset = field->offset - sizeof(Il2CppObject);
                }

                previousField = field;

                if (fieldNameToFind == vm::Field::GetName(field))
                {
                    fieldFound = true;
                    break;
                }
            }

            if (fieldFound)
                break;
        }

        return static_cast<intptr_t>(offset);
    }

    // We _could_ implement these two if we wanted - we'd just need to init their metadata
    void Marshal::Prelink(Il2CppReflectionMethod* m)
    {
        NOT_SUPPORTED_IL2CPP(Marshal::Prelink, "Not implemented.");
    }

    void Marshal::PrelinkAll(Il2CppReflectionType* c)
    {
        NOT_SUPPORTED_IL2CPP(Marshal::PrelinkAll, "Not implemented.");
    }

    intptr_t Marshal::ReAllocCoTaskMem(intptr_t ptr, int32_t size)
    {
        return reinterpret_cast<intptr_t>(vm::MarshalAlloc::ReAlloc(reinterpret_cast<void*>(ptr), size));
    }

    intptr_t Marshal::ReAllocHGlobal(intptr_t ptr, intptr_t size)
    {
        if (ptr == 0)
            vm::Exception::RaiseOutOfMemoryException();

        return reinterpret_cast<intptr_t>(vm::MarshalAlloc::ReAllocHGlobal(reinterpret_cast<void*>(ptr), (size_t)size));
    }

    intptr_t Marshal::UnsafeAddrOfPinnedArrayElement(Il2CppArray* arr, int32_t index)
    {
        return reinterpret_cast<intptr_t>(il2cpp_array_addr_with_size(arr, il2cpp_array_element_size(arr->klass), index));
    }

    intptr_t Marshal::BufferToBSTR(Il2CppChar* ptr, int32_t slen)
    {
        Il2CppChar* bstr = NULL;
        os::MarshalStringAlloc::AllocateBStringLength(ptr, slen, &bstr);
        return (intptr_t)bstr;
    }

    int32_t Marshal::GetHRForException_WinRT(Il2CppException* e)
    {
        IL2CPP_NOT_IMPLEMENTED_ICALL(Marshal::GetHRForException_WinRT);
        IL2CPP_UNREACHABLE;
    }

    intptr_t Marshal::GetRawIUnknownForComObjectNoAddRef(Il2CppObject* o)
    {
        IL2CPP_ASSERT(o->klass->is_import_or_windows_runtime);

        return reinterpret_cast<intptr_t>(static_cast<Il2CppComObject*>(o)->identity);
    }

    Il2CppObject* Marshal::GetNativeActivationFactory(Il2CppObject* type)
    {
        IL2CPP_NOT_IMPLEMENTED_ICALL(Marshal::GetNativeActivationFactory);
        IL2CPP_UNREACHABLE;
    }

    intptr_t Marshal::AllocCoTaskMemSize(intptr_t sizet)
    {
        intptr_t result;
        result = (intptr_t)vm::MarshalAlloc::Allocate(sizet);
        return result;
    }

    void Marshal::copy_from_unmanaged_fixed(intptr_t source, int32_t startIndex, Il2CppArray* destination, int32_t length, void* fixed_destination_element)
    {
        uint32_t element_size = (uint32_t)il2cpp_array_element_size(destination->klass);
        memcpy(il2cpp_array_addr_with_size(destination, element_size, startIndex), reinterpret_cast<void*>(source), length * element_size);
    }

    void Marshal::copy_to_unmanaged_fixed(Il2CppArray* source, int32_t startIndex, intptr_t destination, int32_t length, void* fixed_source_element)
    {
        uint32_t element_size = (uint32_t)il2cpp_array_element_size(source->klass);
        memcpy(reinterpret_cast<void*>(destination), il2cpp_array_addr_with_size(source, element_size, startIndex), length * element_size);
    }
} /* namespace InteropServices */
} /* namespace Runtime */
} /* namespace System */
} /* namespace mscorlib */
} /* namespace icalls */
} /* namespace il2cpp */
