#include <algorithm>
|
#include "PlatformInvoke.h"
|
|
#include "il2cpp-mono-support.h"
|
#include "il2cpp-mapping.h"
|
#include "il2cpp-metadata.h"
|
#include "utils/StringUtils.h"
|
#include "vm-utils/NativeDelegateMethodCache.h"
|
#include "mono-api.h"
|
#include "MetadataCache.h"
|
|
extern const Il2CppCodeRegistration g_CodeRegistration IL2CPP_ATTRIBUTE_WEAK;
|
|
namespace mono
|
{
|
namespace vm
|
{
|
static mono_unichar2* GetFirstElementAddress(MonoArray *array)
|
{
|
return reinterpret_cast<mono_unichar2*>(reinterpret_cast<char*>(array) + kIl2CppSizeOfArray);
|
}
|
|
char* PlatformInvoke::MarshalCSharpStringToCppString(MonoString* managedString)
|
{
|
if (managedString == NULL)
|
return NULL;
|
|
char *utf8String = mono_string_to_utf8(managedString);
|
|
char* nativeString = MarshalAllocateStringBuffer<char>(mono_string_length(managedString) + 1);
|
strcpy(nativeString, utf8String);
|
mono_unity_g_free(utf8String);
|
|
return nativeString;
|
}
|
|
void PlatformInvoke::MarshalCSharpStringToCppStringFixed(MonoString* managedString, char* buffer, int numberOfCharacters)
|
{
|
if (managedString == NULL)
|
{
|
*buffer = '\0';
|
}
|
else
|
{
|
char *utf8String = mono_string_to_utf8(managedString);
|
strncpy(buffer, utf8String, numberOfCharacters - 1);
|
mono_unity_g_free(utf8String);
|
}
|
}
|
|
mono_unichar2* PlatformInvoke::MarshalCSharpStringToCppWString(MonoString* managedString)
|
{
|
if (managedString == NULL)
|
return NULL;
|
|
int stringLength = mono_string_length(managedString);
|
mono_unichar2* nativeString = MarshalAllocateStringBuffer<mono_unichar2>(stringLength + 1);
|
for (int32_t i = 0; i < stringLength; ++i)
|
nativeString[i] = managedString->chars[i];
|
|
nativeString[managedString->length] = '\0';
|
|
return nativeString;
|
}
|
|
void PlatformInvoke::MarshalCSharpStringToCppWStringFixed(MonoString* managedString, mono_unichar2* buffer, int numberOfCharacters)
|
{
|
if (managedString == NULL)
|
{
|
*buffer = '\0';
|
}
|
else
|
{
|
int32_t stringLength = std::min(mono_string_length(managedString), numberOfCharacters - 1);
|
for (int32_t i = 0; i < stringLength; ++i)
|
buffer[i] = managedString->chars[i];
|
|
buffer[stringLength] = '\0';
|
}
|
}
|
|
MonoString* PlatformInvoke::MarshalCppStringToCSharpStringResult(const char* value)
|
{
|
if (value == NULL)
|
return NULL;
|
|
return mono_string_new(g_MonoDomain, value);
|
}
|
|
MonoString* PlatformInvoke::MarshalCppWStringToCSharpStringResult(const mono_unichar2* value)
|
{
|
if (value == NULL)
|
return NULL;
|
|
return mono_string_new_utf16(g_MonoDomain, value, (int)il2cpp::utils::StringUtils::StrLen(value));
|
}
|
|
char* PlatformInvoke::MarshalEmptyStringBuilder(MonoStringBuilder* stringBuilder, size_t& stringLength, std::vector<std::string>& utf8Chunks, std::vector<MonoStringBuilder*>& builders)
|
{
|
if (stringBuilder == NULL)
|
return NULL;
|
|
stringLength = 0;
|
MonoStringBuilder* currentBuilder = stringBuilder;
|
|
while (true)
|
{
|
if (currentBuilder == NULL)
|
break;
|
|
const mono_unichar2 *str = GetFirstElementAddress(currentBuilder->chunkChars);
|
std::string utf8String = il2cpp::utils::StringUtils::Utf16ToUtf8((Il2CppChar*)str, (int)currentBuilder->chunkChars->max_length);
|
|
utf8Chunks.push_back(utf8String);
|
builders.push_back(currentBuilder);
|
|
size_t lenToCount = std::max((size_t)currentBuilder->chunkChars->max_length, utf8String.size());
|
|
stringLength += lenToCount;
|
|
currentBuilder = currentBuilder->chunkPrevious;
|
}
|
|
char* nativeString = MarshalAllocateStringBuffer<char>(stringLength + 1);
|
|
// We need to zero out the memory because the chunkChar array lengh may have been larger than the chunkLength
|
// and when this happens we'll have a utf8String that is smaller than the the nativeString we allocated. When we go to copy the
|
// chunk utf8String into the nativeString it won't fill everything and we can end up with w/e junk value was in that memory before
|
memset(nativeString, 0, sizeof(char) * (stringLength + 1));
|
|
return nativeString;
|
}
|
|
char* PlatformInvoke::MarshalEmptyStringBuilder(MonoStringBuilder* stringBuilder)
|
{
|
size_t sizeLength;
|
std::vector<std::string> utf8Chunks;
|
std::vector<MonoStringBuilder*> builders;
|
return MarshalEmptyStringBuilder(stringBuilder, sizeLength, utf8Chunks, builders);
|
}
|
|
char* PlatformInvoke::MarshalStringBuilder(MonoStringBuilder* stringBuilder)
|
{
|
if (stringBuilder == NULL)
|
return NULL;
|
|
size_t stringLength;
|
std::vector<std::string> utf8Chunks;
|
std::vector<MonoStringBuilder*> builders;
|
char* nativeString = MarshalEmptyStringBuilder(stringBuilder, stringLength, utf8Chunks, builders);
|
|
if (stringLength > 0)
|
{
|
int offsetAdjustment = 0;
|
for (int i = (int)utf8Chunks.size() - 1; i >= 0; i--)
|
{
|
std::string utf8String = utf8Chunks[i];
|
|
const char* utf8CString = utf8String.c_str();
|
|
memcpy(nativeString + builders[i]->chunkOffset + offsetAdjustment, utf8CString, (int)utf8String.size());
|
|
offsetAdjustment += (int)utf8String.size() - builders[i]->chunkLength;
|
}
|
}
|
|
return nativeString;
|
}
|
|
mono_unichar2* PlatformInvoke::MarshalEmptyWStringBuilder(MonoStringBuilder* stringBuilder, size_t& stringLength)
|
{
|
if (stringBuilder == NULL)
|
return NULL;
|
|
stringLength = 0;
|
MonoStringBuilder* currentBuilder = stringBuilder;
|
while (true)
|
{
|
if (currentBuilder == NULL)
|
break;
|
|
stringLength += (size_t)currentBuilder->chunkChars->max_length;
|
|
currentBuilder = currentBuilder->chunkPrevious;
|
}
|
|
return MarshalAllocateStringBuffer<mono_unichar2>(stringLength + 1);
|
}
|
|
mono_unichar2* PlatformInvoke::MarshalEmptyWStringBuilder(MonoStringBuilder* stringBuilder)
|
{
|
size_t stringLength;
|
return MarshalEmptyWStringBuilder(stringBuilder, stringLength);
|
}
|
|
mono_unichar2* PlatformInvoke::MarshalWStringBuilder(MonoStringBuilder* stringBuilder)
|
{
|
if (stringBuilder == NULL)
|
return NULL;
|
|
size_t stringLength;
|
mono_unichar2* nativeString = MarshalEmptyWStringBuilder(stringBuilder, stringLength);
|
|
if (stringLength > 0)
|
{
|
MonoStringBuilder* currentBuilder = stringBuilder;
|
while (true)
|
{
|
if (currentBuilder == NULL)
|
break;
|
|
const mono_unichar2 *str = GetFirstElementAddress(currentBuilder->chunkChars);
|
|
memcpy(nativeString + currentBuilder->chunkOffset, str, (int)currentBuilder->chunkChars->max_length * sizeof(mono_unichar2));
|
|
currentBuilder = currentBuilder->chunkPrevious;
|
}
|
}
|
|
nativeString[stringLength] = '\0';
|
|
return nativeString;
|
}
|
|
void PlatformInvoke::MarshalStringBuilderResult(MonoStringBuilder* stringBuilder, char* buffer)
|
{
|
if (stringBuilder == NULL || buffer == NULL)
|
return;
|
|
UTF16String utf16String = il2cpp::utils::StringUtils::Utf8ToUtf16(buffer);
|
|
MONO_OBJECT_SETREF(stringBuilder, chunkChars, MonoArrayNew(mono_unity_defaults_get_char_class(), (int)utf16String.size() + 1));
|
|
for (int i = 0; i < (int)utf16String.size(); i++)
|
mono_array_set(stringBuilder->chunkChars, mono_unichar2, i, utf16String[i]);
|
|
mono_array_set(stringBuilder->chunkChars, mono_unichar2, (int)utf16String.size(), '\0');
|
|
stringBuilder->chunkLength = (int)utf16String.size();
|
stringBuilder->chunkOffset = 0;
|
MONO_OBJECT_SETREF(stringBuilder, chunkPrevious, NULL);
|
}
|
|
void PlatformInvoke::MarshalWStringBuilderResult(MonoStringBuilder* stringBuilder, mono_unichar2* buffer)
|
{
|
if (stringBuilder == NULL || buffer == NULL)
|
return;
|
|
int len = (int)il2cpp::utils::StringUtils::StrLen(buffer);
|
|
MONO_OBJECT_SETREF(stringBuilder, chunkChars, MonoArrayNew(mono_unity_defaults_get_char_class(), len + 1));
|
|
for (int i = 0; i < len; i++)
|
mono_array_set(stringBuilder->chunkChars, mono_unichar2, i, buffer[i]);
|
|
mono_array_set(stringBuilder->chunkChars, mono_unichar2, len, '\0');
|
|
stringBuilder->chunkLength = len;
|
stringBuilder->chunkOffset = 0;
|
MONO_OBJECT_SETREF(stringBuilder, chunkPrevious, NULL);
|
}
|
|
static int CompareIl2CppTokenIndexMethodTuple(const void* pkey, const void* pelem)
|
{
|
return (int)(((Il2CppTokenIndexMethodTuple*)pkey)->token - ((Il2CppTokenIndexMethodTuple*)pelem)->token);
|
}
|
|
static Il2CppMethodPointer GetReversePInvokeWrapperFromIndex(MonoMethod* method)
|
{
|
Il2CppCodeGenModule* codeGenModule = InitializeCodeGenHandle(method->klass->image);
|
|
if (codeGenModule->reversePInvokeWrapperCount == 0)
|
return NULL;
|
|
Il2CppTokenIndexMethodTuple key;
|
memset(&key, 0, sizeof(Il2CppTokenIndexMethodTuple));
|
key.token = method->token;
|
|
const Il2CppTokenIndexMethodTuple* res = (const Il2CppTokenIndexMethodTuple*)bsearch(&key, codeGenModule->reversePInvokeWrapperIndices, codeGenModule->reversePInvokeWrapperCount, sizeof(Il2CppTokenIndexMethodTuple), CompareIl2CppTokenIndexMethodTuple);
|
|
if (res == NULL)
|
return NULL;
|
|
uint32_t index = res->index;
|
|
assert(index < g_CodeRegistration.reversePInvokeWrapperCount);
|
return g_CodeRegistration.reversePInvokeWrappers[index];
|
}
|
|
intptr_t PlatformInvoke::MarshalDelegate(MonoDelegate* d)
|
{
|
if (d == NULL)
|
return 0;
|
|
if (unity_mono_method_is_inflated(d->method))
|
mono_raise_exception(mono_get_exception_not_supported("IL2CPP does not support marshaling delegates that point to generic methods."));
|
|
Il2CppMethodPointer reversePInvokeWrapper = GetReversePInvokeWrapperFromIndex((MonoMethod*)d->method);
|
if (reversePInvokeWrapper == NULL)
|
{
|
// Okay, we cannot marshal it for some reason. Figure out why.
|
if (mono_signature_is_instance(mono_method_signature((MonoMethod*)d->method)))
|
mono_raise_exception(mono_get_exception_not_supported("IL2CPP does not support marshaling delegates that point to instance methods to native code."));
|
|
mono_raise_exception(mono_get_exception_not_supported("To marshal a managed method, please add an attribute named 'MonoPInvokeCallback' to the method definition."));
|
}
|
|
return reinterpret_cast<intptr_t>(reversePInvokeWrapper);
|
}
|
|
Il2CppDelegate* PlatformInvoke::MarshalFunctionPointerToDelegate(void* functionPtr, MonoClass* delegateType)
|
{
|
if (!mono_unity_class_has_parent_unsafe(delegateType, mono_unity_defaults_get_delegate_class()))
|
mono_raise_exception(mono_get_exception_argument("t", "Type must derive from Delegate."));
|
|
if (mono_class_is_generic(delegateType) || mono_class_is_inflated(delegateType))
|
mono_raise_exception(mono_get_exception_argument("t", "The specified Type must not be a generic type definition."));
|
|
const Il2CppInteropData* interopData = FindInteropDataFor(delegateType);
|
Il2CppMethodPointer managedToNativeWrapperMethodPointer = interopData != NULL ? interopData->delegatePInvokeWrapperFunction : NULL;
|
|
if (managedToNativeWrapperMethodPointer == NULL)
|
mono_raise_exception(mono_unity_exception_get_marshal_directive(il2cpp::utils::StringUtils::Printf("Cannot marshal P/Invoke call through delegate of type '%s.%s'", mono_class_get_namespace(delegateType), mono_class_get_name(delegateType)).c_str()));
|
|
MonoObject* delegate = mono_object_new(g_MonoDomain, delegateType);
|
Il2CppMethodPointer nativeFunctionPointer = (Il2CppMethodPointer)functionPtr;
|
|
const MonoMethod* method = il2cpp::utils::NativeDelegateMethodCache::GetNativeDelegate(nativeFunctionPointer);
|
if (method == NULL)
|
{
|
MonoMethod* newMethod = mono_unity_method_delegate_invoke_wrapper(delegateType);
|
mono_unity_method_set_method_pointer(newMethod, (void*)nativeFunctionPointer);
|
mono_unity_method_set_invoke_pointer(newMethod, NULL);
|
il2cpp::utils::NativeDelegateMethodCache::AddNativeDelegate(nativeFunctionPointer, newMethod);
|
method = newMethod;
|
}
|
|
MonoError unused;
|
// FIXME: do we need to free these handles?
|
MonoObjectHandle thisHandle = MONO_HANDLE_NEW(MonoObject, (MonoObject*)delegate);
|
MonoObjectHandle targetHandle = MONO_HANDLE_NEW(MonoObject, (MonoObject*)delegate);
|
gboolean success = mono_delegate_ctor_with_method(thisHandle, targetHandle, (void*)managedToNativeWrapperMethodPointer, const_cast<MonoMethod*>(method), &unused);
|
|
return (Il2CppDelegate*)delegate;
|
}
|
} // namespace vm
|
} // namespace mono
|