Add Array and Dictionary wrapper classes to C#

This commit is contained in:
Ignacio Etcheverry 2018-07-18 23:07:57 +02:00
parent 2f69e36cef
commit ee3c476c9a
17 changed files with 1409 additions and 183 deletions

View File

@ -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> &params) { bool CSharpScript::_get_signal(GDMonoClass *p_class, GDMonoClass *p_delegate, Vector<Argument> &params) {
if (p_delegate->has_attribute(CACHED_CLASS(SignalAttribute))) { 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) { if (mono_type_get_type(raw_type) == MONO_TYPE_CLASS) {
// Arguments are accessibles as arguments of .Invoke method // Arguments are accessibles as arguments of .Invoke method

View File

@ -100,8 +100,6 @@
#define C_METHOD_MONOSTR_FROM_GODOT C_NS_MONOMARSHAL "::mono_string_from_godot" #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_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_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) #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()) { } else if (return_type->cs_out.empty()) {
p_output.push_back("return " + im_call + ";\n"); p_output.push_back("return " + im_call + ";\n");
} else { } 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(sformat(return_type->cs_out, im_call, return_type->cs_type, return_type->im_type_out));
p_output.push_back("\n"); 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) #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(PoolIntArray, int);
INSERT_ARRAY_FULL(PoolByteArray, PoolByteArray, byte); INSERT_ARRAY_FULL(PoolByteArray, PoolByteArray, byte);
@ -2362,20 +2358,36 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
#undef INSERT_ARRAY #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 // Dictionary
itype = TypeInterface(); itype = TypeInterface();
itype.name = "Dictionary"; itype.name = "Dictionary";
itype.cname = itype.name; itype.cname = itype.name;
itype.proxy_name = "Dictionary<object, object>"; itype.proxy_name = "Dictionary";
itype.c_in = "\t%0 %1_in = " C_METHOD_MANAGED_TO_DICT "(%1);\n"; itype.c_out = "\treturn memnew(Dictionary(%1));\n";
itype.c_out = "\treturn " C_METHOD_MANAGED_FROM_DICT "(%1);\n";
itype.c_arg_in = "&%s_in";
itype.c_type = itype.name; itype.c_type = itype.name;
itype.c_type_in = "MonoObject*"; itype.c_type_in = itype.c_type + "*";
itype.c_type_out = "MonoObject*"; itype.c_type_out = itype.c_type + "*";
itype.cs_type = itype.proxy_name; itype.cs_type = itype.proxy_name;
itype.im_type_in = itype.proxy_name; itype.cs_in = "%0." CS_SMETHOD_GETINSTANCE "()";
itype.im_type_out = itype.proxy_name; itype.cs_out = "return new Dictionary(%0);";
itype.im_type_in = "IntPtr";
itype.im_type_out = "IntPtr";
builtin_types.insert(itype.cname, itype); builtin_types.insert(itype.cname, itype);
// void (fictitious type to represent the return type of methods that do not return anything) // void (fictitious type to represent the return type of methods that do not return anything)

View File

@ -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);
}

View File

@ -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

View File

@ -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();
}
}
}

View File

@ -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();
}
}
}

View File

@ -1,36 +1,17 @@
using System; using System;
using System.Collections.Generic;
namespace Godot 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>(); return type.GetGenericTypeDefinition() == typeof(Array<>);
for (int i = 0; i < keys.Length; i++)
{
ret.Add(keys[i], values[i]);
}
return ret;
} }
private static void DictionaryToArrays(Dictionary<object, object> from, out object[] keysTo, out object[] valuesTo) static bool IsDictionaryGenericType(Type type)
{ {
var keys = from.Keys; return type.GetGenericTypeDefinition() == typeof(Dictionary<, >);
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>);
} }
} }
} }

View File

@ -29,6 +29,7 @@
/*************************************************************************/ /*************************************************************************/
#include "builtin_types_glue.h" #include "builtin_types_glue.h"
#include "collections_glue.h"
#include "../csharp_script.h" #include "../csharp_script.h"
#include "../mono_gd/gd_mono_class.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() { void godot_register_header_icalls() {
godot_register_builtin_type_icalls(); godot_register_builtin_type_icalls();
godot_register_collections_icalls();
} }

View File

