Add Array and Dictionary wrapper classes to C#
This commit is contained in:
parent
2f69e36cef
commit
ee3c476c9a
|
@ -1609,7 +1609,7 @@ void CSharpScript::load_script_signals(GDMonoClass *p_class, GDMonoClass *p_nati
|
|||
|
||||
bool CSharpScript::_get_signal(GDMonoClass *p_class, GDMonoClass *p_delegate, Vector<Argument> ¶ms) {
|
||||
if (p_delegate->has_attribute(CACHED_CLASS(SignalAttribute))) {
|
||||
MonoType *raw_type = GDMonoClass::get_raw_type(p_delegate);
|
||||
MonoType *raw_type = p_delegate->get_mono_type();
|
||||
|
||||
if (mono_type_get_type(raw_type) == MONO_TYPE_CLASS) {
|
||||
// Arguments are accessibles as arguments of .Invoke method
|
||||
|
|
|
@ -100,8 +100,6 @@
|
|||
#define C_METHOD_MONOSTR_FROM_GODOT C_NS_MONOMARSHAL "::mono_string_from_godot"
|
||||
#define C_METHOD_MONOARRAY_TO(m_type) C_NS_MONOMARSHAL "::mono_array_to_" #m_type
|
||||
#define C_METHOD_MONOARRAY_FROM(m_type) C_NS_MONOMARSHAL "::" #m_type "_to_mono_array"
|
||||
#define C_METHOD_MANAGED_TO_DICT C_NS_MONOMARSHAL "::mono_object_to_Dictionary"
|
||||
#define C_METHOD_MANAGED_FROM_DICT C_NS_MONOMARSHAL "::Dictionary_to_mono_object"
|
||||
|
||||
#define BINDINGS_GENERATOR_VERSION UINT32_C(2)
|
||||
|
||||
|
@ -1338,7 +1336,6 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
|
|||
} else if (return_type->cs_out.empty()) {
|
||||
p_output.push_back("return " + im_call + ";\n");
|
||||
} else {
|
||||
p_output.push_back(INDENT3);
|
||||
p_output.push_back(sformat(return_type->cs_out, im_call, return_type->cs_type, return_type->im_type_out));
|
||||
p_output.push_back("\n");
|
||||
}
|
||||
|
@ -2344,7 +2341,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
|
|||
|
||||
#define INSERT_ARRAY(m_type, m_proxy_t) INSERT_ARRAY_FULL(m_type, m_type, m_proxy_t)
|
||||
|
||||
INSERT_ARRAY(Array, object);
|
||||
INSERT_ARRAY(PoolIntArray, int);
|
||||
INSERT_ARRAY_FULL(PoolByteArray, PoolByteArray, byte);
|
||||
|
||||
|
@ -2362,20 +2358,36 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
|
|||
|
||||
#undef INSERT_ARRAY
|
||||
|
||||
// Array
|
||||
itype = TypeInterface();
|
||||
itype.name = "Array";
|
||||
itype.cname = itype.name;
|
||||
itype.proxy_name = "Array";
|
||||
itype.c_out = "\treturn memnew(Array(%1));\n";
|
||||
itype.c_type = itype.name;
|
||||
itype.c_type_in = itype.c_type + "*";
|
||||
itype.c_type_out = itype.c_type + "*";
|
||||
itype.cs_type = itype.proxy_name;
|
||||
itype.cs_in = "%0." CS_SMETHOD_GETINSTANCE "()";
|
||||
itype.cs_out = "return new Array(%0);";
|
||||
itype.im_type_in = "IntPtr";
|
||||
itype.im_type_out = "IntPtr";
|
||||
builtin_types.insert(itype.cname, itype);
|
||||
|
||||
// Dictionary
|
||||
itype = TypeInterface();
|
||||
itype.name = "Dictionary";
|
||||
itype.cname = itype.name;
|
||||
itype.proxy_name = "Dictionary<object, object>";
|
||||
itype.c_in = "\t%0 %1_in = " C_METHOD_MANAGED_TO_DICT "(%1);\n";
|
||||
itype.c_out = "\treturn " C_METHOD_MANAGED_FROM_DICT "(%1);\n";
|
||||
itype.c_arg_in = "&%s_in";
|
||||
itype.proxy_name = "Dictionary";
|
||||
itype.c_out = "\treturn memnew(Dictionary(%1));\n";
|
||||
itype.c_type = itype.name;
|
||||
itype.c_type_in = "MonoObject*";
|
||||
itype.c_type_out = "MonoObject*";
|
||||
itype.c_type_in = itype.c_type + "*";
|
||||
itype.c_type_out = itype.c_type + "*";
|
||||
itype.cs_type = itype.proxy_name;
|
||||
itype.im_type_in = itype.proxy_name;
|
||||
itype.im_type_out = itype.proxy_name;
|
||||
itype.cs_in = "%0." CS_SMETHOD_GETINSTANCE "()";
|
||||
itype.cs_out = "return new Dictionary(%0);";
|
||||
itype.im_type_in = "IntPtr";
|
||||
itype.im_type_out = "IntPtr";
|
||||
builtin_types.insert(itype.cname, itype);
|
||||
|
||||
// void (fictitious type to represent the return type of methods that do not return anything)
|
||||
|
|
|
@ -0,0 +1,240 @@
|
|||
/*************************************************************************/
|
||||
/* collections_glue.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "collections_glue.h"
|
||||
|
||||
#include <mono/metadata/exception.h>
|
||||
|
||||
#include "../mono_gd/gd_mono_class.h"
|
||||
|
||||
Array *godot_icall_Array_Ctor() {
|
||||
return memnew(Array);
|
||||
}
|
||||
|
||||
void godot_icall_Array_Dtor(Array *ptr) {
|
||||
memdelete(ptr);
|
||||
}
|
||||
|
||||
MonoObject *godot_icall_Array_At(Array *ptr, int index) {
|
||||
if (index < 0 || index > ptr->size()) {
|
||||
GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range());
|
||||
return NULL;
|
||||
}
|
||||
return GDMonoMarshal::variant_to_mono_object(ptr->operator[](index));
|
||||
}
|
||||
|
||||
void godot_icall_Array_SetAt(Array *ptr, int index, MonoObject *value) {
|
||||
if (index < 0 || index > ptr->size()) {
|
||||
GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range());
|
||||
return;
|
||||
}
|
||||
ptr->operator[](index) = GDMonoMarshal::mono_object_to_variant(value);
|
||||
}
|
||||
|
||||
int godot_icall_Array_Count(Array *ptr) {
|
||||
return ptr->size();
|
||||
}
|
||||
|
||||
void godot_icall_Array_Add(Array *ptr, MonoObject *item) {
|
||||
ptr->append(GDMonoMarshal::mono_object_to_variant(item));
|
||||
}
|
||||
|
||||
void godot_icall_Array_Clear(Array *ptr) {
|
||||
ptr->clear();
|
||||
}
|
||||
|
||||
bool godot_icall_Array_Contains(Array *ptr, MonoObject *item) {
|
||||
return ptr->find(GDMonoMarshal::mono_object_to_variant(item)) != -1;
|
||||
}
|
||||
|
||||
void godot_icall_Array_CopyTo(Array *ptr, MonoArray *array, int array_index) {
|
||||
int count = ptr->size();
|
||||
|
||||
if (mono_array_length(array) < (array_index + count)) {
|
||||
MonoException *exc = mono_get_exception_argument("", "Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
|
||||
GDMonoUtils::set_pending_exception(exc);
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
MonoObject *boxed = GDMonoMarshal::variant_to_mono_object(ptr->operator[](i));
|
||||
mono_array_setref(array, array_index, boxed);
|
||||
array_index++;
|
||||
}
|
||||
}
|
||||
|
||||
int godot_icall_Array_IndexOf(Array *ptr, MonoObject *item) {
|
||||
return ptr->find(GDMonoMarshal::mono_object_to_variant(item));
|
||||
}
|
||||
|
||||
void godot_icall_Array_Insert(Array *ptr, int index, MonoObject *item) {
|
||||
if (index < 0 || index > ptr->size()) {
|
||||
GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range());
|
||||
return;
|
||||
}
|
||||
ptr->insert(index, GDMonoMarshal::mono_object_to_variant(item));
|
||||
}
|
||||
|
||||
bool godot_icall_Array_Remove(Array *ptr, MonoObject *item) {
|
||||
int idx = ptr->find(GDMonoMarshal::mono_object_to_variant(item));
|
||||
if (idx >= 0) {
|
||||
ptr->remove(idx);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void godot_icall_Array_RemoveAt(Array *ptr, int index) {
|
||||
if (index < 0 || index > ptr->size()) {
|
||||
GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range());
|
||||
return;
|
||||
}
|
||||
ptr->remove(index);
|
||||
}
|
||||
|
||||
Dictionary *godot_icall_Dictionary_Ctor() {
|
||||
return memnew(Dictionary);
|
||||
}
|
||||
|
||||
void godot_icall_Dictionary_Dtor(Dictionary *ptr) {
|
||||
memdelete(ptr);
|
||||
}
|
||||
|
||||
MonoObject *godot_icall_Dictionary_GetValue(Dictionary *ptr, MonoObject *key) {
|
||||
Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key));
|
||||
if (ret == NULL) {
|
||||
MonoObject *exc = mono_object_new(mono_domain_get(), CACHED_CLASS(KeyNotFoundException)->get_mono_ptr());
|
||||
#ifdef DEBUG_ENABLED
|
||||
CRASH_COND(!exc);
|
||||
#endif
|
||||
GDMonoUtils::runtime_object_init(exc);
|
||||
GDMonoUtils::set_pending_exception((MonoException *)exc);
|
||||
return NULL;
|
||||
}
|
||||
return GDMonoMarshal::variant_to_mono_object(ret);
|
||||
}
|
||||
|
||||
void godot_icall_Dictionary_SetValue(Dictionary *ptr, MonoObject *key, MonoObject *value) {
|
||||
ptr->operator[](GDMonoMarshal::mono_object_to_variant(key)) = GDMonoMarshal::mono_object_to_variant(value);
|
||||
}
|
||||
|
||||
Array *godot_icall_Dictionary_Keys(Dictionary *ptr) {
|
||||
return memnew(Array(ptr->keys()));
|
||||
}
|
||||
|
||||
Array *godot_icall_Dictionary_Values(Dictionary *ptr) {
|
||||
return memnew(Array(ptr->values()));
|
||||
}
|
||||
|
||||
int godot_icall_Dictionary_Count(Dictionary *ptr) {
|
||||
return ptr->size();
|
||||
}
|
||||
|
||||
void godot_icall_Dictionary_Add(Dictionary *ptr, MonoObject *key, MonoObject *value) {
|
||||
Variant varKey = GDMonoMarshal::mono_object_to_variant(key);
|
||||
Variant *ret = ptr->getptr(varKey);
|
||||
if (ret != NULL) {
|
||||
GDMonoUtils::set_pending_exception(mono_get_exception_argument("key", "An element with the same key already exists"));
|
||||
return;
|
||||
}
|
||||
ptr->operator[](varKey) = GDMonoMarshal::mono_object_to_variant(value);
|
||||
}
|
||||
|
||||
void godot_icall_Dictionary_Clear(Dictionary *ptr) {
|
||||
ptr->clear();
|
||||
}
|
||||
|
||||
bool godot_icall_Dictionary_Contains(Dictionary *ptr, MonoObject *key, MonoObject *value) {
|
||||
// no dupes
|
||||
Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key));
|
||||
return ret != NULL && *ret == GDMonoMarshal::mono_object_to_variant(value);
|
||||
}
|
||||
|
||||
bool godot_icall_Dictionary_ContainsKey(Dictionary *ptr, MonoObject *key) {
|
||||
return ptr->has(GDMonoMarshal::mono_object_to_variant(key));
|
||||
}
|
||||
|
||||
bool godot_icall_Dictionary_RemoveKey(Dictionary *ptr, MonoObject *key) {
|
||||
return ptr->erase_checked(GDMonoMarshal::mono_object_to_variant(key));
|
||||
}
|
||||
|
||||
bool godot_icall_Dictionary_Remove(Dictionary *ptr, MonoObject *key, MonoObject *value) {
|
||||
Variant varKey = GDMonoMarshal::mono_object_to_variant(key);
|
||||
|
||||
// no dupes
|
||||
Variant *ret = ptr->getptr(varKey);
|
||||
if (ret != NULL && *ret == GDMonoMarshal::mono_object_to_variant(value)) {
|
||||
ptr->erase_checked(varKey);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool godot_icall_Dictionary_TryGetValue(Dictionary *ptr, MonoObject *key, MonoObject **value) {
|
||||
Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key));
|
||||
if (ret == NULL) {
|
||||
*value = NULL;
|
||||
return false;
|
||||
}
|
||||
*value = GDMonoMarshal::variant_to_mono_object(ret);
|
||||
return true;
|
||||
}
|
||||
|
||||
void godot_register_collections_icalls() {
|
||||
mono_add_internal_call("Godot.Array::godot_icall_Array_Ctor", (void *)godot_icall_Array_Ctor);
|
||||
mono_add_internal_call("Godot.Array::godot_icall_Array_Dtor", (void *)godot_icall_Array_Dtor);
|
||||
mono_add_internal_call("Godot.Array::godot_icall_Array_At", (void *)godot_icall_Array_At);
|
||||
mono_add_internal_call("Godot.Array::godot_icall_Array_SetAt", (void *)godot_icall_Array_SetAt);
|
||||
mono_add_internal_call("Godot.Array::godot_icall_Array_Count", (void *)godot_icall_Array_Count);
|
||||
mono_add_internal_call("Godot.Array::godot_icall_Array_Add", (void *)godot_icall_Array_Add);
|
||||
mono_add_internal_call("Godot.Array::godot_icall_Array_Clear", (void *)godot_icall_Array_Clear);
|
||||
mono_add_internal_call("Godot.Array::godot_icall_Array_Contains", (void *)godot_icall_Array_Contains);
|
||||
mono_add_internal_call("Godot.Array::godot_icall_Array_CopyTo", (void *)godot_icall_Array_CopyTo);
|
||||
mono_add_internal_call("Godot.Array::godot_icall_Array_IndexOf", (void *)godot_icall_Array_IndexOf);
|
||||
mono_add_internal_call("Godot.Array::godot_icall_Array_Insert", (void *)godot_icall_Array_Insert);
|
||||
mono_add_internal_call("Godot.Array::godot_icall_Array_Remove", (void *)godot_icall_Array_Remove);
|
||||
mono_add_internal_call("Godot.Array::godot_icall_Array_RemoveAt", (void *)godot_icall_Array_RemoveAt);
|
||||
|
||||
mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Ctor", (void *)godot_icall_Dictionary_Ctor);
|
||||
mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Dtor", (void *)godot_icall_Dictionary_Dtor);
|
||||
mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_GetValue", (void *)godot_icall_Dictionary_GetValue);
|
||||
mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_SetValue", (void *)godot_icall_Dictionary_SetValue);
|
||||
mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Keys", (void *)godot_icall_Dictionary_Keys);
|
||||
mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Values", (void *)godot_icall_Dictionary_Values);
|
||||
mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Count", (void *)godot_icall_Dictionary_Count);
|
||||
mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Add", (void *)godot_icall_Dictionary_Add);
|
||||
mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Clear", (void *)godot_icall_Dictionary_Clear);
|
||||
mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Contains", (void *)godot_icall_Dictionary_Contains);
|
||||
mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_ContainsKey", (void *)godot_icall_Dictionary_ContainsKey);
|
||||
mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_RemoveKey", (void *)godot_icall_Dictionary_RemoveKey);
|
||||
mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Remove", (void *)godot_icall_Dictionary_Remove);
|
||||
mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_TryGetValue", (void *)godot_icall_Dictionary_TryGetValue);
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
/*************************************************************************/
|
||||
/* collections_glue.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#ifndef COLLECTIONS_GLUE_H
|
||||
#define COLLECTIONS_GLUE_H
|
||||
|
||||
#include "core/array.h"
|
||||
|
||||
#include "../mono_gd/gd_mono_marshal.h"
|
||||
|
||||
// Array
|
||||
|
||||
Array *godot_icall_Array_Ctor();
|
||||
|
||||
void godot_icall_Array_Dtor(Array *ptr);
|
||||
|
||||
MonoObject *godot_icall_Array_At(Array *ptr, int index);
|
||||
|
||||
void godot_icall_Array_SetAt(Array *ptr, int index, MonoObject *value);
|
||||
|
||||
int godot_icall_Array_Count(Array *ptr);
|
||||
|
||||
void godot_icall_Array_Add(Array *ptr, MonoObject *item);
|
||||
|
||||
void godot_icall_Array_Clear(Array *ptr);
|
||||
|
||||
bool godot_icall_Array_Contains(Array *ptr, MonoObject *item);
|
||||
|
||||
void godot_icall_Array_CopyTo(Array *ptr, MonoArray *array, int array_index);
|
||||
|
||||
int godot_icall_Array_IndexOf(Array *ptr, MonoObject *item);
|
||||
|
||||
void godot_icall_Array_Insert(Array *ptr, int index, MonoObject *item);
|
||||
|
||||
bool godot_icall_Array_Remove(Array *ptr, MonoObject *item);
|
||||
|
||||
void godot_icall_Array_RemoveAt(Array *ptr, int index);
|
||||
|
||||
// Dictionary
|
||||
|
||||
Dictionary *godot_icall_Dictionary_Ctor();
|
||||
|
||||
void godot_icall_Dictionary_Dtor(Dictionary *ptr);
|
||||
|
||||
MonoObject *godot_icall_Dictionary_GetValue(Dictionary *ptr, MonoObject *key);
|
||||
|
||||
void godot_icall_Dictionary_SetValue(Dictionary *ptr, MonoObject *key, MonoObject *value);
|
||||
|
||||
Array *godot_icall_Dictionary_Keys(Dictionary *ptr);
|
||||
|
||||
Array *godot_icall_Dictionary_Values(Dictionary *ptr);
|
||||
|
||||
int godot_icall_Dictionary_Count(Dictionary *ptr);
|
||||
|
||||
void godot_icall_Dictionary_Add(Dictionary *ptr, MonoObject *key, MonoObject *value);
|
||||
|
||||
void godot_icall_Dictionary_Clear(Dictionary *ptr);
|
||||
|
||||
bool godot_icall_Dictionary_Contains(Dictionary *ptr, MonoObject *key, MonoObject *value);
|
||||
|
||||
bool godot_icall_Dictionary_ContainsKey(Dictionary *ptr, MonoObject *key);
|
||||
|
||||
bool godot_icall_Dictionary_RemoveKey(Dictionary *ptr, MonoObject *key);
|
||||
|
||||
bool godot_icall_Dictionary_Remove(Dictionary *ptr, MonoObject *key, MonoObject *value);
|
||||
|
||||
bool godot_icall_Dictionary_TryGetValue(Dictionary *ptr, MonoObject *key, MonoObject **value);
|
||||
|
||||
// Register internal calls
|
||||
|
||||
void godot_register_collections_icalls();
|
||||
|
||||
#endif // COLLECTIONS_GLUE_H
|
|
@ -0,0 +1,335 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
class ArraySafeHandle : SafeHandle
|
||||
{
|
||||
public ArraySafeHandle(IntPtr handle) : base(IntPtr.Zero, true)
|
||||
{
|
||||
this.handle = handle;
|
||||
}
|
||||
|
||||
public override bool IsInvalid
|
||||
{
|
||||
get
|
||||
{
|
||||
return handle == IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
Array.godot_icall_Array_Dtor(handle);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public class Array : IList<object>, ICollection<object>, IEnumerable<object>, IDisposable
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static IntPtr godot_icall_Array_Ctor();
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static void godot_icall_Array_Dtor(IntPtr ptr);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static object godot_icall_Array_At(IntPtr ptr, int index);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static void godot_icall_Array_SetAt(IntPtr ptr, int index, object value);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static int godot_icall_Array_Count(IntPtr ptr);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static void godot_icall_Array_Add(IntPtr ptr, object item);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static void godot_icall_Array_Clear(IntPtr ptr);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static bool godot_icall_Array_Contains(IntPtr ptr, object item);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static void godot_icall_Array_CopyTo(IntPtr ptr, object[] array, int arrayIndex);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static int godot_icall_Array_IndexOf(IntPtr ptr, object item);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static void godot_icall_Array_Insert(IntPtr ptr, int index, object item);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static bool godot_icall_Array_Remove(IntPtr ptr, object item);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static void godot_icall_Array_RemoveAt(IntPtr ptr, int index);
|
||||
|
||||
ArraySafeHandle safeHandle;
|
||||
bool disposed = false;
|
||||
|
||||
public Array()
|
||||
{
|
||||
safeHandle = new ArraySafeHandle(godot_icall_Array_Ctor());
|
||||
}
|
||||
|
||||
internal Array(ArraySafeHandle handle)
|
||||
{
|
||||
safeHandle = handle;
|
||||
}
|
||||
|
||||
internal Array(IntPtr handle)
|
||||
{
|
||||
safeHandle = new ArraySafeHandle(handle);
|
||||
}
|
||||
|
||||
internal IntPtr GetPtr()
|
||||
{
|
||||
return safeHandle.DangerousGetHandle();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposed)
|
||||
return;
|
||||
|
||||
if (safeHandle != null)
|
||||
{
|
||||
safeHandle.Dispose();
|
||||
safeHandle = null;
|
||||
}
|
||||
|
||||
disposed = true;
|
||||
}
|
||||
|
||||
public object this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
return godot_icall_Array_At(GetPtr(), index);
|
||||
}
|
||||
set
|
||||
{
|
||||
godot_icall_Array_SetAt(GetPtr(), index, value);
|
||||
}
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
return godot_icall_Array_Count(GetPtr());
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsReadOnly
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void Add(object item)
|
||||
{
|
||||
godot_icall_Array_Add(GetPtr(), item);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
godot_icall_Array_Clear(GetPtr());
|
||||
}
|
||||
|
||||
public bool Contains(object item)
|
||||
{
|
||||
return godot_icall_Array_Contains(GetPtr(), item);
|
||||
}
|
||||
|
||||
public void CopyTo(object[] array, int arrayIndex)
|
||||
{
|
||||
if (array == null)
|
||||
throw new ArgumentNullException(nameof(array), "Value cannot be null.");
|
||||
|
||||
if (arrayIndex < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Number was less than the array's lower bound in the first dimension.");
|
||||
|
||||
// Internal call may throw ArgumentException
|
||||
godot_icall_Array_CopyTo(GetPtr(), array, arrayIndex);
|
||||
}
|
||||
|
||||
public IEnumerator<object> GetEnumerator()
|
||||
{
|
||||
int count = Count;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
yield return godot_icall_Array_At(GetPtr(), i);
|
||||
}
|
||||
}
|
||||
|
||||
public int IndexOf(object item)
|
||||
{
|
||||
return godot_icall_Array_IndexOf(GetPtr(), item);
|
||||
}
|
||||
|
||||
public void Insert(int index, object item)
|
||||
{
|
||||
godot_icall_Array_Insert(GetPtr(), index, item);
|
||||
}
|
||||
|
||||
public bool Remove(object item)
|
||||
{
|
||||
return godot_icall_Array_Remove(GetPtr(), item);
|
||||
}
|
||||
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
godot_icall_Array_RemoveAt(GetPtr(), index);
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
|
||||
public class Array<T> : IList<T>, ICollection<T>, IEnumerable<T>
|
||||
{
|
||||
Array objectArray;
|
||||
|
||||
public Array()
|
||||
{
|
||||
objectArray = new Array();
|
||||
}
|
||||
|
||||
public Array(Array array)
|
||||
{
|
||||
objectArray = array;
|
||||
}
|
||||
|
||||
internal Array(IntPtr handle)
|
||||
{
|
||||
objectArray = new Array(handle);
|
||||
}
|
||||
|
||||
internal Array(ArraySafeHandle handle)
|
||||
{
|
||||
objectArray = new Array(handle);
|
||||
}
|
||||
|
||||
public static explicit operator Array(Array<T> from)
|
||||
{
|
||||
return from.objectArray;
|
||||
}
|
||||
|
||||
public T this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
return (T)objectArray[index];
|
||||
}
|
||||
set
|
||||
{
|
||||
objectArray[index] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
return objectArray.Count;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsReadOnly
|
||||
{
|
||||
get
|
||||
{
|
||||
return objectArray.IsReadOnly;
|
||||
}
|
||||
}
|
||||
|
||||
public void Add(T item)
|
||||
{
|
||||
objectArray.Add(item);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
objectArray.Clear();
|
||||
}
|
||||
|
||||
public bool Contains(T item)
|
||||
{
|
||||
return objectArray.Contains(item);
|
||||
}
|
||||
|
||||
public void CopyTo(T[] array, int arrayIndex)
|
||||
{
|
||||
if (array == null)
|
||||
throw new ArgumentNullException(nameof(array), "Value cannot be null.");
|
||||
|
||||
if (arrayIndex < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Number was less than the array's lower bound in the first dimension.");
|
||||
|
||||
// TODO This may be quite slow because every element access is an internal call.
|
||||
// It could be moved entirely to an internal call if we find out how to do the cast there.
|
||||
|
||||
int count = objectArray.Count;
|
||||
|
||||
if (array.Length < (arrayIndex + count))
|
||||
throw new ArgumentException("Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
array[arrayIndex] = (T)objectArray[i];
|
||||
arrayIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
int count = objectArray.Count;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
yield return (T)objectArray[i];
|
||||
}
|
||||
}
|
||||
|
||||
public int IndexOf(T item)
|
||||
{
|
||||
return objectArray.IndexOf(item);
|
||||
}
|
||||
|
||||
public void Insert(int index, T item)
|
||||
{
|
||||
objectArray.Insert(index, item);
|
||||
}
|
||||
|
||||
public bool Remove(T item)
|
||||
{
|
||||
return objectArray.Remove(item);
|
||||
}
|
||||
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
objectArray.RemoveAt(index);
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,401 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
class DictionarySafeHandle : SafeHandle
|
||||
{
|
||||
public DictionarySafeHandle(IntPtr handle) : base(IntPtr.Zero, true)
|
||||
{
|
||||
this.handle = handle;
|
||||
}
|
||||
|
||||
public override bool IsInvalid
|
||||
{
|
||||
get
|
||||
{
|
||||
return handle == IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
Dictionary.godot_icall_Dictionary_Dtor(handle);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public class Dictionary :
|
||||
IDictionary<object, object>,
|
||||
ICollection<KeyValuePair<object, object>>,
|
||||
IEnumerable<KeyValuePair<object, object>>,
|
||||
IDisposable
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static IntPtr godot_icall_Dictionary_Ctor();
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static void godot_icall_Dictionary_Dtor(IntPtr ptr);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static object godot_icall_Dictionary_GetValue(IntPtr ptr, object key);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static void godot_icall_Dictionary_SetValue(IntPtr ptr, object key, object value);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static IntPtr godot_icall_Dictionary_Keys(IntPtr ptr);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static IntPtr godot_icall_Dictionary_Values(IntPtr ptr);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static int godot_icall_Dictionary_Count(IntPtr ptr);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static void godot_icall_Dictionary_Add(IntPtr ptr, object key, object value);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static void godot_icall_Dictionary_Clear(IntPtr ptr);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static bool godot_icall_Dictionary_Contains(IntPtr ptr, object key, object value);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static bool godot_icall_Dictionary_ContainsKey(IntPtr ptr, object key);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static bool godot_icall_Dictionary_RemoveKey(IntPtr ptr, object key);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static bool godot_icall_Dictionary_Remove(IntPtr ptr, object key, object value);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static bool godot_icall_Dictionary_TryGetValue(IntPtr ptr, object key, out object value);
|
||||
|
||||
DictionarySafeHandle safeHandle;
|
||||
bool disposed = false;
|
||||
|
||||
public Dictionary()
|
||||
{
|
||||
safeHandle = new DictionarySafeHandle(godot_icall_Dictionary_Ctor());
|
||||
}
|
||||
|
||||
internal Dictionary(DictionarySafeHandle handle)
|
||||
{
|
||||
safeHandle = handle;
|
||||
}
|
||||
|
||||
internal Dictionary(IntPtr handle)
|
||||
{
|
||||
safeHandle = new DictionarySafeHandle(handle);
|
||||
}
|
||||
|
||||
internal IntPtr GetPtr()
|
||||
{
|
||||
return safeHandle.DangerousGetHandle();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposed)
|
||||
return;
|
||||
|
||||
if (safeHandle != null)
|
||||
{
|
||||
safeHandle.Dispose();
|
||||
safeHandle = null;
|
||||
}
|
||||
|
||||
disposed = true;
|
||||
}
|
||||
|
||||
public object this[object key]
|
||||
{
|
||||
get
|
||||
{
|
||||
return godot_icall_Dictionary_GetValue(GetPtr(), key);
|
||||
}
|
||||
set
|
||||
{
|
||||
godot_icall_Dictionary_SetValue(GetPtr(), key, value);
|
||||
}
|
||||
}
|
||||
|
||||
public ICollection<object> Keys
|
||||
{
|
||||
get
|
||||
{
|
||||
IntPtr handle = godot_icall_Dictionary_Keys(GetPtr());
|
||||
return new Array(new ArraySafeHandle(handle));
|
||||
}
|
||||
}
|
||||
|
||||
public ICollection<object> Values
|
||||
{
|
||||
get
|
||||
{
|
||||
IntPtr handle = godot_icall_Dictionary_Values(GetPtr());
|
||||
return new Array(new ArraySafeHandle(handle));
|
||||
}
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
return godot_icall_Dictionary_Count(GetPtr());
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsReadOnly
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void Add(object key, object value)
|
||||
{
|
||||
godot_icall_Dictionary_Add(GetPtr(), key, value);
|
||||
}
|
||||
|
||||
public void Add(KeyValuePair<object, object> item)
|
||||
{
|
||||
Add(item.Key, item.Value);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
godot_icall_Dictionary_Clear(GetPtr());
|
||||
}
|
||||
|
||||
public bool Contains(KeyValuePair<object, object> item)
|
||||
{
|
||||
return godot_icall_Dictionary_Contains(GetPtr(), item.Key, item.Value);
|
||||
}
|
||||
|
||||
public bool ContainsKey(object key)
|
||||
{
|
||||
return godot_icall_Dictionary_ContainsKey(GetPtr(), key);
|
||||
}
|
||||
|
||||
public void CopyTo(KeyValuePair<object, object>[] array, int arrayIndex)
|
||||
{
|
||||
// TODO 3 internal calls, can reduce to 1
|
||||
Array keys = (Array)Keys;
|
||||
Array values = (Array)Values;
|
||||
int count = Count;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
// TODO 2 internal calls, can reduce to 1
|
||||
array[arrayIndex] = new KeyValuePair<object, object>(keys[i], values[i]);
|
||||
arrayIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<KeyValuePair<object, object>> GetEnumerator()
|
||||
{
|
||||
// TODO 3 internal calls, can reduce to 1
|
||||
Array keys = (Array)Keys;
|
||||
Array values = (Array)Values;
|
||||
int count = Count;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
// TODO 2 internal calls, can reduce to 1
|
||||
yield return new KeyValuePair<object, object>(keys[i], values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Remove(object key)
|
||||
{
|
||||
return godot_icall_Dictionary_RemoveKey(GetPtr(), key);
|
||||
}
|
||||
|
||||
public bool Remove(KeyValuePair<object, object> item)
|
||||
{
|
||||
return godot_icall_Dictionary_Remove(GetPtr(), item.Key, item.Value);
|
||||
}
|
||||
|
||||
public bool TryGetValue(object key, out object value)
|
||||
{
|
||||
object retValue;
|
||||
bool found = godot_icall_Dictionary_TryGetValue(GetPtr(), key, out retValue);
|
||||
value = found ? retValue : default(object);
|
||||
return found;
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class Dictionary<TKey, TValue> :
|
||||
IDictionary<TKey, TValue>,
|
||||
ICollection<KeyValuePair<TKey, TValue>>,
|
||||
IEnumerable<KeyValuePair<TKey, TValue>>
|
||||
{
|
||||
Dictionary objectDict;
|
||||
|
||||
public Dictionary()
|
||||
{
|
||||
objectDict = new Dictionary();
|
||||
}
|
||||
|
||||
public Dictionary(Dictionary dictionary)
|
||||
{
|
||||
objectDict = dictionary;
|
||||
}
|
||||
|
||||
internal Dictionary(IntPtr handle)
|
||||
{
|
||||
objectDict = new Dictionary(handle);
|
||||
}
|
||||
|
||||
internal Dictionary(DictionarySafeHandle handle)
|
||||
{
|
||||
objectDict = new Dictionary(handle);
|
||||
}
|
||||
|
||||
public static explicit operator Dictionary(Dictionary<TKey, TValue> from)
|
||||
{
|
||||
return from.objectDict;
|
||||
}
|
||||
|
||||
public TValue this[TKey key]
|
||||
{
|
||||
get
|
||||
{
|
||||
return (TValue)objectDict[key];
|
||||
}
|
||||
set
|
||||
{
|
||||
objectDict[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public ICollection<TKey> Keys
|
||||
{
|
||||
get
|
||||
{
|
||||
IntPtr handle = Dictionary.godot_icall_Dictionary_Keys(objectDict.GetPtr());
|
||||
return new Array<TKey>(new ArraySafeHandle(handle));
|
||||
}
|
||||
}
|
||||
|
||||
public ICollection<TValue> Values
|
||||
{
|
||||
get
|
||||
{
|
||||
IntPtr handle = Dictionary.godot_icall_Dictionary_Values(objectDict.GetPtr());
|
||||
return new Array<TValue>(new ArraySafeHandle(handle));
|
||||
}
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
return objectDict.Count;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsReadOnly
|
||||
{
|
||||
get
|
||||
{
|
||||
return objectDict.IsReadOnly;
|
||||
}
|
||||
}
|
||||
|
||||
public void Add(TKey key, TValue value)
|
||||
{
|
||||
objectDict.Add(key, value);
|
||||
}
|
||||
|
||||
public void Add(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
objectDict.Add(item.Key, item.Value);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
objectDict.Clear();
|
||||
}
|
||||
|
||||
public bool Contains(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
return objectDict.Contains(new KeyValuePair<object, object>(item.Key, item.Value));
|
||||
}
|
||||
|
||||
public bool ContainsKey(TKey key)
|
||||
{
|
||||
return objectDict.ContainsKey(key);
|
||||
}
|
||||
|
||||
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
|
||||
{
|
||||
// TODO 3 internal calls, can reduce to 1
|
||||
Array<TKey> keys = (Array<TKey>)Keys;
|
||||
Array<TValue> values = (Array<TValue>)Values;
|
||||
int count = Count;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
// TODO 2 internal calls, can reduce to 1
|
||||
array[arrayIndex] = new KeyValuePair<TKey, TValue>(keys[i], values[i]);
|
||||
arrayIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
|
||||
{
|
||||
// TODO 3 internal calls, can reduce to 1
|
||||
Array<TKey> keys = (Array<TKey>)Keys;
|
||||
Array<TValue> values = (Array<TValue>)Values;
|
||||
int count = Count;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
// TODO 2 internal calls, can reduce to 1
|
||||
yield return new KeyValuePair<TKey, TValue>(keys[i], values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Remove(TKey key)
|
||||
{
|
||||
return objectDict.Remove(key);
|
||||
}
|
||||
|
||||
public bool Remove(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
return objectDict.Remove(new KeyValuePair<object, object>(item.Key, item.Value));
|
||||
}
|
||||
|
||||
public bool TryGetValue(TKey key, out TValue value)
|
||||
{
|
||||
object retValue;
|
||||
bool found = objectDict.TryGetValue(key, out retValue);
|
||||
value = found ? (TValue)retValue : default(TValue);
|
||||
return found;
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,36 +1,17 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
internal static class MarshalUtils
|
||||
static class MarshalUtils
|
||||
{
|
||||
private static Dictionary<object, object> ArraysToDictionary(object[] keys, object[] values)
|
||||
static bool IsArrayGenericType(Type type)
|
||||
{
|
||||
var ret = new Dictionary<object, object>();
|
||||
|
||||
for (int i = 0; i < keys.Length; i++)
|
||||
{
|
||||
ret.Add(keys[i], values[i]);
|
||||
}
|
||||
|
||||
return ret;
|
||||
return type.GetGenericTypeDefinition() == typeof(Array<>);
|
||||
}
|
||||
|
||||
private static void DictionaryToArrays(Dictionary<object, object> from, out object[] keysTo, out object[] valuesTo)
|
||||
static bool IsDictionaryGenericType(Type type)
|
||||
{
|
||||
var keys = from.Keys;
|
||||
keysTo = new object[keys.Count];
|
||||
keys.CopyTo(keysTo, 0);
|
||||
|
||||
var values = from.Values;
|
||||
valuesTo = new object[values.Count];
|
||||
values.CopyTo(valuesTo, 0);
|
||||
}
|
||||
|
||||
private static Type GetDictionaryType()
|
||||
{
|
||||
return typeof(Dictionary<object, object>);
|
||||
return type.GetGenericTypeDefinition() == typeof(Dictionary<, >);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
/*************************************************************************/
|
||||
|
||||
#include "builtin_types_glue.h"
|
||||
#include "collections_glue.h"
|
||||
|
||||
#include "../csharp_script.h"
|
||||
#include "../mono_gd/gd_mono_class.h"
|
||||
|
@ -308,4 +309,5 @@ MonoObject *godot_icall_Godot_weakref(Object *p_obj) {
|
|||
|
||||
void godot_register_header_icalls() {
|
||||
godot_register_builtin_type_icalls();
|
||||
godot_register_collections_icalls();
|
||||
}
|
||||
|
|
|
@ -33,10 +33,32 @@
|
|||
#include <mono/metadata/attrdefs.h>
|
||||
|
||||
#include "gd_mono_assembly.h"
|
||||
#include "gd_mono_marshal.h"
|
||||
|
||||
MonoType *GDMonoClass::get_raw_type(GDMonoClass *p_class) {
|
||||
String GDMonoClass::get_full_name(MonoClass *p_mono_class) {
|
||||
// mono_type_get_full_name is not exposed to embedders, but this seems to do the job
|
||||
MonoReflectionType *type_obj = mono_type_get_object(mono_domain_get(), get_mono_type(p_mono_class));
|
||||
|
||||
return mono_class_get_type(p_class->get_mono_ptr());
|
||||
MonoException *exc = NULL;
|
||||
GD_MONO_BEGIN_RUNTIME_INVOKE;
|
||||
MonoString *str = mono_object_to_string((MonoObject *)type_obj, (MonoObject **)&exc);
|
||||
GD_MONO_END_RUNTIME_INVOKE;
|
||||
UNLIKELY_UNHANDLED_EXCEPTION(exc);
|
||||
|
||||
return GDMonoMarshal::mono_string_to_godot(str);
|
||||
}
|
||||
|
||||
MonoType *GDMonoClass::get_mono_type(MonoClass *p_mono_class) {
|
||||
return mono_class_get_type(p_mono_class);
|
||||
}
|
||||
|
||||
String GDMonoClass::get_full_name() const {
|
||||
return get_full_name(mono_class);
|
||||
}
|
||||
|
||||
MonoType *GDMonoClass::get_mono_type() {
|
||||
// Care, you cannot compare MonoType pointers
|
||||
return get_mono_type(mono_class);
|
||||
}
|
||||
|
||||
bool GDMonoClass::is_assignable_from(GDMonoClass *p_from) const {
|
||||
|
@ -44,14 +66,6 @@ bool GDMonoClass::is_assignable_from(GDMonoClass *p_from) const {
|
|||
return mono_class_is_assignable_from(mono_class, p_from->mono_class);
|
||||
}
|
||||
|
||||
String GDMonoClass::get_full_name() const {
|
||||
|
||||
String res = namespace_name;
|
||||
if (res.length())
|
||||
res += ".";
|
||||
return res + class_name;
|
||||
}
|
||||
|
||||
GDMonoClass *GDMonoClass::get_parent_class() {
|
||||
|
||||
if (assembly) {
|
||||
|
|
|
@ -98,7 +98,11 @@ class GDMonoClass {
|
|||
GDMonoClass(const StringName &p_namespace, const StringName &p_name, MonoClass *p_class, GDMonoAssembly *p_assembly);
|
||||
|
||||
public:
|
||||
static MonoType *get_raw_type(GDMonoClass *p_class);
|
||||
static String get_full_name(MonoClass *p_mono_class);
|
||||
static MonoType *get_mono_type(MonoClass *p_mono_class);
|
||||
|
||||
String get_full_name() const;
|
||||
MonoType *get_mono_type();
|
||||
|
||||
bool is_assignable_from(GDMonoClass *p_from) const;
|
||||
|
||||
|
@ -108,8 +112,6 @@ public:
|
|||
_FORCE_INLINE_ MonoClass *get_mono_ptr() const { return mono_class; }
|
||||
_FORCE_INLINE_ const GDMonoAssembly *get_assembly() const { return assembly; }
|
||||
|
||||
String get_full_name() const;
|
||||
|
||||
GDMonoClass *get_parent_class();
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
|
|
|
@ -148,7 +148,7 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
|
|||
|
||||
case MONO_TYPE_ARRAY:
|
||||
case MONO_TYPE_SZARRAY: {
|
||||
MonoArrayType *array_type = mono_type_get_array_type(GDMonoClass::get_raw_type(type.type_class));
|
||||
MonoArrayType *array_type = mono_type_get_array_type(type.type_class->get_mono_type());
|
||||
|
||||
if (array_type->eklass == CACHED_CLASS_RAW(MonoObject))
|
||||
SET_FROM_ARRAY_AND_BREAK(Array);
|
||||
|
@ -200,6 +200,18 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
|
|||
break;
|
||||
}
|
||||
|
||||
if (CACHED_CLASS(Dictionary) == type_class) {
|
||||
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), CACHED_CLASS(Dictionary));
|
||||
mono_field_set_value(p_object, mono_field, managed);
|
||||
break;
|
||||
}
|
||||
|
||||
if (CACHED_CLASS(Array) == type_class) {
|
||||
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array));
|
||||
mono_field_set_value(p_object, mono_field, managed);
|
||||
break;
|
||||
}
|
||||
|
||||
ERR_EXPLAIN(String() + "Attempted to set the value of a field of unmarshallable type: " + type_class->get_name());
|
||||
ERR_FAIL();
|
||||
} break;
|
||||
|
@ -248,10 +260,13 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
|
|||
break;
|
||||
}
|
||||
case Variant::DICTIONARY: {
|
||||
MonoObject *managed = GDMonoMarshal::Dictionary_to_mono_object(p_value.operator Dictionary());
|
||||
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), CACHED_CLASS(Dictionary));
|
||||
mono_field_set_value(p_object, mono_field, managed);
|
||||
} break;
|
||||
case Variant::ARRAY: {
|
||||
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array));
|
||||
mono_field_set_value(p_object, mono_field, managed);
|
||||
} break;
|
||||
case Variant::ARRAY: SET_FROM_ARRAY_AND_BREAK(Array);
|
||||
case Variant::POOL_BYTE_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolByteArray);
|
||||
case Variant::POOL_INT_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolIntArray);
|
||||
case Variant::POOL_REAL_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolRealArray);
|
||||
|
@ -265,8 +280,28 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
|
|||
} break;
|
||||
|
||||
case MONO_TYPE_GENERICINST: {
|
||||
if (CACHED_RAW_MONO_CLASS(Dictionary) == type.type_class->get_mono_ptr()) {
|
||||
MonoObject *managed = GDMonoMarshal::Dictionary_to_mono_object(p_value.operator Dictionary());
|
||||
MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type.type_class->get_mono_type());
|
||||
|
||||
MonoException *exc = NULL;
|
||||
|
||||
GDMonoUtils::IsDictionaryGenericType type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, IsDictionaryGenericType);
|
||||
MonoBoolean is_dict = type_is_dict((MonoObject *)reftype, (MonoObject **)&exc);
|
||||
UNLIKELY_UNHANDLED_EXCEPTION(exc);
|
||||
|
||||
if (is_dict) {
|
||||
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), type.type_class);
|
||||
mono_field_set_value(p_object, mono_field, managed);
|
||||
break;
|
||||
}
|
||||
|
||||
exc = NULL;
|
||||
|
||||
GDMonoUtils::IsArrayGenericType type_is_array = CACHED_METHOD_THUNK(MarshalUtils, IsArrayGenericType);
|
||||
MonoBoolean is_array = type_is_array((MonoObject *)reftype, (MonoObject **)&exc);
|
||||
UNLIKELY_UNHANDLED_EXCEPTION(exc);
|
||||
|
||||
if (is_array) {
|
||||
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), type.type_class);
|
||||
mono_field_set_value(p_object, mono_field, managed);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -45,7 +45,8 @@ struct ManagedType {
|
|||
GDMonoClass *type_class;
|
||||
|
||||
ManagedType() {
|
||||
type_class = 0;
|
||||
type_encoding = 0;
|
||||
type_class = NULL;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -120,7 +120,7 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) {
|
|||
|
||||
case MONO_TYPE_ARRAY:
|
||||
case MONO_TYPE_SZARRAY: {
|
||||
MonoArrayType *array_type = mono_type_get_array_type(GDMonoClass::get_raw_type(p_type.type_class));
|
||||
MonoArrayType *array_type = mono_type_get_array_type(p_type.type_class->get_mono_type());
|
||||
|
||||
if (array_type->eklass == CACHED_CLASS_RAW(MonoObject))
|
||||
return Variant::ARRAY;
|
||||
|
@ -162,12 +162,36 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) {
|
|||
if (CACHED_CLASS(RID) == type_class) {
|
||||
return Variant::_RID;
|
||||
}
|
||||
|
||||
if (CACHED_CLASS(Dictionary) == type_class) {
|
||||
return Variant::DICTIONARY;
|
||||
}
|
||||
|
||||
if (CACHED_CLASS(Array) == type_class) {
|
||||
return Variant::ARRAY;
|
||||
}
|
||||
} break;
|
||||
|
||||
case MONO_TYPE_GENERICINST: {
|
||||
if (CACHED_RAW_MONO_CLASS(Dictionary) == p_type.type_class->get_mono_ptr()) {
|
||||
MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_type.type_class->get_mono_type());
|
||||
|
||||
MonoException *exc = NULL;
|
||||
GDMonoUtils::IsDictionaryGenericType type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, IsDictionaryGenericType);
|
||||
MonoBoolean is_dict = type_is_dict((MonoObject *)reftype, (MonoObject **)&exc);
|
||||
UNLIKELY_UNHANDLED_EXCEPTION(exc);
|
||||
|
||||
if (is_dict) {
|
||||
return Variant::DICTIONARY;
|
||||
}
|
||||
|
||||
exc = NULL;
|
||||
GDMonoUtils::IsArrayGenericType type_is_array = CACHED_METHOD_THUNK(MarshalUtils, IsArrayGenericType);
|
||||
MonoBoolean is_array = type_is_array((MonoObject *)reftype, (MonoObject **)&exc);
|
||||
UNLIKELY_UNHANDLED_EXCEPTION(exc);
|
||||
|
||||
if (is_array) {
|
||||
return Variant::ARRAY;
|
||||
}
|
||||
} break;
|
||||
|
||||
default: {
|
||||
|
@ -216,6 +240,7 @@ MonoObject *variant_to_mono_object(const Variant *p_var) {
|
|||
ManagedType type;
|
||||
|
||||
type.type_encoding = MONO_TYPE_OBJECT;
|
||||
// type.type_class is not needed when we specify the MONO_TYPE_OBJECT encoding
|
||||
|
||||
return variant_to_mono_object(p_var, type);
|
||||
}
|
||||
|
@ -315,7 +340,7 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
|
|||
|
||||
case MONO_TYPE_ARRAY:
|
||||
case MONO_TYPE_SZARRAY: {
|
||||
MonoArrayType *array_type = mono_type_get_array_type(GDMonoClass::get_raw_type(p_type.type_class));
|
||||
MonoArrayType *array_type = mono_type_get_array_type(p_type.type_class->get_mono_type());
|
||||
|
||||
if (array_type->eklass == CACHED_CLASS_RAW(MonoObject))
|
||||
return (MonoObject *)Array_to_mono_array(p_var->operator Array());
|
||||
|
@ -360,6 +385,14 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
|
|||
if (CACHED_CLASS(RID) == type_class) {
|
||||
return GDMonoUtils::create_managed_from(p_var->operator RID());
|
||||
}
|
||||
|
||||
if (CACHED_CLASS(Dictionary) == type_class) {
|
||||
return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary));
|
||||
}
|
||||
|
||||
if (CACHED_CLASS(Array) == type_class) {
|
||||
return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array));
|
||||
}
|
||||
} break;
|
||||
case MONO_TYPE_OBJECT: {
|
||||
// Variant
|
||||
|
@ -411,9 +444,9 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
|
|||
return GDMonoUtils::unmanaged_get_managed(p_var->operator Object *());
|
||||
}
|
||||
case Variant::DICTIONARY:
|
||||
return Dictionary_to_mono_object(p_var->operator Dictionary());
|
||||
return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary));
|
||||
case Variant::ARRAY:
|
||||
return (MonoObject *)Array_to_mono_array(p_var->operator Array());
|
||||
return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array));
|
||||
case Variant::POOL_BYTE_ARRAY:
|
||||
return (MonoObject *)PoolByteArray_to_mono_array(p_var->operator PoolByteArray());
|
||||
case Variant::POOL_INT_ARRAY:
|
||||
|
@ -433,8 +466,24 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
|
|||
}
|
||||
break;
|
||||
case MONO_TYPE_GENERICINST: {
|
||||
if (CACHED_RAW_MONO_CLASS(Dictionary) == p_type.type_class->get_mono_ptr()) {
|
||||
return Dictionary_to_mono_object(p_var->operator Dictionary());
|
||||
MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_type.type_class->get_mono_type());
|
||||
|
||||
MonoException *exc = NULL;
|
||||
GDMonoUtils::IsDictionaryGenericType type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, IsDictionaryGenericType);
|
||||
MonoBoolean is_dict = type_is_dict((MonoObject *)reftype, (MonoObject **)&exc);
|
||||
UNLIKELY_UNHANDLED_EXCEPTION(exc);
|
||||
|
||||
if (is_dict) {
|
||||
return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), p_type.type_class);
|
||||
}
|
||||
|
||||
exc = NULL;
|
||||
GDMonoUtils::IsArrayGenericType type_is_array = CACHED_METHOD_THUNK(MarshalUtils, IsArrayGenericType);
|
||||
MonoBoolean is_array = type_is_array((MonoObject *)reftype, (MonoObject **)&exc);
|
||||
UNLIKELY_UNHANDLED_EXCEPTION(exc);
|
||||
|
||||
if (is_array) {
|
||||
return GDMonoUtils::create_managed_from(p_var->operator Array(), p_type.type_class);
|
||||
}
|
||||
} break;
|
||||
} break;
|
||||
|
@ -452,7 +501,7 @@ Variant mono_object_to_variant(MonoObject *p_obj) {
|
|||
GDMonoClass *tclass = GDMono::get_singleton()->get_class(mono_object_get_class(p_obj));
|
||||
ERR_FAIL_COND_V(!tclass, Variant());
|
||||
|
||||
MonoType *raw_type = tclass->get_raw_type(tclass);
|
||||
MonoType *raw_type = tclass->get_mono_type();
|
||||
|
||||
ManagedType type;
|
||||
|
||||
|
@ -531,7 +580,7 @@ Variant mono_object_to_variant(MonoObject *p_obj) {
|
|||
|
||||
case MONO_TYPE_ARRAY:
|
||||
case MONO_TYPE_SZARRAY: {
|
||||
MonoArrayType *array_type = mono_type_get_array_type(GDMonoClass::get_raw_type(type.type_class));
|
||||
MonoArrayType *array_type = mono_type_get_array_type(type.type_class->get_mono_type());
|
||||
|
||||
if (array_type->eklass == CACHED_CLASS_RAW(MonoObject))
|
||||
return mono_array_to_Array((MonoArray *)p_obj);
|
||||
|
@ -579,11 +628,51 @@ Variant mono_object_to_variant(MonoObject *p_obj) {
|
|||
RID *ptr = unbox<RID *>(CACHED_FIELD(RID, ptr)->get_value(p_obj));
|
||||
return ptr ? Variant(*ptr) : Variant();
|
||||
}
|
||||
|
||||
if (CACHED_CLASS(Array) == type_class) {
|
||||
MonoException *exc = NULL;
|
||||
GDMonoUtils::Array_GetPtr get_ptr = CACHED_METHOD_THUNK(Array, GetPtr);
|
||||
Array *ptr = get_ptr(p_obj, (MonoObject **)&exc);
|
||||
UNLIKELY_UNHANDLED_EXCEPTION(exc);
|
||||
return ptr ? Variant(*ptr) : Variant();
|
||||
}
|
||||
|
||||
if (CACHED_CLASS(Dictionary) == type_class) {
|
||||
MonoException *exc = NULL;
|
||||
GDMonoUtils::Dictionary_GetPtr get_ptr = CACHED_METHOD_THUNK(Dictionary, GetPtr);
|
||||
Dictionary *ptr = get_ptr(p_obj, (MonoObject **)&exc);
|
||||
UNLIKELY_UNHANDLED_EXCEPTION(exc);
|
||||
return ptr ? Variant(*ptr) : Variant();
|
||||
}
|
||||
} break;
|
||||
|
||||
case MONO_TYPE_GENERICINST: {
|
||||
if (CACHED_RAW_MONO_CLASS(Dictionary) == type.type_class->get_mono_ptr()) {
|
||||
return mono_object_to_Dictionary(p_obj);
|
||||
MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type.type_class->get_mono_type());
|
||||
|
||||
MonoException *exc = NULL;
|
||||
|
||||
GDMonoUtils::IsDictionaryGenericType type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, IsDictionaryGenericType);
|
||||
MonoBoolean is_dict = type_is_dict((MonoObject *)reftype, (MonoObject **)&exc);
|
||||
UNLIKELY_UNHANDLED_EXCEPTION(exc);
|
||||
|
||||
if (is_dict) {
|
||||
MonoException *exc = NULL;
|
||||
MonoObject *ret = type.type_class->get_method("GetPtr")->invoke(p_obj, &exc);
|
||||
UNLIKELY_UNHANDLED_EXCEPTION(exc);
|
||||
return *unbox<Dictionary *>(ret);
|
||||
}
|
||||
|
||||
exc = NULL;
|
||||
|
||||
GDMonoUtils::IsArrayGenericType type_is_array = CACHED_METHOD_THUNK(MarshalUtils, IsArrayGenericType);
|
||||
MonoBoolean is_array = type_is_array((MonoObject *)reftype, (MonoObject **)&exc);
|
||||
UNLIKELY_UNHANDLED_EXCEPTION(exc);
|
||||
|
||||
if (is_array) {
|
||||
MonoException *exc = NULL;
|
||||
MonoObject *ret = type.type_class->get_method("GetPtr")->invoke(p_obj, &exc);
|
||||
UNLIKELY_UNHANDLED_EXCEPTION(exc);
|
||||
return *unbox<Array *>(ret);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
@ -822,66 +911,4 @@ PoolVector3Array mono_array_to_PoolVector3Array(MonoArray *p_array) {
|
|||
|
||||
return ret;
|
||||
}
|
||||
|
||||
MonoObject *Dictionary_to_mono_object(const Dictionary &p_dict) {
|
||||
MonoArray *keys = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), p_dict.size());
|
||||
MonoArray *values = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), p_dict.size());
|
||||
|
||||
int i = 0;
|
||||
const Variant *dkey = NULL;
|
||||
while ((dkey = p_dict.next(dkey))) {
|
||||
mono_array_set(keys, MonoObject *, i, variant_to_mono_object(dkey));
|
||||
mono_array_set(values, MonoObject *, i, variant_to_mono_object(p_dict[*dkey]));
|
||||
i++;
|
||||
}
|
||||
|
||||
GDMonoUtils::MarshalUtils_ArraysToDict arrays_to_dict = CACHED_METHOD_THUNK(MarshalUtils, ArraysToDictionary);
|
||||
|
||||
MonoException *exc = NULL;
|
||||
GD_MONO_BEGIN_RUNTIME_INVOKE;
|
||||
MonoObject *ret = arrays_to_dict(keys, values, (MonoObject **)&exc);
|
||||
GD_MONO_END_RUNTIME_INVOKE;
|
||||
|
||||
if (exc) {
|
||||
GDMonoUtils::set_pending_exception(exc);
|
||||
ERR_FAIL_V(NULL);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Dictionary mono_object_to_Dictionary(MonoObject *p_dict) {
|
||||
Dictionary ret;
|
||||
|
||||
if (!p_dict)
|
||||
return ret;
|
||||
|
||||
GDMonoUtils::MarshalUtils_DictToArrays dict_to_arrays = CACHED_METHOD_THUNK(MarshalUtils, DictionaryToArrays);
|
||||
|
||||
MonoArray *keys = NULL;
|
||||
MonoArray *values = NULL;
|
||||
MonoException *exc = NULL;
|
||||
GD_MONO_BEGIN_RUNTIME_INVOKE;
|
||||
dict_to_arrays(p_dict, &keys, &values, (MonoObject **)&exc);
|
||||
GD_MONO_END_RUNTIME_INVOKE;
|
||||
|
||||
if (exc) {
|
||||
GDMonoUtils::set_pending_exception(exc);
|
||||
ERR_FAIL_V(Dictionary());
|
||||
}
|
||||
|
||||
int length = mono_array_length(keys);
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
MonoObject *key_obj = mono_array_get(keys, MonoObject *, i);
|
||||
MonoObject *value_obj = mono_array_get(values, MonoObject *, i);
|
||||
|
||||
Variant key = key_obj ? mono_object_to_variant(key_obj) : Variant();
|
||||
Variant value = value_obj ? mono_object_to_variant(value_obj) : Variant();
|
||||
|
||||
ret[key] = value;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
} // namespace GDMonoMarshal
|
||||
|
|
|
@ -143,11 +143,6 @@ PoolVector2Array mono_array_to_PoolVector2Array(MonoArray *p_array);
|
|||
MonoArray *PoolVector3Array_to_mono_array(const PoolVector3Array &p_array);
|
||||
PoolVector3Array mono_array_to_PoolVector3Array(MonoArray *p_array);
|
||||
|
||||
// Dictionary
|
||||
|
||||
MonoObject *Dictionary_to_mono_object(const Dictionary &p_dict);
|
||||
Dictionary mono_object_to_Dictionary(MonoObject *p_dict);
|
||||
|
||||
#ifdef YOLO_COPY
|
||||
#define MARSHALLED_OUT(m_t, m_in, m_out) m_t *m_out = (m_t *)&m_in;
|
||||
#define MARSHALLED_IN(m_t, m_in, m_out) m_t m_out = *reinterpret_cast<m_t *>(m_in);
|
||||
|
|
|
@ -139,23 +139,8 @@ bool GDMonoProperty::has_setter() {
|
|||
}
|
||||
|
||||
void GDMonoProperty::set_value(MonoObject *p_object, MonoObject *p_value, MonoException **r_exc) {
|
||||
MonoMethod *prop_method = mono_property_get_set_method(mono_property);
|
||||
|
||||
MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), 1);
|
||||
mono_array_set(params, MonoObject *, 0, p_value);
|
||||
|
||||
MonoException *exc = NULL;
|
||||
GD_MONO_BEGIN_RUNTIME_INVOKE;
|
||||
mono_runtime_invoke_array(prop_method, p_object, params, (MonoObject **)&exc);
|
||||
GD_MONO_END_RUNTIME_INVOKE;
|
||||
|
||||
if (exc) {
|
||||
if (r_exc) {
|
||||
*r_exc = exc;
|
||||
} else {
|
||||
GDMonoUtils::set_pending_exception(exc);
|
||||
}
|
||||
}
|
||||
void *params[1] = { p_value };
|
||||
set_value(p_object, params, r_exc);
|
||||
}
|
||||
|
||||
void GDMonoProperty::set_value(MonoObject *p_object, void **p_params, MonoException **r_exc) {
|
||||
|
|
|
@ -87,6 +87,8 @@ void MonoCache::clear_members() {
|
|||
method_System_Diagnostics_StackTrace_ctor_Exception_bool = NULL;
|
||||
#endif
|
||||
|
||||
class_KeyNotFoundException = NULL;
|
||||
|
||||
rawclass_Dictionary = NULL;
|
||||
|
||||
class_Vector2 = NULL;
|
||||
|
@ -107,6 +109,8 @@ void MonoCache::clear_members() {
|
|||
class_Control = NULL;
|
||||
class_Spatial = NULL;
|
||||
class_WeakRef = NULL;
|
||||
class_Array = NULL;
|
||||
class_Dictionary = NULL;
|
||||
class_MarshalUtils = NULL;
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
|
@ -134,8 +138,10 @@ void MonoCache::clear_members() {
|
|||
field_Image_ptr = NULL;
|
||||
field_RID_ptr = NULL;
|
||||
|
||||
methodthunk_MarshalUtils_DictionaryToArrays = NULL;
|
||||
methodthunk_MarshalUtils_ArraysToDictionary = NULL;
|
||||
methodthunk_Array_GetPtr = NULL;
|
||||
methodthunk_Dictionary_GetPtr = NULL;
|
||||
methodthunk_MarshalUtils_IsArrayGenericType = NULL;
|
||||
methodthunk_MarshalUtils_IsDictionaryGenericType = NULL;
|
||||
methodthunk_SignalAwaiter_SignalCallback = NULL;
|
||||
methodthunk_SignalAwaiter_FailureCallback = NULL;
|
||||
methodthunk_GodotTaskScheduler_Activate = NULL;
|
||||
|
@ -175,6 +181,8 @@ void update_corlib_cache() {
|
|||
CACHE_METHOD_AND_CHECK(System_Diagnostics_StackTrace, ctor_Exception_bool, CACHED_CLASS(System_Diagnostics_StackTrace)->get_method_with_desc("System.Diagnostics.StackTrace:.ctor(System.Exception,bool)", true));
|
||||
#endif
|
||||
|
||||
CACHE_CLASS_AND_CHECK(KeyNotFoundException, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections.Generic", "KeyNotFoundException"));
|
||||
|
||||
mono_cache.corlib_cache_updated = true;
|
||||
}
|
||||
|
||||
|
@ -198,6 +206,8 @@ void update_godot_api_cache() {
|
|||
CACHE_CLASS_AND_CHECK(Control, GODOT_API_CLASS(Control));
|
||||
CACHE_CLASS_AND_CHECK(Spatial, GODOT_API_CLASS(Spatial));
|
||||
CACHE_CLASS_AND_CHECK(WeakRef, GODOT_API_CLASS(WeakRef));
|
||||
CACHE_CLASS_AND_CHECK(Array, GODOT_API_CLASS(Array));
|
||||
CACHE_CLASS_AND_CHECK(Dictionary, GODOT_API_CLASS(Dictionary));
|
||||
CACHE_CLASS_AND_CHECK(MarshalUtils, GODOT_API_CLASS(MarshalUtils));
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
|
@ -224,8 +234,10 @@ void update_godot_api_cache() {
|
|||
CACHE_FIELD_AND_CHECK(NodePath, ptr, CACHED_CLASS(NodePath)->get_field(BINDINGS_PTR_FIELD));
|
||||
CACHE_FIELD_AND_CHECK(RID, ptr, CACHED_CLASS(RID)->get_field(BINDINGS_PTR_FIELD));
|
||||
|
||||
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, DictionaryToArrays, (MarshalUtils_DictToArrays)CACHED_CLASS(MarshalUtils)->get_method("DictionaryToArrays", 3)->get_thunk());
|
||||
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, ArraysToDictionary, (MarshalUtils_ArraysToDict)CACHED_CLASS(MarshalUtils)->get_method("ArraysToDictionary", 2)->get_thunk());
|
||||
CACHE_METHOD_THUNK_AND_CHECK(Array, GetPtr, (Array_GetPtr)GODOT_API_CLASS(Array)->get_method("GetPtr", 0)->get_thunk());
|
||||
CACHE_METHOD_THUNK_AND_CHECK(Dictionary, GetPtr, (Dictionary_GetPtr)GODOT_API_CLASS(Dictionary)->get_method("GetPtr", 0)->get_thunk());
|
||||
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, IsArrayGenericType, (IsArrayGenericType)GODOT_API_CLASS(MarshalUtils)->get_method("IsArrayGenericType", 1)->get_thunk());
|
||||
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, IsDictionaryGenericType, (IsDictionaryGenericType)GODOT_API_CLASS(MarshalUtils)->get_method("IsDictionaryGenericType", 1)->get_thunk());
|
||||
CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, SignalCallback, (SignalAwaiter_SignalCallback)GODOT_API_CLASS(SignalAwaiter)->get_method("SignalCallback", 1)->get_thunk());
|
||||
CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, FailureCallback, (SignalAwaiter_FailureCallback)GODOT_API_CLASS(SignalAwaiter)->get_method("FailureCallback", 0)->get_thunk());
|
||||
CACHE_METHOD_THUNK_AND_CHECK(GodotTaskScheduler, Activate, (GodotTaskScheduler_Activate)GODOT_API_CLASS(GodotTaskScheduler)->get_method("Activate", 0)->get_thunk());
|
||||
|
@ -234,24 +246,9 @@ void update_godot_api_cache() {
|
|||
CACHE_METHOD_THUNK_AND_CHECK(DebuggingUtils, GetStackFrameInfo, (DebugUtils_StackFrameInfo)GODOT_API_CLASS(DebuggingUtils)->get_method("GetStackFrameInfo", 4)->get_thunk());
|
||||
#endif
|
||||
|
||||
{
|
||||
/*
|
||||
* TODO Right now we only support Dictionary<object, object>.
|
||||
* It would be great if we could support other key/value types
|
||||
* without forcing the user to copy the entries.
|
||||
*/
|
||||
GDMonoMethod *method_get_dict_type = CACHED_CLASS(MarshalUtils)->get_method("GetDictionaryType", 0);
|
||||
ERR_FAIL_NULL(method_get_dict_type);
|
||||
MonoReflectionType *dict_refl_type = (MonoReflectionType *)method_get_dict_type->invoke(NULL);
|
||||
ERR_FAIL_NULL(dict_refl_type);
|
||||
MonoType *dict_type = mono_reflection_type_get_type(dict_refl_type);
|
||||
ERR_FAIL_NULL(dict_type);
|
||||
|
||||
CACHE_RAW_MONO_CLASS_AND_CHECK(Dictionary, mono_class_from_mono_type(dict_type));
|
||||
}
|
||||
|
||||
// TODO Move to CSharpLanguage::init()
|
||||
MonoObject *task_scheduler = mono_object_new(SCRIPTS_DOMAIN, GODOT_API_CLASS(GodotTaskScheduler)->get_mono_ptr());
|
||||
mono_runtime_object_init(task_scheduler);
|
||||
GDMonoUtils::runtime_object_init(task_scheduler);
|
||||
mono_cache.task_scheduler_handle = MonoGCHandle::create_strong(task_scheduler);
|
||||
|
||||
mono_cache.godot_api_cache_updated = true;
|
||||
|
@ -304,6 +301,12 @@ MonoThread *get_current_thread() {
|
|||
return mono_thread_current();
|
||||
}
|
||||
|
||||
void runtime_object_init(MonoObject *p_this_obj) {
|
||||
GD_MONO_BEGIN_RUNTIME_INVOKE;
|
||||
mono_runtime_object_init(p_this_obj);
|
||||
GD_MONO_END_RUNTIME_INVOKE;
|
||||
}
|
||||
|
||||
GDMonoClass *get_object_class(MonoObject *p_object) {
|
||||
return GDMono::get_singleton()->get_class(mono_object_get_class(p_object));
|
||||
}
|
||||
|
@ -358,7 +361,7 @@ MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringNa
|
|||
CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, p_object);
|
||||
|
||||
// Construct
|
||||
mono_runtime_object_init(mono_object);
|
||||
GDMonoUtils::runtime_object_init(mono_object);
|
||||
|
||||
return mono_object;
|
||||
}
|
||||
|
@ -368,7 +371,7 @@ MonoObject *create_managed_from(const NodePath &p_from) {
|
|||
ERR_FAIL_NULL_V(mono_object, NULL);
|
||||
|
||||
// Construct
|
||||
mono_runtime_object_init(mono_object);
|
||||
GDMonoUtils::runtime_object_init(mono_object);
|
||||
|
||||
CACHED_FIELD(NodePath, ptr)->set_value_raw(mono_object, memnew(NodePath(p_from)));
|
||||
|
||||
|
@ -380,13 +383,73 @@ MonoObject *create_managed_from(const RID &p_from) {
|
|||
ERR_FAIL_NULL_V(mono_object, NULL);
|
||||
|
||||
// Construct
|
||||
mono_runtime_object_init(mono_object);
|
||||
GDMonoUtils::runtime_object_init(mono_object);
|
||||
|
||||
CACHED_FIELD(RID, ptr)->set_value_raw(mono_object, memnew(RID(p_from)));
|
||||
|
||||
return mono_object;
|
||||
}
|
||||
|
||||
MonoObject *create_managed_from(const Array &p_from, GDMonoClass *p_class) {
|
||||
MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, p_class->get_mono_ptr());
|
||||
ERR_FAIL_NULL_V(mono_object, NULL);
|
||||
|
||||
// Search constructor that takes a pointer as parameter
|
||||
MonoMethod *m;
|
||||
void *iter = NULL;
|
||||
while ((m = mono_class_get_methods(p_class->get_mono_ptr(), &iter))) {
|
||||
if (strcmp(mono_method_get_name(m), ".ctor") == 0) {
|
||||
MonoMethodSignature *sig = mono_method_signature(m);
|
||||
void *front = NULL;
|
||||
if (mono_signature_get_param_count(sig) == 1 &&
|
||||
mono_class_from_mono_type(mono_signature_get_params(sig, &front)) == CACHED_CLASS(IntPtr)->get_mono_ptr()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CRASH_COND(m == NULL);
|
||||
|
||||
Array *new_array = memnew(Array(p_from));
|
||||
void *args[1] = { &new_array };
|
||||
|
||||
MonoException *exc = NULL;
|
||||
mono_runtime_invoke(m, mono_object, args, (MonoObject **)&exc);
|
||||
UNLIKELY_UNHANDLED_EXCEPTION(exc);
|
||||
|
||||
return mono_object;
|
||||
}
|
||||
|
||||
MonoObject *create_managed_from(const Dictionary &p_from, GDMonoClass *p_class) {
|
||||
MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, p_class->get_mono_ptr());
|
||||
ERR_FAIL_NULL_V(mono_object, NULL);
|
||||
|
||||
// Search constructor that takes a pointer as parameter
|
||||
MonoMethod *m;
|
||||
void *iter = NULL;
|
||||
while ((m = mono_class_get_methods(p_class->get_mono_ptr(), &iter))) {
|
||||
if (strcmp(mono_method_get_name(m), ".ctor") == 0) {
|
||||
MonoMethodSignature *sig = mono_method_signature(m);
|
||||
void *front = NULL;
|
||||
if (mono_signature_get_param_count(sig) == 1 &&
|
||||
mono_class_from_mono_type(mono_signature_get_params(sig, &front)) == CACHED_CLASS(IntPtr)->get_mono_ptr()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CRASH_COND(m == NULL);
|
||||
|
||||
Dictionary *new_dict = memnew(Dictionary(p_from));
|
||||
void *args[1] = { &new_dict };
|
||||
|
||||
MonoException *exc = NULL;
|
||||
mono_runtime_invoke(m, mono_object, args, (MonoObject **)&exc);
|
||||
UNLIKELY_UNHANDLED_EXCEPTION(exc);
|
||||
|
||||
return mono_object;
|
||||
}
|
||||
|
||||
MonoDomain *create_domain(const String &p_friendly_name) {
|
||||
MonoDomain *domain = mono_domain_create_appdomain((char *)p_friendly_name.utf8().get_data(), NULL);
|
||||
|
||||
|
@ -400,10 +463,10 @@ MonoDomain *create_domain(const String &p_friendly_name) {
|
|||
return domain;
|
||||
}
|
||||
|
||||
String get_exception_name_and_message(MonoException *p_ex) {
|
||||
String get_exception_name_and_message(MonoException *p_exc) {
|
||||
String res;
|
||||
|
||||
MonoClass *klass = mono_object_get_class((MonoObject *)p_ex);
|
||||
MonoClass *klass = mono_object_get_class((MonoObject *)p_exc);
|
||||
MonoType *type = mono_class_get_type(klass);
|
||||
|
||||
char *full_name = mono_type_full_name(type);
|
||||
|
@ -413,12 +476,24 @@ String get_exception_name_and_message(MonoException *p_ex) {
|
|||
res += ": ";
|
||||
|
||||
MonoProperty *prop = mono_class_get_property_from_name(klass, "Message");
|
||||
MonoString *msg = (MonoString *)mono_property_get_value(prop, (MonoObject *)p_ex, NULL, NULL);
|
||||
GD_MONO_BEGIN_RUNTIME_INVOKE;
|
||||
MonoString *msg = (MonoString *)mono_property_get_value(prop, (MonoObject *)p_exc, NULL, NULL);
|
||||
GD_MONO_END_RUNTIME_INVOKE;
|
||||
res += GDMonoMarshal::mono_string_to_godot(msg);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void set_exception_message(MonoException *p_exc, String message) {
|
||||
MonoClass *klass = mono_object_get_class((MonoObject *)p_exc);
|
||||
MonoProperty *prop = mono_class_get_property_from_name(klass, "Message");
|
||||
MonoString *msg = GDMonoMarshal::mono_string_from_godot(message);
|
||||
void *params[1] = { msg };
|
||||
GD_MONO_BEGIN_RUNTIME_INVOKE;
|
||||
mono_property_set_value(prop, (MonoObject *)p_exc, params, NULL);
|
||||
GD_MONO_END_RUNTIME_INVOKE;
|
||||
}
|
||||
|
||||
void debug_print_unhandled_exception(MonoException *p_exc) {
|
||||
print_unhandled_exception(p_exc);
|
||||
debug_send_unhandled_exception_error(p_exc);
|
||||
|
|
|
@ -41,14 +41,24 @@
|
|||
#include "object.h"
|
||||
#include "reference.h"
|
||||
|
||||
#define UNLIKELY_UNHANDLED_EXCEPTION(m_exc) \
|
||||
if (unlikely(m_exc != NULL)) { \
|
||||
GDMonoUtils::debug_unhandled_exception(m_exc); \
|
||||
_UNREACHABLE_(); \
|
||||
}
|
||||
|
||||
namespace GDMonoUtils {
|
||||
|
||||
typedef MonoObject *(*MarshalUtils_DictToArrays)(MonoObject *, MonoArray **, MonoArray **, MonoObject **);
|
||||
typedef MonoObject *(*MarshalUtils_ArraysToDict)(MonoArray *, MonoArray *, MonoObject **);
|
||||
typedef Array *(*Array_GetPtr)(MonoObject *, MonoObject **);
|
||||
typedef Dictionary *(*Dictionary_GetPtr)(MonoObject *, MonoObject **);
|
||||
typedef MonoObject *(*SignalAwaiter_SignalCallback)(MonoObject *, MonoArray *, MonoObject **);
|
||||
typedef MonoObject *(*SignalAwaiter_FailureCallback)(MonoObject *, MonoObject **);
|
||||
typedef MonoObject *(*GodotTaskScheduler_Activate)(MonoObject *, MonoObject **);
|
||||
typedef MonoArray *(*StackTrace_GetFrames)(MonoObject *, MonoObject **);
|
||||
typedef MonoBoolean (*IsArrayGenericType)(MonoObject *, MonoObject **);
|
||||
typedef MonoBoolean (*IsDictionaryGenericType)(MonoObject *, MonoObject **);
|
||||
typedef MonoBoolean (*IsArrayGenericType)(MonoObject *, MonoObject **);
|
||||
typedef MonoBoolean (*IsDictionaryGenericType)(MonoObject *, MonoObject **);
|
||||
typedef void (*DebugUtils_StackFrameInfo)(MonoObject *, MonoString **, int *, MonoString **, MonoObject **);
|
||||
|
||||
struct MonoCache {
|
||||
|
@ -79,6 +89,8 @@ struct MonoCache {
|
|||
GDMonoMethod *method_System_Diagnostics_StackTrace_ctor_Exception_bool;
|
||||
#endif
|
||||
|
||||
GDMonoClass *class_KeyNotFoundException;
|
||||
|
||||
MonoClass *rawclass_Dictionary;
|
||||
// -----------------------------------------------
|
||||
|
||||
|
@ -100,6 +112,8 @@ struct MonoCache {
|
|||
GDMonoClass *class_Control;
|
||||
GDMonoClass *class_Spatial;
|
||||
GDMonoClass *class_WeakRef;
|
||||
GDMonoClass *class_Array;
|
||||
GDMonoClass *class_Dictionary;
|
||||
GDMonoClass *class_MarshalUtils;
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
|
@ -127,8 +141,10 @@ struct MonoCache {
|
|||
GDMonoField *field_Image_ptr;
|
||||
GDMonoField *field_RID_ptr;
|
||||
|
||||
MarshalUtils_DictToArrays methodthunk_MarshalUtils_DictionaryToArrays;
|
||||
MarshalUtils_ArraysToDict methodthunk_MarshalUtils_ArraysToDictionary;
|
||||
Array_GetPtr methodthunk_Array_GetPtr;
|
||||
Dictionary_GetPtr methodthunk_Dictionary_GetPtr;
|
||||
IsArrayGenericType methodthunk_MarshalUtils_IsArrayGenericType;
|
||||
IsDictionaryGenericType methodthunk_MarshalUtils_IsDictionaryGenericType;
|
||||
SignalAwaiter_SignalCallback methodthunk_SignalAwaiter_SignalCallback;
|
||||
SignalAwaiter_FailureCallback methodthunk_SignalAwaiter_FailureCallback;
|
||||
GodotTaskScheduler_Activate methodthunk_GodotTaskScheduler_Activate;
|
||||
|
@ -175,6 +191,8 @@ _FORCE_INLINE_ bool is_main_thread() {
|
|||
return mono_domain_get() != NULL && mono_thread_get_main() == mono_thread_current();
|
||||
}
|
||||
|
||||
void runtime_object_init(MonoObject *p_this_obj);
|
||||
|
||||
GDMonoClass *get_object_class(MonoObject *p_object);
|
||||
GDMonoClass *type_get_proxy_class(const StringName &p_type);
|
||||
GDMonoClass *get_class_native_base(GDMonoClass *p_class);
|
||||
|
@ -183,10 +201,13 @@ MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringNa
|
|||
|
||||
MonoObject *create_managed_from(const NodePath &p_from);
|
||||
MonoObject *create_managed_from(const RID &p_from);
|
||||
MonoObject *create_managed_from(const Array &p_from, GDMonoClass *p_class);
|
||||
MonoObject *create_managed_from(const Dictionary &p_from, GDMonoClass *p_class);
|
||||
|
||||
MonoDomain *create_domain(const String &p_friendly_name);
|
||||
|
||||
String get_exception_name_and_message(MonoException *p_ex);
|
||||
String get_exception_name_and_message(MonoException *p_exc);
|
||||
void set_exception_message(MonoException *p_exc, String message);
|
||||
|
||||
void debug_print_unhandled_exception(MonoException *p_exc);
|
||||
void debug_send_unhandled_exception_error(MonoException *p_exc);
|
||||
|
|
Loading…
Reference in New Issue