@ -33,10 +33,32 @@
#include <mono/metadata/attrdefs.h> #include <mono/metadata/attrdefs.h>
#include "gd_mono_assembly.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 { 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); 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() { GDMonoClass *GDMonoClass::get_parent_class() {
if (assembly) { if (assembly) {

View File

@ -98,7 +98,11 @@ class GDMonoClass {
GDMonoClass(const StringName &p_namespace, const StringName &p_name, MonoClass *p_class, GDMonoAssembly *p_assembly); GDMonoClass(const StringName &p_namespace, const StringName &p_name, MonoClass *p_class, GDMonoAssembly *p_assembly);
public: 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; 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_ MonoClass *get_mono_ptr() const { return mono_class; }
_FORCE_INLINE_ const GDMonoAssembly *get_assembly() const { return assembly; } _FORCE_INLINE_ const GDMonoAssembly *get_assembly() const { return assembly; }
String get_full_name() const;
GDMonoClass *get_parent_class(); GDMonoClass *get_parent_class();
#ifdef TOOLS_ENABLED #ifdef TOOLS_ENABLED

View File

@ -148,7 +148,7 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
case MONO_TYPE_ARRAY: case MONO_TYPE_ARRAY:
case MONO_TYPE_SZARRAY: { 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)) if (array_type->eklass == CACHED_CLASS_RAW(MonoObject))
SET_FROM_ARRAY_AND_BREAK(Array); SET_FROM_ARRAY_AND_BREAK(Array);
@ -200,6 +200,18 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
break; 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_EXPLAIN(String() + "Attempted to set the value of a field of unmarshallable type: " + type_class->get_name());
ERR_FAIL(); ERR_FAIL();
} break; } break;
@ -248,10 +260,13 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
break; break;
} }
case Variant::DICTIONARY: { 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); mono_field_set_value(p_object, mono_field, managed);
} break; } break;
case Variant::ARRAY: SET_FROM_ARRAY_AND_BREAK(Array);
case Variant::POOL_BYTE_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolByteArray); 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_INT_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolIntArray);
case Variant::POOL_REAL_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolRealArray); 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; } break;
case MONO_TYPE_GENERICINST: { case MONO_TYPE_GENERICINST: {
if (CACHED_RAW_MONO_CLASS(Dictionary) == type.type_class->get_mono_ptr()) { MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type.type_class->get_mono_type());
MonoObject *managed = GDMonoMarshal::Dictionary_to_mono_object(p_value.operator Dictionary());
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); mono_field_set_value(p_object, mono_field, managed);
break; break;
} }

View File

@ -45,7 +45,8 @@ struct ManagedType {
GDMonoClass *type_class; GDMonoClass *type_class;
ManagedType() { ManagedType() {
type_class = 0; type_encoding = 0;
type_class = NULL;
} }
}; };

View File

@ -120,7 +120,7 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) {
case MONO_TYPE_ARRAY: case MONO_TYPE_ARRAY:
case MONO_TYPE_SZARRAY: { 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)) if (array_type->eklass == CACHED_CLASS_RAW(MonoObject))
return Variant::ARRAY; return Variant::ARRAY;
@ -162,12 +162,36 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) {
if (CACHED_CLASS(RID) == type_class) { if (CACHED_CLASS(RID) == type_class) {
return Variant::_RID; return Variant::_RID;
} }
if (CACHED_CLASS(Dictionary) == type_class) {
return Variant::DICTIONARY;
}
if (CACHED_CLASS(Array) == type_class) {
return Variant::ARRAY;
}
} break; } break;
case MONO_TYPE_GENERICINST: { 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; 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; } break;
default: { default: {
@ -216,6 +240,7 @@ MonoObject *variant_to_mono_object(const Variant *p_var) {
ManagedType type; ManagedType type;
type.type_encoding = MONO_TYPE_OBJECT; 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); 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_ARRAY:
case MONO_TYPE_SZARRAY: { 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)) if (array_type->eklass == CACHED_CLASS_RAW(MonoObject))
return (MonoObject *)Array_to_mono_array(p_var->operator Array()); 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) { if (CACHED_CLASS(RID) == type_class) {
return GDMonoUtils::create_managed_from(p_var->operator RID()); 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; } break;
case MONO_TYPE_OBJECT: { case MONO_TYPE_OBJECT: {
// Variant // 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 *()); return GDMonoUtils::unmanaged_get_managed(p_var->operator Object *());
} }
case Variant::DICTIONARY: 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: 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: case Variant::POOL_BYTE_ARRAY:
return (MonoObject *)PoolByteArray_to_mono_array(p_var->operator PoolByteArray()); return (MonoObject *)PoolByteArray_to_mono_array(p_var->operator PoolByteArray());
case Variant::POOL_INT_ARRAY: case Variant::POOL_INT_ARRAY:
@ -433,8 +466,24 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
} }
break; break;
case MONO_TYPE_GENERICINST: { 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());
return Dictionary_to_mono_object(p_var->operator Dictionary());
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;
} 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)); GDMonoClass *tclass = GDMono::get_singleton()->get_class(mono_object_get_class(p_obj));
ERR_FAIL_COND_V(!tclass, Variant()); ERR_FAIL_COND_V(!tclass, Variant());
MonoType *raw_type = tclass->get_raw_type(tclass); MonoType *raw_type = tclass->get_mono_type();
ManagedType type; ManagedType type;
@ -531,7 +580,7 @@ Variant mono_object_to_variant(MonoObject *p_obj) {
case MONO_TYPE_ARRAY: case MONO_TYPE_ARRAY:
case MONO_TYPE_SZARRAY: { 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)) if (array_type->eklass == CACHED_CLASS_RAW(MonoObject))
return mono_array_to_Array((MonoArray *)p_obj); 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)); RID *ptr = unbox<RID *>(CACHED_FIELD(RID, ptr)->get_value(p_obj));
return ptr ? Variant(*ptr) : Variant(); 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; } break;
case MONO_TYPE_GENERICINST: { case MONO_TYPE_GENERICINST: {
if (CACHED_RAW_MONO_CLASS(Dictionary) == type.type_class->get_mono_ptr()) { MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type.type_class->get_mono_type());
return mono_object_to_Dictionary(p_obj);
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; } break;
} }
@ -822,66 +911,4 @@ PoolVector3Array mono_array_to_PoolVector3Array(MonoArray *p_array) {
return ret; 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 } // namespace GDMonoMarshal

View File

@ -143,11 +143,6 @@ PoolVector2Array mono_array_to_PoolVector2Array(MonoArray *p_array);
MonoArray *PoolVector3Array_to_mono_array(const PoolVector3Array &p_array); MonoArray *PoolVector3Array_to_mono_array(const PoolVector3Array &p_array);
PoolVector3Array mono_array_to_PoolVector3Array(MonoArray *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 #ifdef YOLO_COPY
#define MARSHALLED_OUT(m_t, m_in, m_out) m_t *m_out = (m_t *)&m_in; #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); #define MARSHALLED_IN(m_t, m_in, m_out) m_t m_out = *reinterpret_cast<m_t *>(m_in);

View File

@ -139,23 +139,8 @@ bool GDMonoProperty::has_setter() {
} }
void GDMonoProperty::set_value(MonoObject *p_object, MonoObject *p_value, MonoException **r_exc) { void GDMonoProperty::set_value(MonoObject *p_object, MonoObject *p_value, MonoException **r_exc) {
MonoMethod *prop_method = mono_property_get_set_method(mono_property); void *params[1] = { p_value };
set_value(p_object, params, r_exc);
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 GDMonoProperty::set_value(MonoObject *p_object, void **p_params, MonoException **r_exc) { void GDMonoProperty::set_value(MonoObject *p_object, void **p_params, MonoException **r_exc) {

View File

@ -87,6 +87,8 @@ void MonoCache::clear_members() {
method_System_Diagnostics_StackTrace_ctor_Exception_bool = NULL; method_System_Diagnostics_StackTrace_ctor_Exception_bool = NULL;
#endif #endif
class_KeyNotFoundException = NULL;
rawclass_Dictionary = NULL; rawclass_Dictionary = NULL;
class_Vector2 = NULL; class_Vector2 = NULL;
@ -107,6 +109,8 @@ void MonoCache::clear_members() {
class_Control = NULL; class_Control = NULL;
class_Spatial = NULL; class_Spatial = NULL;
class_WeakRef = NULL; class_WeakRef = NULL;
class_Array = NULL;
class_Dictionary = NULL;
class_MarshalUtils = NULL; class_MarshalUtils = NULL;
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
@ -134,8 +138,10 @@ void MonoCache::clear_members() {
field_Image_ptr = NULL; field_Image_ptr = NULL;
field_RID_ptr = NULL; field_RID_ptr = NULL;
methodthunk_MarshalUtils_DictionaryToArrays = NULL; methodthunk_Array_GetPtr = NULL;
methodthunk_MarshalUtils_ArraysToDictionary = NULL; methodthunk_Dictionary_GetPtr = NULL;
methodthunk_MarshalUtils_IsArrayGenericType = NULL;
methodthunk_MarshalUtils_IsDictionaryGenericType = NULL;
methodthunk_SignalAwaiter_SignalCallback = NULL; methodthunk_SignalAwaiter_SignalCallback = NULL;
methodthunk_SignalAwaiter_FailureCallback = NULL; methodthunk_SignalAwaiter_FailureCallback = NULL;
methodthunk_GodotTaskScheduler_Activate = 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)); 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 #endif
CACHE_CLASS_AND_CHECK(KeyNotFoundException, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections.Generic", "KeyNotFoundException"));
mono_cache.corlib_cache_updated = true; 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(Control, GODOT_API_CLASS(Control));
CACHE_CLASS_AND_CHECK(Spatial, GODOT_API_CLASS(Spatial)); CACHE_CLASS_AND_CHECK(Spatial, GODOT_API_CLASS(Spatial));
CACHE_CLASS_AND_CHECK(WeakRef, GODOT_API_CLASS(WeakRef)); 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)); CACHE_CLASS_AND_CHECK(MarshalUtils, GODOT_API_CLASS(MarshalUtils));
#ifdef DEBUG_ENABLED #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(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_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(Array, GetPtr, (Array_GetPtr)GODOT_API_CLASS(Array)->get_method("GetPtr", 0)->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(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, 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(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()); 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()); CACHE_METHOD_THUNK_AND_CHECK(DebuggingUtils, GetStackFrameInfo, (DebugUtils_StackFrameInfo)GODOT_API_CLASS(DebuggingUtils)->get_method("GetStackFrameInfo", 4)->get_thunk());
#endif #endif
{ // TODO Move to CSharpLanguage::init()
/*
* 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));
}
MonoObject *task_scheduler = mono_object_new(SCRIPTS_DOMAIN, GODOT_API_CLASS(GodotTaskScheduler)->get_mono_ptr()); 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.task_scheduler_handle = MonoGCHandle::create_strong(task_scheduler);
mono_cache.godot_api_cache_updated = true; mono_cache.godot_api_cache_updated = true;
@ -304,6 +301,12 @@ MonoThread *get_current_thread() {
return mono_thread_current(); 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) { GDMonoClass *get_object_class(MonoObject *p_object) {
return GDMono::get_singleton()->get_class(mono_object_get_class(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); CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, p_object);
// Construct // Construct
mono_runtime_object_init(mono_object); GDMonoUtils::runtime_object_init(mono_object);
return mono_object; return mono_object;
} }
@ -368,7 +371,7 @@ MonoObject *create_managed_from(const NodePath &p_from) {
ERR_FAIL_NULL_V(mono_object, NULL); ERR_FAIL_NULL_V(mono_object, NULL);
// Construct // 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))); 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); ERR_FAIL_NULL_V(mono_object, NULL);
// Construct // 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))); CACHED_FIELD(RID, ptr)->set_value_raw(mono_object, memnew(RID(p_from)));
return mono_object; 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 *create_domain(const String &p_friendly_name) {
MonoDomain *domain = mono_domain_create_appdomain((char *)p_friendly_name.utf8().get_data(), NULL); 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; return domain;
} }
String get_exception_name_and_message(MonoException *p_ex) { String get_exception_name_and_message(MonoException *p_exc) {
String res; 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); MonoType *type = mono_class_get_type(klass);
char *full_name = mono_type_full_name(type); char *full_name = mono_type_full_name(type);
@ -413,12 +476,24 @@ String get_exception_name_and_message(MonoException *p_ex) {
res += ": "; res += ": ";
MonoProperty *prop = mono_class_get_property_from_name(klass, "Message"); 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); res += GDMonoMarshal::mono_string_to_godot(msg);
return res; 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) { void debug_print_unhandled_exception(MonoException *p_exc) {
print_unhandled_exception(p_exc); print_unhandled_exception(p_exc);
debug_send_unhandled_exception_error(p_exc); debug_send_unhandled_exception_error(p_exc);

View File

@ -41,14 +41,24 @@
#include "object.h" #include "object.h"
#include "reference.h" #include "reference.h"
#define UNLIKELY_UNHANDLED_EXCEPTION(m_exc) \
if (unlikely(m_exc != NULL)) { \
GDMonoUtils::debug_unhandled_exception(m_exc); \
_UNREACHABLE_(); \
}
namespace GDMonoUtils { namespace GDMonoUtils {
typedef MonoObject *(*MarshalUtils_DictToArrays)(MonoObject *, MonoArray **, MonoArray **, MonoObject **); typedef Array *(*Array_GetPtr)(MonoObject *, MonoObject **);
typedef MonoObject *(*MarshalUtils_ArraysToDict)(MonoArray *, MonoArray *, MonoObject **); typedef Dictionary *(*Dictionary_GetPtr)(MonoObject *, MonoObject **);
typedef MonoObject *(*SignalAwaiter_SignalCallback)(MonoObject *, MonoArray *, MonoObject **); typedef MonoObject *(*SignalAwaiter_SignalCallback)(MonoObject *, MonoArray *, MonoObject **);
typedef MonoObject *(*SignalAwaiter_FailureCallback)(MonoObject *, MonoObject **); typedef MonoObject *(*SignalAwaiter_FailureCallback)(MonoObject *, MonoObject **);
typedef MonoObject *(*GodotTaskScheduler_Activate)(MonoObject *, MonoObject **); typedef MonoObject *(*GodotTaskScheduler_Activate)(MonoObject *, MonoObject **);
typedef MonoArray *(*StackTrace_GetFrames)(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 **); typedef void (*DebugUtils_StackFrameInfo)(MonoObject *, MonoString **, int *, MonoString **, MonoObject **);
struct MonoCache { struct MonoCache {
@ -79,6 +89,8 @@ struct MonoCache {
GDMonoMethod *method_System_Diagnostics_StackTrace_ctor_Exception_bool; GDMonoMethod *method_System_Diagnostics_StackTrace_ctor_Exception_bool;
#endif #endif
GDMonoClass *class_KeyNotFoundException;
MonoClass *rawclass_Dictionary; MonoClass *rawclass_Dictionary;
// ----------------------------------------------- // -----------------------------------------------
@ -100,6 +112,8 @@ struct MonoCache {
GDMonoClass *class_Control; GDMonoClass *class_Control;
GDMonoClass *class_Spatial; GDMonoClass *class_Spatial;
GDMonoClass *class_WeakRef; GDMonoClass *class_WeakRef;
GDMonoClass *class_Array;
GDMonoClass *class_Dictionary;
GDMonoClass *class_MarshalUtils; GDMonoClass *class_MarshalUtils;
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
@ -127,8 +141,10 @@ struct MonoCache {
GDMonoField *field_Image_ptr; GDMonoField *field_Image_ptr;
GDMonoField *field_RID_ptr; GDMonoField *field_RID_ptr;
MarshalUtils_DictToArrays methodthunk_MarshalUtils_DictionaryToArrays; Array_GetPtr methodthunk_Array_GetPtr;
MarshalUtils_ArraysToDict methodthunk_MarshalUtils_ArraysToDictionary; Dictionary_GetPtr methodthunk_Dictionary_GetPtr;
IsArrayGenericType methodthunk_MarshalUtils_IsArrayGenericType;
IsDictionaryGenericType methodthunk_MarshalUtils_IsDictionaryGenericType;
SignalAwaiter_SignalCallback methodthunk_SignalAwaiter_SignalCallback; SignalAwaiter_SignalCallback methodthunk_SignalAwaiter_SignalCallback;
SignalAwaiter_FailureCallback methodthunk_SignalAwaiter_FailureCallback; SignalAwaiter_FailureCallback methodthunk_SignalAwaiter_FailureCallback;
GodotTaskScheduler_Activate methodthunk_GodotTaskScheduler_Activate; 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(); 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 *get_object_class(MonoObject *p_object);
GDMonoClass *type_get_proxy_class(const StringName &p_type); GDMonoClass *type_get_proxy_class(const StringName &p_type);
GDMonoClass *get_class_native_base(GDMonoClass *p_class); 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 NodePath &p_from);
MonoObject *create_managed_from(const RID &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); 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_print_unhandled_exception(MonoException *p_exc);
void debug_send_unhandled_exception_error(MonoException *p_exc); void debug_send_unhandled_exception_error(MonoException *p_exc);