Merge pull request #37050 from neikeq/fix-mono-after-vulkan-merge
Fix C# bindings after recent breaking changes
This commit is contained in:
commit
989a223c5a
@ -78,6 +78,12 @@ StringName Callable::get_method() const {
|
||||
return method;
|
||||
}
|
||||
|
||||
CallableCustom *Callable::get_custom() const {
|
||||
ERR_FAIL_COND_V_MSG(!is_custom(), NULL,
|
||||
vformat("Can't get custom on non-CallableCustom \"%s\".", operator String()));
|
||||
return custom;
|
||||
}
|
||||
|
||||
uint32_t Callable::hash() const {
|
||||
if (is_custom()) {
|
||||
return custom->hash();
|
||||
|
@ -84,6 +84,7 @@ public:
|
||||
Object *get_object() const;
|
||||
ObjectID get_object_id() const;
|
||||
StringName get_method() const;
|
||||
CallableCustom *get_custom() const;
|
||||
|
||||
uint32_t hash() const;
|
||||
|
||||
|
@ -174,7 +174,7 @@ MAKE_TYPE_INFO(IP_Address, Variant::STRING)
|
||||
template <>
|
||||
struct GetTypeInfo<ObjectID> {
|
||||
static const Variant::Type VARIANT_TYPE = Variant::INT;
|
||||
static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE;
|
||||
static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_INT_IS_UINT64;
|
||||
static inline PropertyInfo get_class_info() {
|
||||
return PropertyInfo(Variant::INT, String(), PROPERTY_HINT_INT_IS_OBJECTID);
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import build_scripts.tls_configure as tls_configure
|
||||
import build_scripts.mono_configure as mono_configure
|
||||
|
||||
Import('env')
|
||||
@ -24,12 +23,6 @@ if env_mono['mono_glue']:
|
||||
if env_mono['tools'] or env_mono['target'] != 'release':
|
||||
env_mono.Append(CPPDEFINES=['GD_MONO_HOT_RELOAD'])
|
||||
|
||||
# Configure Thread Local Storage
|
||||
|
||||
conf = Configure(env_mono)
|
||||
tls_configure.configure(conf)
|
||||
env_mono = conf.Finish()
|
||||
|
||||
# Configure Mono
|
||||
|
||||
mono_configure.configure(env, env_mono)
|
||||
|
@ -1,36 +0,0 @@
|
||||
from __future__ import print_function
|
||||
|
||||
def supported(result):
|
||||
return 'supported' if result else 'not supported'
|
||||
|
||||
|
||||
def check_cxx11_thread_local(conf):
|
||||
print('Checking for `thread_local` support...', end=" ")
|
||||
result = conf.TryCompile('thread_local int foo = 0; int main() { return foo; }', '.cpp')
|
||||
print(supported(result))
|
||||
return bool(result)
|
||||
|
||||
|
||||
def check_declspec_thread(conf):
|
||||
print('Checking for `__declspec(thread)` support...', end=" ")
|
||||
result = conf.TryCompile('__declspec(thread) int foo = 0; int main() { return foo; }', '.cpp')
|
||||
print(supported(result))
|
||||
return bool(result)
|
||||
|
||||
|
||||
def check_gcc___thread(conf):
|
||||
print('Checking for `__thread` support...', end=" ")
|
||||
result = conf.TryCompile('__thread int foo = 0; int main() { return foo; }', '.cpp')
|
||||
print(supported(result))
|
||||
return bool(result)
|
||||
|
||||
|
||||
def configure(conf):
|
||||
if check_cxx11_thread_local(conf):
|
||||
conf.env.Append(CPPDEFINES=['HAVE_CXX11_THREAD_LOCAL'])
|
||||
else:
|
||||
if conf.env.msvc:
|
||||
if check_declspec_thread(conf):
|
||||
conf.env.Append(CPPDEFINES=['HAVE_DECLSPEC_THREAD'])
|
||||
elif check_gcc___thread(conf):
|
||||
conf.env.Append(CPPDEFINES=['HAVE_GCC___THREAD'])
|
@ -62,7 +62,6 @@
|
||||
#include "signal_awaiter_utils.h"
|
||||
#include "utils/macros.h"
|
||||
#include "utils/string_utils.h"
|
||||
#include "utils/thread_local.h"
|
||||
|
||||
#define CACHED_STRING_NAME(m_var) (CSharpLanguage::get_singleton()->get_string_names().m_var)
|
||||
|
||||
@ -75,7 +74,7 @@ static bool _create_project_solution_if_needed() {
|
||||
if (!FileAccess::exists(sln_path) || !FileAccess::exists(csproj_path)) {
|
||||
// A solution does not yet exist, create a new one
|
||||
|
||||
CRASH_COND(CSharpLanguage::get_singleton()->get_godotsharp_editor() == NULL);
|
||||
CRASH_COND(CSharpLanguage::get_singleton()->get_godotsharp_editor() == nullptr);
|
||||
return CSharpLanguage::get_singleton()->get_godotsharp_editor()->call("CreateProjectSolution");
|
||||
}
|
||||
|
||||
@ -83,7 +82,7 @@ static bool _create_project_solution_if_needed() {
|
||||
}
|
||||
#endif
|
||||
|
||||
CSharpLanguage *CSharpLanguage::singleton = NULL;
|
||||
CSharpLanguage *CSharpLanguage::singleton = nullptr;
|
||||
|
||||
String CSharpLanguage::get_name() const {
|
||||
|
||||
@ -142,6 +141,9 @@ void CSharpLanguage::init() {
|
||||
|
||||
void CSharpLanguage::finish() {
|
||||
|
||||
if (finalized)
|
||||
return;
|
||||
|
||||
finalizing = true;
|
||||
|
||||
// Make sure all script binding gchandles are released before finalizing GDMono
|
||||
@ -175,7 +177,10 @@ void CSharpLanguage::finish() {
|
||||
}
|
||||
#endif
|
||||
|
||||
memdelete(managed_callable_middleman);
|
||||
|
||||
finalizing = false;
|
||||
finalized = true;
|
||||
}
|
||||
|
||||
void CSharpLanguage::get_reserved_words(List<String> *p_words) const {
|
||||
@ -434,13 +439,12 @@ static String variant_type_to_managed_name(const String &p_var_type_name) {
|
||||
return "byte[]";
|
||||
if (p_var_type_name == Variant::get_type_name(Variant::PACKED_INT32_ARRAY))
|
||||
return "int[]";
|
||||
if (p_var_type_name == Variant::get_type_name(Variant::PACKED_FLOAT32_ARRAY)) {
|
||||
#ifdef REAL_T_IS_DOUBLE
|
||||
return "double[]";
|
||||
#else
|
||||
if (p_var_type_name == Variant::get_type_name(Variant::PACKED_INT64_ARRAY))
|
||||
return "long[]";
|
||||
if (p_var_type_name == Variant::get_type_name(Variant::PACKED_FLOAT32_ARRAY))
|
||||
return "float[]";
|
||||
#endif
|
||||
}
|
||||
if (p_var_type_name == Variant::get_type_name(Variant::PACKED_FLOAT64_ARRAY))
|
||||
return "double[]";
|
||||
if (p_var_type_name == Variant::get_type_name(Variant::PACKED_STRING_ARRAY))
|
||||
return "string[]";
|
||||
if (p_var_type_name == Variant::get_type_name(Variant::PACKED_VECTOR2_ARRAY))
|
||||
@ -450,6 +454,9 @@ static String variant_type_to_managed_name(const String &p_var_type_name) {
|
||||
if (p_var_type_name == Variant::get_type_name(Variant::PACKED_COLOR_ARRAY))
|
||||
return "Color[]";
|
||||
|
||||
if (p_var_type_name == Variant::get_type_name(Variant::SIGNAL))
|
||||
return "SignalInfo";
|
||||
|
||||
Variant::Type var_types[] = {
|
||||
Variant::BOOL,
|
||||
Variant::INT,
|
||||
@ -463,8 +470,10 @@ static String variant_type_to_managed_name(const String &p_var_type_name) {
|
||||
Variant::BASIS,
|
||||
Variant::TRANSFORM,
|
||||
Variant::COLOR,
|
||||
Variant::STRING_NAME,
|
||||
Variant::NODE_PATH,
|
||||
Variant::_RID
|
||||
Variant::_RID,
|
||||
Variant::CALLABLE
|
||||
};
|
||||
|
||||
for (unsigned int i = 0; i < sizeof(var_types) / sizeof(Variant::Type); i++) {
|
||||
@ -561,7 +570,13 @@ String CSharpLanguage::debug_get_stack_level_source(int p_level) const {
|
||||
Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info() {
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
_TLS_RECURSION_GUARD_V_(Vector<StackInfo>());
|
||||
// Printing an error here will result in endless recursion, so we must be careful
|
||||
static thread_local bool _recursion_flag_ = false;
|
||||
if (_recursion_flag_)
|
||||
return Vector<StackInfo>();
|
||||
_recursion_flag_ = true;
|
||||
SCOPE_EXIT { _recursion_flag_ = false; };
|
||||
|
||||
GD_MONO_SCOPE_THREAD_ATTACH;
|
||||
|
||||
if (!gdmono->is_runtime_initialized() || !GDMono::get_singleton()->get_core_api_assembly() || !GDMonoCache::cached_data.corlib_cache_updated)
|
||||
@ -586,7 +601,13 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info()
|
||||
#ifdef DEBUG_ENABLED
|
||||
Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObject *p_stack_trace) {
|
||||
|
||||
_TLS_RECURSION_GUARD_V_(Vector<StackInfo>());
|
||||
// Printing an error here will result in endless recursion, so we must be careful
|
||||
static thread_local bool _recursion_flag_ = false;
|
||||
if (_recursion_flag_)
|
||||
return Vector<StackInfo>();
|
||||
_recursion_flag_ = true;
|
||||
SCOPE_EXIT { _recursion_flag_ = false; };
|
||||
|
||||
GD_MONO_SCOPE_THREAD_ATTACH;
|
||||
|
||||
MonoException *exc = NULL;
|
||||
@ -774,6 +795,36 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
|
||||
}
|
||||
}
|
||||
|
||||
scripts.sort_custom<CSharpScriptDepSort>(); // Update in inheritance dependency order
|
||||
|
||||
// Serialize managed callables
|
||||
{
|
||||
MutexLock lock(ManagedCallable::instances_mutex);
|
||||
|
||||
for (SelfList<ManagedCallable> *elem = ManagedCallable::instances.first(); elem; elem = elem->next()) {
|
||||
ManagedCallable *managed_callable = elem->self();
|
||||
|
||||
MonoDelegate *delegate = (MonoDelegate *)managed_callable->delegate_handle->get_target();
|
||||
|
||||
Array serialized_data;
|
||||
MonoObject *managed_serialized_data = GDMonoMarshal::variant_to_mono_object(serialized_data);
|
||||
|
||||
MonoException *exc = NULL;
|
||||
bool success = (bool)CACHED_METHOD_THUNK(DelegateUtils, TrySerializeDelegate).invoke(delegate, managed_serialized_data, &exc);
|
||||
|
||||
if (exc) {
|
||||
GDMonoUtils::debug_print_unhandled_exception(exc);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (success) {
|
||||
ManagedCallable::instances_pending_reload.insert(managed_callable, serialized_data);
|
||||
} else if (OS::get_singleton()->is_stdout_verbose()) {
|
||||
OS::get_singleton()->print("Failed to serialize delegate\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<Ref<CSharpScript>> to_reload;
|
||||
|
||||
// We need to keep reference instances alive during reloading
|
||||
@ -789,8 +840,6 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
|
||||
|
||||
// As scripts are going to be reloaded, must proceed without locking here
|
||||
|
||||
scripts.sort_custom<CSharpScriptDepSort>(); // Update in inheritance dependency order
|
||||
|
||||
for (List<Ref<CSharpScript>>::Element *E = scripts.front(); E; E = E->next()) {
|
||||
Ref<CSharpScript> &script = E->get();
|
||||
|
||||
@ -845,6 +894,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
|
||||
|
||||
// TODO: Proper state backup (Not only variants, serialize managed state of scripts)
|
||||
csi->get_properties_state_for_reloading(state.properties);
|
||||
csi->get_event_signals_state_for_reloading(state.event_signals);
|
||||
|
||||
owners_map[obj->get_instance_id()] = state;
|
||||
}
|
||||
@ -957,7 +1007,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
|
||||
CSharpScript::initialize_for_managed_type(script, script_class, native);
|
||||
}
|
||||
|
||||
String native_name = NATIVE_GDMONOCLASS_NAME(script->native);
|
||||
StringName native_name = NATIVE_GDMONOCLASS_NAME(script->native);
|
||||
|
||||
{
|
||||
for (Set<ObjectID>::Element *F = script->pending_reload_instances.front(); F; F = F->next()) {
|
||||
@ -1034,15 +1084,80 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
|
||||
obj->get_script_instance()->set(G->get().first, G->get().second);
|
||||
}
|
||||
|
||||
// Call OnAfterDeserialization
|
||||
CSharpInstance *csi = CAST_CSHARP_INSTANCE(obj->get_script_instance());
|
||||
if (csi && csi->script->script_class->implements_interface(CACHED_CLASS(ISerializationListener)))
|
||||
obj->get_script_instance()->call_multilevel(string_names.on_after_deserialize);
|
||||
|
||||
if (csi) {
|
||||
for (List<Pair<StringName, Array>>::Element *G = state_backup.event_signals.front(); G; G = G->next()) {
|
||||
const StringName &name = G->get().first;
|
||||
const Array &serialized_data = G->get().second;
|
||||
|
||||
Map<StringName, CSharpScript::EventSignal>::Element *match = script->event_signals.find(name);
|
||||
|
||||
if (!match) {
|
||||
// The event or its signal attribute were removed
|
||||
continue;
|
||||
}
|
||||
|
||||
const CSharpScript::EventSignal &event_signal = match->value();
|
||||
|
||||
MonoObject *managed_serialized_data = GDMonoMarshal::variant_to_mono_object(serialized_data);
|
||||
MonoDelegate *delegate = NULL;
|
||||
|
||||
MonoException *exc = NULL;
|
||||
bool success = (bool)CACHED_METHOD_THUNK(DelegateUtils, TryDeserializeDelegate).invoke(managed_serialized_data, &delegate, &exc);
|
||||
|
||||
if (exc) {
|
||||
GDMonoUtils::debug_print_unhandled_exception(exc);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (success) {
|
||||
ERR_CONTINUE(delegate == NULL);
|
||||
event_signal.field->set_value(csi->get_mono_object(), (MonoObject *)delegate);
|
||||
} else if (OS::get_singleton()->is_stdout_verbose()) {
|
||||
OS::get_singleton()->print("Failed to deserialize event signal delegate\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Call OnAfterDeserialization
|
||||
if (csi->script->script_class->implements_interface(CACHED_CLASS(ISerializationListener)))
|
||||
obj->get_script_instance()->call_multilevel(string_names.on_after_deserialize);
|
||||
}
|
||||
}
|
||||
|
||||
script->pending_reload_instances.clear();
|
||||
}
|
||||
|
||||
// Deserialize managed callables
|
||||
{
|
||||
MutexLock lock(ManagedCallable::instances_mutex);
|
||||
|
||||
for (Map<ManagedCallable *, Array>::Element *elem = ManagedCallable::instances_pending_reload.front(); elem; elem = elem->next()) {
|
||||
ManagedCallable *managed_callable = elem->key();
|
||||
const Array &serialized_data = elem->value();
|
||||
|
||||
MonoObject *managed_serialized_data = GDMonoMarshal::variant_to_mono_object(serialized_data);
|
||||
MonoDelegate *delegate = NULL;
|
||||
|
||||
MonoException *exc = NULL;
|
||||
bool success = (bool)CACHED_METHOD_THUNK(DelegateUtils, TryDeserializeDelegate).invoke(managed_serialized_data, &delegate, &exc);
|
||||
|
||||
if (exc) {
|
||||
GDMonoUtils::debug_print_unhandled_exception(exc);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (success) {
|
||||
ERR_CONTINUE(delegate == NULL);
|
||||
managed_callable->set_delegate(delegate);
|
||||
} else if (OS::get_singleton()->is_stdout_verbose()) {
|
||||
OS::get_singleton()->print("Failed to deserialize delegate\n");
|
||||
}
|
||||
}
|
||||
|
||||
ManagedCallable::instances_pending_reload.clear();
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
// FIXME: Hack to refresh editor in order to display new properties and signals. See if there is a better alternative.
|
||||
if (Engine::get_singleton()->is_editor_hint()) {
|
||||
@ -1166,6 +1281,16 @@ void CSharpLanguage::_on_scripts_domain_unloaded() {
|
||||
script_binding.inited = false;
|
||||
}
|
||||
|
||||
{
|
||||
MutexLock lock(ManagedCallable::instances_mutex);
|
||||
|
||||
for (SelfList<ManagedCallable> *elem = ManagedCallable::instances.first(); elem; elem = elem->next()) {
|
||||
ManagedCallable *managed_callable = elem->self();
|
||||
managed_callable->delegate_handle.unref();
|
||||
managed_callable->delegate_invoke = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
scripts_metadata_invalidated = true;
|
||||
}
|
||||
|
||||
@ -1236,24 +1361,12 @@ CSharpLanguage::CSharpLanguage() {
|
||||
|
||||
ERR_FAIL_COND_MSG(singleton, "C# singleton already exist.");
|
||||
singleton = this;
|
||||
|
||||
finalizing = false;
|
||||
|
||||
gdmono = NULL;
|
||||
|
||||
lang_idx = -1;
|
||||
|
||||
scripts_metadata_invalidated = true;
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
godotsharp_editor = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
CSharpLanguage::~CSharpLanguage() {
|
||||
|
||||
finish();
|
||||
singleton = NULL;
|
||||
singleton = nullptr;
|
||||
}
|
||||
|
||||
bool CSharpLanguage::setup_csharp_script_binding(CSharpScriptBinding &r_script_binding, Object *p_object) {
|
||||
@ -1440,12 +1553,11 @@ bool CSharpLanguage::refcount_decremented_instance_binding(Object *p_object) {
|
||||
|
||||
CSharpInstance *CSharpInstance::create_for_managed_type(Object *p_owner, CSharpScript *p_script, const Ref<MonoGCHandle> &p_gchandle) {
|
||||
|
||||
CSharpInstance *instance = memnew(CSharpInstance);
|
||||
CSharpInstance *instance = memnew(CSharpInstance(Ref<CSharpScript>(p_script)));
|
||||
|
||||
Reference *ref = Object::cast_to<Reference>(p_owner);
|
||||
|
||||
instance->base_ref = ref != NULL;
|
||||
instance->script = Ref<CSharpScript>(p_script);
|
||||
instance->owner = p_owner;
|
||||
instance->gchandle = p_gchandle;
|
||||
|
||||
@ -1610,6 +1722,37 @@ void CSharpInstance::get_properties_state_for_reloading(List<Pair<StringName, Va
|
||||
}
|
||||
}
|
||||
|
||||
void CSharpInstance::get_event_signals_state_for_reloading(List<Pair<StringName, Array>> &r_state) {
|
||||
|
||||
MonoObject *owner_managed = get_mono_object();
|
||||
ERR_FAIL_NULL(owner_managed);
|
||||
|
||||
for (const Map<StringName, CSharpScript::EventSignal>::Element *E = script->event_signals.front(); E; E = E->next()) {
|
||||
const CSharpScript::EventSignal &event_signal = E->value();
|
||||
|
||||
MonoDelegate *delegate_field_value = (MonoDelegate *)event_signal.field->get_value(owner_managed);
|
||||
if (!delegate_field_value)
|
||||
continue; // Empty
|
||||
|
||||
Array serialized_data;
|
||||
MonoObject *managed_serialized_data = GDMonoMarshal::variant_to_mono_object(serialized_data);
|
||||
|
||||
MonoException *exc = NULL;
|
||||
bool success = (bool)CACHED_METHOD_THUNK(DelegateUtils, TrySerializeDelegate).invoke(delegate_field_value, managed_serialized_data, &exc);
|
||||
|
||||
if (exc) {
|
||||
GDMonoUtils::debug_print_unhandled_exception(exc);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (success) {
|
||||
r_state.push_back(Pair<StringName, Array>(event_signal.field->get_name(), serialized_data));
|
||||
} else if (OS::get_singleton()->is_stdout_verbose()) {
|
||||
OS::get_singleton()->print("Failed to serialize event signal delegate\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CSharpInstance::get_property_list(List<PropertyInfo> *p_properties) const {
|
||||
|
||||
for (Map<StringName, PropertyInfo>::Element *E = script->member_info.front(); E; E = E->next()) {
|
||||
@ -1847,6 +1990,8 @@ MonoObject *CSharpInstance::_internal_new_managed() {
|
||||
|
||||
void CSharpInstance::mono_object_disposed(MonoObject *p_obj) {
|
||||
|
||||
disconnect_event_signals();
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
CRASH_COND(base_ref);
|
||||
CRASH_COND(gchandle.is_null());
|
||||
@ -1888,6 +2033,33 @@ void CSharpInstance::mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_f
|
||||
}
|
||||
}
|
||||
|
||||
void CSharpInstance::connect_event_signals() {
|
||||
for (const Map<StringName, CSharpScript::EventSignal>::Element *E = script->event_signals.front(); E; E = E->next()) {
|
||||
const CSharpScript::EventSignal &event_signal = E->value();
|
||||
|
||||
StringName signal_name = event_signal.field->get_name();
|
||||
|
||||
// TODO: Use pooling for ManagedCallable instances.
|
||||
auto event_signal_callable = memnew(EventSignalCallable(owner, &event_signal));
|
||||
|
||||
owner->connect(signal_name, Callable(event_signal_callable));
|
||||
}
|
||||
}
|
||||
|
||||
void CSharpInstance::disconnect_event_signals() {
|
||||
for (const Map<StringName, CSharpScript::EventSignal>::Element *E = script->event_signals.front(); E; E = E->next()) {
|
||||
const CSharpScript::EventSignal &event_signal = E->value();
|
||||
|
||||
StringName signal_name = event_signal.field->get_name();
|
||||
|
||||
// TODO: It would be great if we could store this EventSignalCallable on the stack.
|
||||
// The problem is that Callable memdeletes it when it's destructed...
|
||||
auto event_signal_callable = memnew(EventSignalCallable(owner, &event_signal));
|
||||
|
||||
owner->disconnect(signal_name, Callable(event_signal_callable));
|
||||
}
|
||||
}
|
||||
|
||||
void CSharpInstance::refcount_incremented() {
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
@ -2087,13 +2259,8 @@ ScriptLanguage *CSharpInstance::get_language() {
|
||||
return CSharpLanguage::get_singleton();
|
||||
}
|
||||
|
||||
CSharpInstance::CSharpInstance() :
|
||||
owner(NULL),
|
||||
base_ref(false),
|
||||
ref_dying(false),
|
||||
unsafe_referenced(false),
|
||||
predelete_notified(false),
|
||||
destructing_script_instance(false) {
|
||||
CSharpInstance::CSharpInstance(const Ref<CSharpScript> &p_script) :
|
||||
script(p_script) {
|
||||
}
|
||||
|
||||
CSharpInstance::~CSharpInstance() {
|
||||
@ -2416,6 +2583,7 @@ void CSharpScript::load_script_signals(GDMonoClass *p_class, GDMonoClass *p_nati
|
||||
|
||||
// make sure this classes signals are empty when loading for the first time
|
||||
_signals.clear();
|
||||
event_signals.clear();
|
||||
|
||||
GD_MONO_SCOPE_THREAD_ATTACH;
|
||||
|
||||
@ -2423,56 +2591,90 @@ void CSharpScript::load_script_signals(GDMonoClass *p_class, GDMonoClass *p_nati
|
||||
while (top && top != p_native_class) {
|
||||
const Vector<GDMonoClass *> &delegates = top->get_all_delegates();
|
||||
for (int i = delegates.size() - 1; i >= 0; --i) {
|
||||
Vector<Argument> parameters;
|
||||
|
||||
GDMonoClass *delegate = delegates[i];
|
||||
|
||||
if (_get_signal(top, delegate, parameters)) {
|
||||
if (!delegate->has_attribute(CACHED_CLASS(SignalAttribute)))
|
||||
continue;
|
||||
|
||||
// Arguments are accessibles as arguments of .Invoke method
|
||||
GDMonoMethod *invoke_method = delegate->get_method(mono_get_delegate_invoke(delegate->get_mono_ptr()));
|
||||
|
||||
Vector<SignalParameter> parameters;
|
||||
if (_get_signal(top, invoke_method, parameters)) {
|
||||
_signals[delegate->get_name()] = parameters;
|
||||
}
|
||||
}
|
||||
|
||||
List<StringName> found_event_signals;
|
||||
|
||||
void *iter = NULL;
|
||||
MonoEvent *raw_event = NULL;
|
||||
while ((raw_event = mono_class_get_events(top->get_mono_ptr(), &iter)) != NULL) {
|
||||
MonoCustomAttrInfo *event_attrs = mono_custom_attrs_from_event(top->get_mono_ptr(), raw_event);
|
||||
if (event_attrs) {
|
||||
if (mono_custom_attrs_has_attr(event_attrs, CACHED_CLASS(SignalAttribute)->get_mono_ptr())) {
|
||||
const char *event_name = mono_event_get_name(raw_event);
|
||||
found_event_signals.push_back(StringName(event_name));
|
||||
}
|
||||
|
||||
mono_custom_attrs_free(event_attrs);
|
||||
}
|
||||
}
|
||||
|
||||
const Vector<GDMonoField *> &fields = top->get_all_fields();
|
||||
for (int i = 0; i < fields.size(); i++) {
|
||||
GDMonoField *field = fields[i];
|
||||
|
||||
GDMonoClass *field_class = field->get_type().type_class;
|
||||
|
||||
if (!mono_class_is_delegate(field_class->get_mono_ptr()))
|
||||
continue;
|
||||
|
||||
if (!found_event_signals.find(field->get_name()))
|
||||
continue;
|
||||
|
||||
GDMonoMethod *invoke_method = field_class->get_method(mono_get_delegate_invoke(field_class->get_mono_ptr()));
|
||||
|
||||
Vector<SignalParameter> parameters;
|
||||
if (_get_signal(top, invoke_method, parameters)) {
|
||||
event_signals[field->get_name()] = { field, invoke_method, parameters };
|
||||
}
|
||||
}
|
||||
|
||||
top = top->get_parent_class();
|
||||
}
|
||||
|
||||
signals_invalidated = false;
|
||||
}
|
||||
|
||||
bool CSharpScript::_get_signal(GDMonoClass *p_class, GDMonoClass *p_delegate, Vector<Argument> ¶ms) {
|
||||
bool CSharpScript::_get_signal(GDMonoClass *p_class, GDMonoMethod *p_delegate_invoke, Vector<SignalParameter> ¶ms) {
|
||||
GD_MONO_ASSERT_THREAD_ATTACHED;
|
||||
|
||||
if (p_delegate->has_attribute(CACHED_CLASS(SignalAttribute))) {
|
||||
MonoType *raw_type = p_delegate->get_mono_type();
|
||||
Vector<StringName> names;
|
||||
Vector<ManagedType> types;
|
||||
p_delegate_invoke->get_parameter_names(names);
|
||||
p_delegate_invoke->get_parameter_types(types);
|
||||
|
||||
if (mono_type_get_type(raw_type) == MONO_TYPE_CLASS) {
|
||||
// Arguments are accessibles as arguments of .Invoke method
|
||||
GDMonoMethod *invoke = p_delegate->get_method("Invoke", -1);
|
||||
for (int i = 0; i < names.size(); ++i) {
|
||||
SignalParameter arg;
|
||||
arg.name = names[i];
|
||||
|
||||
Vector<StringName> names;
|
||||
Vector<ManagedType> types;
|
||||
invoke->get_parameter_names(names);
|
||||
invoke->get_parameter_types(types);
|
||||
bool nil_is_variant = false;
|
||||
arg.type = GDMonoMarshal::managed_to_variant_type(types[i], &nil_is_variant);
|
||||
|
||||
if (names.size() == types.size()) {
|
||||
for (int i = 0; i < names.size(); ++i) {
|
||||
Argument arg;
|
||||
arg.name = names[i];
|
||||
arg.type = GDMonoMarshal::managed_to_variant_type(types[i]);
|
||||
|
||||
if (arg.type == Variant::NIL) {
|
||||
ERR_PRINT("Unknown type of signal parameter: '" + arg.name + "' in '" + p_class->get_full_name() + "'.");
|
||||
return false;
|
||||
}
|
||||
|
||||
params.push_back(arg);
|
||||
}
|
||||
|
||||
return true;
|
||||
if (arg.type == Variant::NIL) {
|
||||
if (nil_is_variant) {
|
||||
arg.nil_is_variant = true;
|
||||
} else {
|
||||
ERR_PRINT("Unknown type of signal parameter: '" + arg.name + "' in '" + p_class->get_full_name() + "'.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
params.push_back(arg);
|
||||
}
|
||||
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
@ -2523,7 +2725,8 @@ bool CSharpScript::_get_member_export(IMonoClassMember *p_member, bool p_inspect
|
||||
}
|
||||
}
|
||||
|
||||
Variant::Type variant_type = GDMonoMarshal::managed_to_variant_type(type);
|
||||
bool nil_is_variant = false;
|
||||
Variant::Type variant_type = GDMonoMarshal::managed_to_variant_type(type, &nil_is_variant);
|
||||
|
||||
if (!p_inspect_export || !exported) {
|
||||
r_prop_info = PropertyInfo(variant_type, (String)p_member->get_name(), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_SCRIPT_VARIABLE);
|
||||
@ -2536,7 +2739,7 @@ bool CSharpScript::_get_member_export(IMonoClassMember *p_member, bool p_inspect
|
||||
PropertyHint hint = PROPERTY_HINT_NONE;
|
||||
String hint_string;
|
||||
|
||||
if (variant_type == Variant::NIL) {
|
||||
if (variant_type == Variant::NIL && !nil_is_variant) {
|
||||
ERR_PRINT("Unknown exported member type: '" + MEMBER_FULL_QUALIFIED_NAME(p_member) + "'.");
|
||||
return false;
|
||||
}
|
||||
@ -2552,7 +2755,14 @@ bool CSharpScript::_get_member_export(IMonoClassMember *p_member, bool p_inspect
|
||||
hint_string = CACHED_FIELD(ExportAttribute, hintString)->get_string_value(attr);
|
||||
}
|
||||
|
||||
r_prop_info = PropertyInfo(variant_type, (String)p_member->get_name(), hint, hint_string, PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE);
|
||||
uint32_t prop_usage = PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE;
|
||||
|
||||
if (variant_type == Variant::NIL) {
|
||||
// System.Object (Variant)
|
||||
prop_usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
|
||||
}
|
||||
|
||||
r_prop_info = PropertyInfo(variant_type, (String)p_member->get_name(), hint, hint_string, prop_usage);
|
||||
r_exported = true;
|
||||
|
||||
return true;
|
||||
@ -2562,6 +2772,11 @@ bool CSharpScript::_get_member_export(IMonoClassMember *p_member, bool p_inspect
|
||||
|
||||
int CSharpScript::_try_get_member_export_hint(IMonoClassMember *p_member, ManagedType p_type, Variant::Type p_variant_type, bool p_allow_generics, PropertyHint &r_hint, String &r_hint_string) {
|
||||
|
||||
if (p_variant_type == Variant::NIL) {
|
||||
// System.Object (Variant)
|
||||
return 1;
|
||||
}
|
||||
|
||||
GD_MONO_ASSERT_THREAD_ATTACHED;
|
||||
|
||||
if (p_variant_type == Variant::INT && p_type.type_encoding == MONO_TYPE_VALUETYPE && mono_class_is_enum(p_type.type_class->get_mono_ptr())) {
|
||||
@ -2621,7 +2836,7 @@ int CSharpScript::_try_get_member_export_hint(IMonoClassMember *p_member, Manage
|
||||
CRASH_COND(field_native_class == NULL);
|
||||
|
||||
r_hint = PROPERTY_HINT_RESOURCE_TYPE;
|
||||
r_hint_string = NATIVE_GDMONOCLASS_NAME(field_native_class);
|
||||
r_hint_string = String(NATIVE_GDMONOCLASS_NAME(field_native_class));
|
||||
} else if (p_allow_generics && p_variant_type == Variant::ARRAY) {
|
||||
// Nested arrays are not supported in the inspector
|
||||
|
||||
@ -2909,9 +3124,8 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg
|
||||
}
|
||||
}
|
||||
|
||||
CSharpInstance *instance = memnew(CSharpInstance);
|
||||
CSharpInstance *instance = memnew(CSharpInstance(Ref<CSharpScript>(this)));
|
||||
instance->base_ref = p_isref;
|
||||
instance->script = Ref<CSharpScript>(this);
|
||||
instance->owner = p_owner;
|
||||
instance->owner->set_script_instance(instance);
|
||||
|
||||
@ -2998,12 +3212,14 @@ ScriptInstance *CSharpScript::instance_create(Object *p_this) {
|
||||
#endif
|
||||
|
||||
if (native) {
|
||||
String native_name = NATIVE_GDMONOCLASS_NAME(native);
|
||||
StringName native_name = NATIVE_GDMONOCLASS_NAME(native);
|
||||
if (!ClassDB::is_parent_class(p_this->get_class_name(), native_name)) {
|
||||
if (EngineDebugger::is_active()) {
|
||||
CSharpLanguage::get_singleton()->debug_break_parse(get_path(), 0, "Script inherits from native type '" + native_name + "', so it can't be instanced in object of type: '" + p_this->get_class() + "'");
|
||||
CSharpLanguage::get_singleton()->debug_break_parse(get_path(), 0,
|
||||
"Script inherits from native type '" + String(native_name) +
|
||||
"', so it can't be instanced in object of type: '" + p_this->get_class() + "'");
|
||||
}
|
||||
ERR_FAIL_V_MSG(NULL, "Script inherits from native type '" + native_name +
|
||||
ERR_FAIL_V_MSG(NULL, "Script inherits from native type '" + String(native_name) +
|
||||
"', so it can't be instanced in object of type: '" + p_this->get_class() + "'.");
|
||||
}
|
||||
}
|
||||
@ -3290,19 +3506,45 @@ void CSharpScript::update_exports() {
|
||||
}
|
||||
|
||||
bool CSharpScript::has_script_signal(const StringName &p_signal) const {
|
||||
return _signals.has(p_signal);
|
||||
return event_signals.has(p_signal) || _signals.has(p_signal);
|
||||
}
|
||||
|
||||
void CSharpScript::get_script_signal_list(List<MethodInfo> *r_signals) const {
|
||||
for (const Map<StringName, Vector<Argument>>::Element *E = _signals.front(); E; E = E->next()) {
|
||||
MethodInfo mi;
|
||||
|
||||
for (const Map<StringName, Vector<SignalParameter>>::Element *E = _signals.front(); E; E = E->next()) {
|
||||
MethodInfo mi;
|
||||
mi.name = E->key();
|
||||
for (int i = 0; i < E->get().size(); i++) {
|
||||
PropertyInfo arg;
|
||||
arg.name = E->get()[i].name;
|
||||
mi.arguments.push_back(arg);
|
||||
|
||||
const Vector<SignalParameter> ¶ms = E->value();
|
||||
for (int i = 0; i < params.size(); i++) {
|
||||
const SignalParameter ¶m = params[i];
|
||||
|
||||
PropertyInfo arg_info = PropertyInfo(param.type, param.name);
|
||||
if (param.type == Variant::NIL && param.nil_is_variant)
|
||||
arg_info.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
|
||||
|
||||
mi.arguments.push_back(arg_info);
|
||||
}
|
||||
|
||||
r_signals->push_back(mi);
|
||||
}
|
||||
|
||||
for (const Map<StringName, EventSignal>::Element *E = event_signals.front(); E; E = E->next()) {
|
||||
MethodInfo mi;
|
||||
mi.name = E->key();
|
||||
|
||||
const EventSignal &event_signal = E->value();
|
||||
const Vector<SignalParameter> ¶ms = event_signal.parameters;
|
||||
for (int i = 0; i < params.size(); i++) {
|
||||
const SignalParameter ¶m = params[i];
|
||||
|
||||
PropertyInfo arg_info = PropertyInfo(param.type, param.name);
|
||||
if (param.type == Variant::NIL && param.nil_is_variant)
|
||||
arg_info.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
|
||||
|
||||
mi.arguments.push_back(arg_info);
|
||||
}
|
||||
|
||||
r_signals->push_back(mi);
|
||||
}
|
||||
}
|
||||
@ -3420,19 +3662,10 @@ StringName CSharpScript::get_script_name() const {
|
||||
return name;
|
||||
}
|
||||
|
||||
CSharpScript::CSharpScript() :
|
||||
script_list(this) {
|
||||
CSharpScript::CSharpScript() {
|
||||
|
||||
_clear();
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
source_changed_cache = false;
|
||||
placeholder_fallback_enabled = false;
|
||||
exports_invalidated = true;
|
||||
#endif
|
||||
|
||||
signals_invalidated = true;
|
||||
|
||||
_resource_path_changed();
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
@ -3561,4 +3794,5 @@ CSharpLanguage::StringNameCache::StringNameCache() {
|
||||
on_before_serialize = StaticCString::create("OnBeforeSerialize");
|
||||
on_after_deserialize = StaticCString::create("OnAfterDeserialize");
|
||||
dotctor = StaticCString::create(".ctor");
|
||||
delegate_invoke_method_name = StaticCString::create("Invoke");
|
||||
}
|
||||
|
@ -53,8 +53,8 @@ class CSharpLanguage;
|
||||
template <typename TScriptInstance, typename TScriptLanguage>
|
||||
TScriptInstance *cast_script_instance(ScriptInstance *p_inst) {
|
||||
if (!p_inst)
|
||||
return NULL;
|
||||
return p_inst->get_language() == TScriptLanguage::get_singleton() ? static_cast<TScriptInstance *>(p_inst) : NULL;
|
||||
return nullptr;
|
||||
return p_inst->get_language() == TScriptLanguage::get_singleton() ? static_cast<TScriptInstance *>(p_inst) : nullptr;
|
||||
}
|
||||
#else
|
||||
template <typename TScriptInstance, typename TScriptLanguage>
|
||||
@ -69,18 +69,32 @@ class CSharpScript : public Script {
|
||||
|
||||
GDCLASS(CSharpScript, Script);
|
||||
|
||||
public:
|
||||
struct SignalParameter {
|
||||
String name;
|
||||
Variant::Type type;
|
||||
bool nil_is_variant = false;
|
||||
};
|
||||
|
||||
struct EventSignal {
|
||||
GDMonoField *field = NULL;
|
||||
GDMonoMethod *invoke_method = NULL;
|
||||
Vector<SignalParameter> parameters;
|
||||
};
|
||||
|
||||
private:
|
||||
friend class CSharpInstance;
|
||||
friend class CSharpLanguage;
|
||||
friend struct CSharpScriptDepSort;
|
||||
|
||||
bool tool;
|
||||
bool valid;
|
||||
bool tool = false;
|
||||
bool valid = false;
|
||||
|
||||
bool builtin;
|
||||
|
||||
GDMonoClass *base;
|
||||
GDMonoClass *native;
|
||||
GDMonoClass *script_class;
|
||||
GDMonoClass *base = nullptr;
|
||||
GDMonoClass *native = nullptr;
|
||||
GDMonoClass *script_class = nullptr;
|
||||
|
||||
Ref<CSharpScript> base_cache; // TODO what's this for?
|
||||
|
||||
@ -92,6 +106,7 @@ class CSharpScript : public Script {
|
||||
// Replace with buffer containing the serialized state of managed scripts.
|
||||
// Keep variant state backup to use only with script instance placeholders.
|
||||
List<Pair<StringName, Variant>> properties;
|
||||
List<Pair<StringName, Array>> event_signals;
|
||||
};
|
||||
|
||||
Set<ObjectID> pending_reload_instances;
|
||||
@ -103,15 +118,11 @@ class CSharpScript : public Script {
|
||||
String source;
|
||||
StringName name;
|
||||
|
||||
SelfList<CSharpScript> script_list;
|
||||
SelfList<CSharpScript> script_list = this;
|
||||
|
||||
struct Argument {
|
||||
String name;
|
||||
Variant::Type type;
|
||||
};
|
||||
|
||||
Map<StringName, Vector<Argument>> _signals;
|
||||
bool signals_invalidated;
|
||||
Map<StringName, Vector<SignalParameter>> _signals;
|
||||
Map<StringName, EventSignal> event_signals;
|
||||
bool signals_invalidated = true;
|
||||
|
||||
Vector<ScriptNetData> rpc_functions;
|
||||
Vector<ScriptNetData> rpc_variables;
|
||||
@ -120,9 +131,9 @@ class CSharpScript : public Script {
|
||||
List<PropertyInfo> exported_members_cache; // members_cache
|
||||
Map<StringName, Variant> exported_members_defval_cache; // member_default_values_cache
|
||||
Set<PlaceHolderScriptInstance *> placeholders;
|
||||
bool source_changed_cache;
|
||||
bool placeholder_fallback_enabled;
|
||||
bool exports_invalidated;
|
||||
bool source_changed_cache = false;
|
||||
bool placeholder_fallback_enabled = false;
|
||||
bool exports_invalidated = true;
|
||||
void _update_exports_values(Map<StringName, Variant> &values, List<PropertyInfo> &propnames);
|
||||
void _update_member_info_no_exports();
|
||||
virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder);
|
||||
@ -133,7 +144,7 @@ class CSharpScript : public Script {
|
||||
void _clear();
|
||||
|
||||
void load_script_signals(GDMonoClass *p_class, GDMonoClass *p_native_class);
|
||||
bool _get_signal(GDMonoClass *p_class, GDMonoClass *p_delegate, Vector<Argument> ¶ms);
|
||||
bool _get_signal(GDMonoClass *p_class, GDMonoMethod *p_delegate_invoke, Vector<SignalParameter> ¶ms);
|
||||
|
||||
bool _update_exports();
|
||||
#ifdef TOOLS_ENABLED
|
||||
@ -221,16 +232,18 @@ class CSharpInstance : public ScriptInstance {
|
||||
friend class CSharpScript;
|
||||
friend class CSharpLanguage;
|
||||
|
||||
Object *owner;
|
||||
bool base_ref;
|
||||
bool ref_dying;
|
||||
bool unsafe_referenced;
|
||||
bool predelete_notified;
|
||||
bool destructing_script_instance;
|
||||
Object *owner = nullptr;
|
||||
bool base_ref = false;
|
||||
bool ref_dying = false;
|
||||
bool unsafe_referenced = false;
|
||||
bool predelete_notified = false;
|
||||
bool destructing_script_instance = false;
|
||||
|
||||
Ref<CSharpScript> script;
|
||||
Ref<MonoGCHandle> gchandle;
|
||||
|
||||
Vector<Callable> event_signal_callables;
|
||||
|
||||
bool _reference_owner_unsafe();
|
||||
|
||||
/*
|
||||
@ -239,7 +252,7 @@ class CSharpInstance : public ScriptInstance {
|
||||
bool _unreference_owner_unsafe();
|
||||
|
||||
/*
|
||||
* If NULL is returned, the caller must destroy the script instance by removing it from its owner.
|
||||
* If nullptr is returned, the caller must destroy the script instance by removing it from its owner.
|
||||
*/
|
||||
MonoObject *_internal_new_managed();
|
||||
|
||||
@ -250,6 +263,7 @@ class CSharpInstance : public ScriptInstance {
|
||||
void _call_multilevel(MonoObject *p_mono_object, const StringName &p_method, const Variant **p_args, int p_argcount);
|
||||
|
||||
void get_properties_state_for_reloading(List<Pair<StringName, Variant>> &r_state);
|
||||
void get_event_signals_state_for_reloading(List<Pair<StringName, Array>> &r_state);
|
||||
|
||||
public:
|
||||
MonoObject *get_mono_object() const;
|
||||
@ -272,11 +286,14 @@ public:
|
||||
void mono_object_disposed(MonoObject *p_obj);
|
||||
|
||||
/*
|
||||
* If 'r_delete_owner' is set to true, the caller must memdelete the script instance's owner. Otherwise, if
|
||||
* If 'r_delete_owner' is set to true, the caller must memdelete the script instance's owner. Otherwise, ifevent_signal
|
||||
* 'r_remove_script_instance' is set to true, the caller must destroy the script instance by removing it from its owner.
|
||||
*/
|
||||
void mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_finalizer, bool &r_delete_owner, bool &r_remove_script_instance);
|
||||
|
||||
void connect_event_signals();
|
||||
void disconnect_event_signals();
|
||||
|
||||
virtual void refcount_incremented();
|
||||
virtual bool refcount_decremented();
|
||||
|
||||
@ -301,7 +318,7 @@ public:
|
||||
|
||||
virtual ScriptLanguage *get_language();
|
||||
|
||||
CSharpInstance();
|
||||
CSharpInstance(const Ref<CSharpScript> &p_script);
|
||||
~CSharpInstance();
|
||||
};
|
||||
|
||||
@ -313,6 +330,10 @@ struct CSharpScriptBinding {
|
||||
Object *owner;
|
||||
};
|
||||
|
||||
class ManagedCallableMiddleman : public Object {
|
||||
GDCLASS(ManagedCallableMiddleman, Object);
|
||||
};
|
||||
|
||||
class CSharpLanguage : public ScriptLanguage {
|
||||
|
||||
friend class CSharpScript;
|
||||
@ -320,9 +341,10 @@ class CSharpLanguage : public ScriptLanguage {
|
||||
|
||||
static CSharpLanguage *singleton;
|
||||
|
||||
bool finalizing;
|
||||
bool finalizing = false;
|
||||
bool finalized = false;
|
||||
|
||||
GDMono *gdmono;
|
||||
GDMono *gdmono = nullptr;
|
||||
SelfList<CSharpScript>::List script_list;
|
||||
|
||||
Mutex script_instances_mutex;
|
||||
@ -337,6 +359,8 @@ class CSharpLanguage : public ScriptLanguage {
|
||||
Mutex unsafe_object_references_lock;
|
||||
#endif
|
||||
|
||||
ManagedCallableMiddleman *managed_callable_middleman = memnew(ManagedCallableMiddleman);
|
||||
|
||||
struct StringNameCache {
|
||||
|
||||
StringName _signal_callback;
|
||||
@ -348,17 +372,18 @@ class CSharpLanguage : public ScriptLanguage {
|
||||
StringName dotctor; // .ctor
|
||||
StringName on_before_serialize; // OnBeforeSerialize
|
||||
StringName on_after_deserialize; // OnAfterDeserialize
|
||||
StringName delegate_invoke_method_name;
|
||||
|
||||
StringNameCache();
|
||||
};
|
||||
|
||||
int lang_idx;
|
||||
int lang_idx = -1;
|
||||
|
||||
Dictionary scripts_metadata;
|
||||
bool scripts_metadata_invalidated;
|
||||
bool scripts_metadata_invalidated = true;
|
||||
|
||||
// For debug_break and debug_break_parse
|
||||
int _debug_parse_err_line;
|
||||
int _debug_parse_err_line = -1;
|
||||
String _debug_parse_err_file;
|
||||
String _debug_error;
|
||||
|
||||
@ -368,7 +393,7 @@ class CSharpLanguage : public ScriptLanguage {
|
||||
void _on_scripts_domain_unloaded();
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
EditorPlugin *godotsharp_editor;
|
||||
EditorPlugin *godotsharp_editor = nullptr;
|
||||
|
||||
static void _editor_init_callback();
|
||||
#endif
|
||||
@ -410,6 +435,8 @@ public:
|
||||
return scripts_metadata;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ ManagedCallableMiddleman *get_managed_callable_middleman() const { return managed_callable_middleman; }
|
||||
|
||||
virtual String get_name() const;
|
||||
|
||||
/* LANGUAGE FUNCTIONS */
|
||||
@ -426,7 +453,7 @@ public:
|
||||
virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const;
|
||||
virtual bool is_using_templates();
|
||||
virtual void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script);
|
||||
/* TODO */ virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions, List<ScriptLanguage::Warning> *r_warnings = NULL, Set<int> *r_safe_lines = NULL) const { return true; }
|
||||
/* TODO */ virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions, List<ScriptLanguage::Warning> *r_warnings = nullptr, Set<int> *r_safe_lines = nullptr) const { return true; }
|
||||
virtual String validate_path(const String &p_path) const;
|
||||
virtual Script *create_script() const;
|
||||
virtual bool has_named_classes() const;
|
||||
@ -497,7 +524,7 @@ public:
|
||||
|
||||
class ResourceFormatLoaderCSharpScript : public ResourceFormatLoader {
|
||||
public:
|
||||
virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL, bool p_use_sub_threads = false, float *r_progress = nullptr);
|
||||
virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr);
|
||||
virtual void get_recognized_extensions(List<String> *p_extensions) const;
|
||||
virtual bool handles_type(const String &p_type) const;
|
||||
virtual String get_resource_type(const String &p_path) const;
|
||||
|
@ -280,7 +280,7 @@ namespace GodotTools
|
||||
Text = "Build Project".TTR(),
|
||||
FocusMode = FocusModeEnum.None
|
||||
};
|
||||
buildProjectBtn.Connect("pressed", this, nameof(BuildProjectPressed));
|
||||
buildProjectBtn.PressedSignal += BuildProjectPressed;
|
||||
toolBarHBox.AddChild(buildProjectBtn);
|
||||
|
||||
toolBarHBox.AddSpacer(begin: false);
|
||||
@ -293,7 +293,7 @@ namespace GodotTools
|
||||
Visible = false,
|
||||
FocusMode = FocusModeEnum.None
|
||||
};
|
||||
warningsBtn.Connect("toggled", this, nameof(_WarningsToggled));
|
||||
warningsBtn.Toggled += _WarningsToggled;
|
||||
toolBarHBox.AddChild(warningsBtn);
|
||||
|
||||
errorsBtn = new ToolButton
|
||||
@ -304,7 +304,7 @@ namespace GodotTools
|
||||
Visible = false,
|
||||
FocusMode = FocusModeEnum.None
|
||||
};
|
||||
errorsBtn.Connect("toggled", this, nameof(_ErrorsToggled));
|
||||
errorsBtn.Toggled += _ErrorsToggled;
|
||||
toolBarHBox.AddChild(errorsBtn);
|
||||
|
||||
toolBarHBox.AddSpacer(begin: false);
|
||||
@ -315,7 +315,7 @@ namespace GodotTools
|
||||
FocusMode = FocusModeEnum.None,
|
||||
Visible = false
|
||||
};
|
||||
viewLogBtn.Connect("pressed", this, nameof(_ViewLogPressed));
|
||||
viewLogBtn.PressedSignal += _ViewLogPressed;
|
||||
toolBarHBox.AddChild(viewLogBtn);
|
||||
|
||||
var hsc = new HSplitContainer
|
||||
@ -326,8 +326,8 @@ namespace GodotTools
|
||||
panelBuildsTab.AddChild(hsc);
|
||||
|
||||
buildTabsList = new ItemList { SizeFlagsHorizontal = (int)SizeFlags.ExpandFill };
|
||||
buildTabsList.Connect("item_selected", this, nameof(_BuildTabsItemSelected));
|
||||
buildTabsList.Connect("nothing_selected", this, nameof(_BuildTabsNothingSelected));
|
||||
buildTabsList.ItemSelected += _BuildTabsItemSelected;
|
||||
buildTabsList.NothingSelected += _BuildTabsNothingSelected;
|
||||
hsc.AddChild(buildTabsList);
|
||||
|
||||
buildTabs = new TabContainer
|
||||
|
@ -251,7 +251,7 @@ namespace GodotTools
|
||||
base._Ready();
|
||||
|
||||
issuesList = new ItemList { SizeFlagsVertical = (int)SizeFlags.ExpandFill };
|
||||
issuesList.Connect("item_activated", this, nameof(_IssueActivated));
|
||||
issuesList.ItemActivated += _IssueActivated;
|
||||
AddChild(issuesList);
|
||||
}
|
||||
|
||||
|
@ -121,16 +121,9 @@ namespace GodotTools
|
||||
aboutDialog.PopupCenteredMinsize();
|
||||
}
|
||||
|
||||
private void _ToggleAboutDialogOnStart(bool enabled)
|
||||
private void _MenuOptionPressed(int id)
|
||||
{
|
||||
bool showOnStart = (bool)editorSettings.GetSetting("mono/editor/show_info_on_start");
|
||||
if (showOnStart != enabled)
|
||||
editorSettings.SetSetting("mono/editor/show_info_on_start", enabled);
|
||||
}
|
||||
|
||||
private void _MenuOptionPressed(MenuOptions id)
|
||||
{
|
||||
switch (id)
|
||||
switch ((MenuOptions)id)
|
||||
{
|
||||
case MenuOptions.CreateSln:
|
||||
CreateProjectSolution();
|
||||
@ -210,7 +203,7 @@ namespace GodotTools
|
||||
string scriptPath = ProjectSettings.GlobalizePath(script.ResourcePath);
|
||||
RiderPathManager.OpenFile(GodotSharpDirs.ProjectSlnPath, scriptPath, line);
|
||||
return Error.Ok;
|
||||
}
|
||||
}
|
||||
case ExternalEditorId.MonoDevelop:
|
||||
{
|
||||
string scriptPath = ProjectSettings.GlobalizePath(script.ResourcePath);
|
||||
@ -395,7 +388,12 @@ namespace GodotTools
|
||||
|
||||
// CheckBox in main container
|
||||
aboutDialogCheckBox = new CheckBox { Text = "Show this warning when starting the editor" };
|
||||
aboutDialogCheckBox.Connect("toggled", this, nameof(_ToggleAboutDialogOnStart));
|
||||
aboutDialogCheckBox.Toggled += enabled =>
|
||||
{
|
||||
bool showOnStart = (bool)editorSettings.GetSetting("mono/editor/show_info_on_start");
|
||||
if (showOnStart != enabled)
|
||||
editorSettings.SetSetting("mono/editor/show_info_on_start", enabled);
|
||||
};
|
||||
aboutVBox.AddChild(aboutDialogCheckBox);
|
||||
}
|
||||
|
||||
@ -424,7 +422,7 @@ namespace GodotTools
|
||||
menuPopup.AddItem("Create C# solution".TTR(), (int)MenuOptions.CreateSln);
|
||||
}
|
||||
|
||||
menuPopup.Connect("id_pressed", this, nameof(_MenuOptionPressed));
|
||||
menuPopup.IdPressed += _MenuOptionPressed;
|
||||
|
||||
var buildButton = new ToolButton
|
||||
{
|
||||
@ -432,7 +430,7 @@ namespace GodotTools
|
||||
HintTooltip = "Build solution",
|
||||
FocusMode = Control.FocusModeEnum.None
|
||||
};
|
||||
buildButton.Connect("pressed", this, nameof(_BuildSolutionPressed));
|
||||
buildButton.PressedSignal += _BuildSolutionPressed;
|
||||
AddControlToContainer(CustomControlContainer.Toolbar, buildButton);
|
||||
|
||||
// External editor settings
|
||||
|
@ -40,7 +40,7 @@ namespace GodotTools
|
||||
OneShot = false,
|
||||
WaitTime = (float)EditorDef("mono/assembly_watch_interval_sec", 0.5)
|
||||
};
|
||||
watchTimer.Connect("timeout", this, nameof(TimerTimeout));
|
||||
watchTimer.Timeout += TimerTimeout;
|
||||
AddChild(watchTimer);
|
||||
watchTimer.Start();
|
||||
}
|
||||
|
@ -76,7 +76,7 @@
|
||||
#define GLUE_HEADER_FILE "glue_header.h"
|
||||
#define ICALL_PREFIX "godot_icall_"
|
||||
#define SINGLETON_ICALL_SUFFIX "_get_singleton"
|
||||
#define ICALL_GET_METHODBIND ICALL_PREFIX "Object_ClassDB_get_method"
|
||||
#define ICALL_GET_METHODBIND "__ClassDB_get_method"
|
||||
|
||||
#define C_LOCAL_RET "ret"
|
||||
#define C_LOCAL_VARARG_RET "vararg_ret"
|
||||
@ -95,6 +95,10 @@
|
||||
#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_CALLABLE C_NS_MONOMARSHAL "::managed_to_callable"
|
||||
#define C_METHOD_MANAGED_FROM_CALLABLE C_NS_MONOMARSHAL "::callable_to_managed"
|
||||
#define C_METHOD_MANAGED_TO_SIGNAL C_NS_MONOMARSHAL "::signal_info_to_callable"
|
||||
#define C_METHOD_MANAGED_FROM_SIGNAL C_NS_MONOMARSHAL "::callable_to_signal_info"
|
||||
|
||||
#define BINDINGS_GENERATOR_VERSION UINT32_C(11)
|
||||
|
||||
@ -504,23 +508,23 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf
|
||||
xml_output.append(tag);
|
||||
xml_output.append("</c>");
|
||||
} else if (tag == "PackedByteArray") {
|
||||
xml_output.append("<see cref=\"byte\"/>");
|
||||
xml_output.append("<see cref=\"T:byte[]\"/>");
|
||||
} else if (tag == "PackedInt32Array") {
|
||||
xml_output.append("<see cref=\"int\"/>");
|
||||
xml_output.append("<see cref=\"T:int[]\"/>");
|
||||
} else if (tag == "PackedInt64Array") {
|
||||
xml_output.append("<see cref=\"T:long[]\"/>");
|
||||
} else if (tag == "PackedFloat32Array") {
|
||||
#ifdef REAL_T_IS_DOUBLE
|
||||
xml_output.append("<see cref=\"double\"/>");
|
||||
#else
|
||||
xml_output.append("<see cref=\"float\"/>");
|
||||
#endif
|
||||
xml_output.append("<see cref=\"T:float[]\"/>");
|
||||
} else if (tag == "PackedFloat64Array") {
|
||||
xml_output.append("<see cref=\"T:double[]\"/>");
|
||||
} else if (tag == "PackedStringArray") {
|
||||
xml_output.append("<see cref=\"string\"/>");
|
||||
xml_output.append("<see cref=\"T:string[]\"/>");
|
||||
} else if (tag == "PackedVector2Array") {
|
||||
xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".Vector2\"/>");
|
||||
xml_output.append("<see cref=\"T:" BINDINGS_NAMESPACE ".Vector2[]\"/>");
|
||||
} else if (tag == "PackedVector3Array") {
|
||||
xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".Vector3\"/>");
|
||||
xml_output.append("<see cref=\"T:" BINDINGS_NAMESPACE ".Vector3[]\"/>");
|
||||
} else if (tag == "PackedColorArray") {
|
||||
xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".Color\"/>");
|
||||
xml_output.append("<see cref=\"T:" BINDINGS_NAMESPACE ".Color[]\"/>");
|
||||
} else {
|
||||
const TypeInterface *target_itype = _get_type_or_null(TypeReference(tag));
|
||||
|
||||
@ -932,7 +936,7 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_proj_dir) {
|
||||
"using System.Runtime.CompilerServices;\n"
|
||||
"\n");
|
||||
cs_icalls_content.append("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK);
|
||||
cs_icalls_content.append(INDENT1 "internal static class " BINDINGS_CLASS_NATIVECALLS "\n" INDENT1 OPEN_BLOCK);
|
||||
cs_icalls_content.append(INDENT1 "internal static class " BINDINGS_CLASS_NATIVECALLS "\n" INDENT1 "{");
|
||||
|
||||
cs_icalls_content.append(MEMBER_BEGIN "internal static ulong godot_api_hash = ");
|
||||
cs_icalls_content.append(String::num_uint64(GDMono::get_singleton()->get_api_core_hash()) + ";\n");
|
||||
@ -944,7 +948,7 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_proj_dir) {
|
||||
#define ADD_INTERNAL_CALL(m_icall) \
|
||||
if (!m_icall.editor_only) { \
|
||||
cs_icalls_content.append(MEMBER_BEGIN "[MethodImpl(MethodImplOptions.InternalCall)]\n"); \
|
||||
cs_icalls_content.append(INDENT2 "internal extern static "); \
|
||||
cs_icalls_content.append(INDENT2 "internal static extern "); \
|
||||
cs_icalls_content.append(m_icall.im_type_out + " "); \
|
||||
cs_icalls_content.append(m_icall.name + "("); \
|
||||
cs_icalls_content.append(m_icall.im_sig + ");\n"); \
|
||||
@ -1046,7 +1050,7 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_proj_dir) {
|
||||
#define ADD_INTERNAL_CALL(m_icall) \
|
||||
if (m_icall.editor_only) { \
|
||||
cs_icalls_content.append(INDENT2 "[MethodImpl(MethodImplOptions.InternalCall)]\n"); \
|
||||
cs_icalls_content.append(INDENT2 "internal extern static "); \
|
||||
cs_icalls_content.append(INDENT2 "internal static extern "); \
|
||||
cs_icalls_content.append(m_icall.im_type_out + " "); \
|
||||
cs_icalls_content.append(m_icall.name + "("); \
|
||||
cs_icalls_content.append(m_icall.im_sig + ");\n"); \
|
||||
@ -1312,7 +1316,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
|
||||
output.append(itype.proxy_name);
|
||||
output.append(").Name);\n" INDENT4 "return singleton;\n" INDENT3 "}\n" INDENT2 "}\n");
|
||||
|
||||
output.append(MEMBER_BEGIN "private const string " BINDINGS_NATIVE_NAME_FIELD " = \"");
|
||||
output.append(MEMBER_BEGIN "private static StringName " BINDINGS_NATIVE_NAME_FIELD " = \"");
|
||||
output.append(itype.name);
|
||||
output.append("\";\n");
|
||||
|
||||
@ -1324,7 +1328,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
|
||||
} else if (is_derived_type) {
|
||||
// Add member fields
|
||||
|
||||
output.append(MEMBER_BEGIN "private const string " BINDINGS_NATIVE_NAME_FIELD " = \"");
|
||||
output.append(MEMBER_BEGIN "private static StringName " BINDINGS_NATIVE_NAME_FIELD " = \"");
|
||||
output.append(itype.name);
|
||||
output.append("\";\n");
|
||||
|
||||
@ -1363,6 +1367,13 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
|
||||
"Failed to generate method '" + imethod.name + "' for class '" + itype.name + "'.");
|
||||
}
|
||||
|
||||
for (const List<SignalInterface>::Element *E = itype.signals_.front(); E; E = E->next()) {
|
||||
const SignalInterface &isignal = E->get();
|
||||
Error method_err = _generate_cs_signal(itype, isignal, output);
|
||||
ERR_FAIL_COND_V_MSG(method_err != OK, method_err,
|
||||
"Failed to generate signal '" + isignal.name + "' for class '" + itype.name + "'.");
|
||||
}
|
||||
|
||||
if (itype.is_singleton) {
|
||||
InternalCall singleton_icall = InternalCall(itype.api_type, ICALL_PREFIX + itype.name + SINGLETON_ICALL_SUFFIX, "IntPtr");
|
||||
|
||||
@ -1424,7 +1435,16 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
|
||||
}
|
||||
|
||||
if (getter && setter) {
|
||||
ERR_FAIL_COND_V(getter->return_type.cname != setter->arguments.back()->get().type.cname, ERR_BUG);
|
||||
const ArgumentInterface &setter_first_arg = setter->arguments.back()->get();
|
||||
if (getter->return_type.cname != setter_first_arg.type.cname) {
|
||||
// Special case for Node::set_name
|
||||
bool whitelisted = getter->return_type.cname == name_cache.type_StringName &&
|
||||
setter_first_arg.type.cname == name_cache.type_String;
|
||||
|
||||
ERR_FAIL_COND_V_MSG(!whitelisted, ERR_BUG,
|
||||
"Return type from getter doesn't match first argument of setter for property: '" +
|
||||
p_itype.name + "." + String(p_iprop.cname) + "'.");
|
||||
}
|
||||
}
|
||||
|
||||
const TypeReference &proptype_name = getter ? getter->return_type : setter->arguments.back()->get().type;
|
||||
@ -1525,7 +1545,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
|
||||
|
||||
const TypeInterface *return_type = _get_type_or_placeholder(p_imethod.return_type);
|
||||
|
||||
String method_bind_field = "method_bind_" + itos(p_method_bind_count);
|
||||
String method_bind_field = "__method_bind_" + itos(p_method_bind_count);
|
||||
|
||||
String arguments_sig;
|
||||
String cs_in_statements;
|
||||
@ -1612,7 +1632,8 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
|
||||
{
|
||||
if (!p_imethod.is_virtual && !p_imethod.requires_object_call) {
|
||||
p_output.append(MEMBER_BEGIN "[DebuggerBrowsable(DebuggerBrowsableState.Never)]" MEMBER_BEGIN "private static IntPtr ");
|
||||
p_output.append(method_bind_field + " = Object." ICALL_GET_METHODBIND "(" BINDINGS_NATIVE_NAME_FIELD ", \"");
|
||||
p_output.append(method_bind_field);
|
||||
p_output.append(" = Object." ICALL_GET_METHODBIND "(" BINDINGS_NATIVE_NAME_FIELD ", \"");
|
||||
p_output.append(p_imethod.name);
|
||||
p_output.append("\");\n");
|
||||
}
|
||||
@ -1726,6 +1747,106 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::SignalInterface &p_isignal, StringBuilder &p_output) {
|
||||
String arguments_sig;
|
||||
|
||||
// Retrieve information from the arguments
|
||||
for (const List<ArgumentInterface>::Element *F = p_isignal.arguments.front(); F; F = F->next()) {
|
||||
const ArgumentInterface &iarg = F->get();
|
||||
const TypeInterface *arg_type = _get_type_or_placeholder(iarg.type);
|
||||
|
||||
// Add the current arguments to the signature
|
||||
|
||||
if (F != p_isignal.arguments.front())
|
||||
arguments_sig += ", ";
|
||||
|
||||
arguments_sig += arg_type->cs_type;
|
||||
arguments_sig += " ";
|
||||
arguments_sig += iarg.name;
|
||||
}
|
||||
|
||||
// Generate signal
|
||||
{
|
||||
if (p_isignal.method_doc && p_isignal.method_doc->description.size()) {
|
||||
String xml_summary = bbcode_to_xml(fix_doc_description(p_isignal.method_doc->description), &p_itype);
|
||||
Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>();
|
||||
|
||||
if (summary_lines.size()) {
|
||||
p_output.append(MEMBER_BEGIN "/// <summary>\n");
|
||||
|
||||
for (int i = 0; i < summary_lines.size(); i++) {
|
||||
p_output.append(INDENT2 "/// ");
|
||||
p_output.append(summary_lines[i]);
|
||||
p_output.append("\n");
|
||||
}
|
||||
|
||||
p_output.append(INDENT2 "/// </summary>");
|
||||
}
|
||||
}
|
||||
|
||||
if (p_isignal.is_deprecated) {
|
||||
if (p_isignal.deprecation_message.empty())
|
||||
WARN_PRINT("An empty deprecation message is discouraged. Signal: '" + p_isignal.proxy_name + "'.");
|
||||
|
||||
p_output.append(MEMBER_BEGIN "[Obsolete(\"");
|
||||
p_output.append(p_isignal.deprecation_message);
|
||||
p_output.append("\")]");
|
||||
}
|
||||
|
||||
String delegate_name = p_isignal.proxy_name;
|
||||
delegate_name += "Handler"; // Delegate name is [SignalName]Handler
|
||||
|
||||
// Generate delegate
|
||||
p_output.append(MEMBER_BEGIN "public delegate void ");
|
||||
p_output.append(delegate_name);
|
||||
p_output.append("(");
|
||||
p_output.append(arguments_sig);
|
||||
p_output.append(");\n");
|
||||
|
||||
// TODO:
|
||||
// Could we assume the StringName instance of signal name will never be freed (it's stored in ClassDB) before the managed world is unloaded?
|
||||
// If so, we could store the pointer we get from `data_unique_pointer()` instead of allocating StringName here.
|
||||
|
||||
// Cached signal name (StringName)
|
||||
p_output.append(MEMBER_BEGIN "[DebuggerBrowsable(DebuggerBrowsableState.Never)]" MEMBER_BEGIN "private static StringName __signal_name_");
|
||||
p_output.append(p_isignal.name);
|
||||
p_output.append(" = \"");
|
||||
p_output.append(p_isignal.name);
|
||||
p_output.append("\";\n");
|
||||
|
||||
// Generate event
|
||||
p_output.append(MEMBER_BEGIN "[Signal]" MEMBER_BEGIN "public ");
|
||||
|
||||
if (p_itype.is_singleton)
|
||||
p_output.append("static ");
|
||||
|
||||
p_output.append("event ");
|
||||
p_output.append(delegate_name);
|
||||
p_output.append(" ");
|
||||
p_output.append(p_isignal.proxy_name);
|
||||
p_output.append("\n" OPEN_BLOCK_L2);
|
||||
|
||||
if (p_itype.is_singleton)
|
||||
p_output.append("add => Singleton.Connect(__signal_name_");
|
||||
else
|
||||
p_output.append("add => Connect(__signal_name_");
|
||||
|
||||
p_output.append(p_isignal.name);
|
||||
p_output.append(", new Callable(value));\n");
|
||||
|
||||
if (p_itype.is_singleton)
|
||||
p_output.append(INDENT3 "remove => Singleton.Disconnect(__signal_name_");
|
||||
else
|
||||
p_output.append(INDENT3 "remove => Disconnect(__signal_name_");
|
||||
|
||||
p_output.append(p_isignal.name);
|
||||
p_output.append(", new Callable(value));\n");
|
||||
p_output.append(CLOSE_BLOCK_L2);
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error BindingsGenerator::generate_glue(const String &p_output_dir) {
|
||||
|
||||
ERR_FAIL_COND_V(!initialized, ERR_UNCONFIGURED);
|
||||
@ -2479,13 +2600,92 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
|
||||
}
|
||||
}
|
||||
|
||||
// Populate signals
|
||||
|
||||
const HashMap<StringName, MethodInfo> &signal_map = class_info->signal_map;
|
||||
const StringName *k = NULL;
|
||||
|
||||
while ((k = signal_map.next(k))) {
|
||||
SignalInterface isignal;
|
||||
|
||||
const MethodInfo &method_info = signal_map.get(*k);
|
||||
|
||||
isignal.name = method_info.name;
|
||||
isignal.cname = method_info.name;
|
||||
|
||||
int argc = method_info.arguments.size();
|
||||
|
||||
for (int i = 0; i < argc; i++) {
|
||||
PropertyInfo arginfo = method_info.arguments[i];
|
||||
|
||||
String orig_arg_name = arginfo.name;
|
||||
|
||||
ArgumentInterface iarg;
|
||||
iarg.name = orig_arg_name;
|
||||
|
||||
if (arginfo.type == Variant::INT && arginfo.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
|
||||
iarg.type.cname = arginfo.class_name;
|
||||
iarg.type.is_enum = true;
|
||||
} else if (arginfo.class_name != StringName()) {
|
||||
iarg.type.cname = arginfo.class_name;
|
||||
} else if (arginfo.hint == PROPERTY_HINT_RESOURCE_TYPE) {
|
||||
iarg.type.cname = arginfo.hint_string;
|
||||
} else if (arginfo.type == Variant::NIL) {
|
||||
iarg.type.cname = name_cache.type_Variant;
|
||||
} else {
|
||||
if (arginfo.type == Variant::INT) {
|
||||
iarg.type.cname = _get_int_type_name_from_meta(GodotTypeInfo::METADATA_NONE);
|
||||
} else if (arginfo.type == Variant::FLOAT) {
|
||||
iarg.type.cname = _get_float_type_name_from_meta(GodotTypeInfo::METADATA_NONE);
|
||||
} else {
|
||||
iarg.type.cname = Variant::get_type_name(arginfo.type);
|
||||
}
|
||||
}
|
||||
|
||||
iarg.name = escape_csharp_keyword(snake_to_camel_case(iarg.name));
|
||||
|
||||
isignal.add_argument(iarg);
|
||||
}
|
||||
|
||||
isignal.proxy_name = escape_csharp_keyword(snake_to_pascal_case(isignal.name));
|
||||
|
||||
// Prevent the signal and its enclosing type from sharing the same name
|
||||
if (isignal.proxy_name == itype.proxy_name) {
|
||||
_log("Name of signal '%s' is ambiguous with the name of its enclosing class '%s'. Renaming signal to '%s_'\n",
|
||||
isignal.proxy_name.utf8().get_data(), itype.proxy_name.utf8().get_data(), isignal.proxy_name.utf8().get_data());
|
||||
|
||||
isignal.proxy_name += "_";
|
||||
}
|
||||
|
||||
if (itype.find_property_by_proxy_name(isignal.proxy_name) || itype.find_method_by_proxy_name(isignal.proxy_name)) {
|
||||
// ClassDB allows signal names that conflict with method or property names.
|
||||
// While registering a signal with a conflicting name is considered wrong,
|
||||
// it may still happen and it may take some time until someone fixes the name.
|
||||
// We can't allow the bindings to be in a broken state while we wait for a fix;
|
||||
// that's why we must handle this possibility by renaming the signal.
|
||||
isignal.proxy_name += "Signal";
|
||||
}
|
||||
|
||||
if (itype.class_doc) {
|
||||
for (int i = 0; i < itype.class_doc->signals.size(); i++) {
|
||||
const DocData::MethodDoc &signal_doc = itype.class_doc->signals[i];
|
||||
if (signal_doc.name == isignal.name) {
|
||||
isignal.method_doc = &signal_doc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
itype.signals_.push_back(isignal);
|
||||
}
|
||||
|
||||
// Populate enums and constants
|
||||
|
||||
List<String> constants;
|
||||
ClassDB::get_integer_constant_list(type_cname, &constants, true);
|
||||
|
||||
const HashMap<StringName, List<StringName>> &enum_map = class_info->enum_map;
|
||||
const StringName *k = NULL;
|
||||
k = NULL;
|
||||
|
||||
while ((k = enum_map.next(k))) {
|
||||
StringName enum_proxy_cname = *k;
|
||||
@ -2587,8 +2787,15 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar
|
||||
#endif
|
||||
break;
|
||||
case Variant::STRING:
|
||||
case Variant::STRING_NAME:
|
||||
case Variant::NODE_PATH:
|
||||
r_iarg.default_argument = "\"" + r_iarg.default_argument + "\"";
|
||||
if (r_iarg.type.cname == name_cache.type_StringName || r_iarg.type.cname == name_cache.type_NodePath) {
|
||||
r_iarg.default_argument = "(%s)\"" + r_iarg.default_argument + "\"";
|
||||
r_iarg.def_param_mode = ArgumentInterface::NULLABLE_REF;
|
||||
} else {
|
||||
CRASH_COND(r_iarg.type.cname != name_cache.type_String);
|
||||
r_iarg.default_argument = "\"" + r_iarg.default_argument + "\"";
|
||||
}
|
||||
break;
|
||||
case Variant::TRANSFORM:
|
||||
if (p_val.operator Transform() == Transform())
|
||||
@ -2630,8 +2837,8 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar
|
||||
case Variant::ARRAY:
|
||||
case Variant::PACKED_BYTE_ARRAY:
|
||||
case Variant::PACKED_INT32_ARRAY:
|
||||
case Variant::PACKED_FLOAT32_ARRAY:
|
||||
case Variant::PACKED_INT64_ARRAY:
|
||||
case Variant::PACKED_FLOAT32_ARRAY:
|
||||
case Variant::PACKED_FLOAT64_ARRAY:
|
||||
case Variant::PACKED_STRING_ARRAY:
|
||||
case Variant::PACKED_VECTOR2_ARRAY:
|
||||
@ -2646,8 +2853,13 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar
|
||||
r_iarg.default_argument = Variant::get_type_name(p_val.get_type()) + ".Identity";
|
||||
r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
|
||||
break;
|
||||
default: {
|
||||
}
|
||||
case Variant::CALLABLE:
|
||||
case Variant::SIGNAL:
|
||||
CRASH_NOW_MSG("Parameter of type '" + String(r_iarg.type.cname) + "' cannot have a default value.");
|
||||
break;
|
||||
default:
|
||||
CRASH_NOW_MSG("Unexpected Variant type: " + itos(p_val.get_type()));
|
||||
break;
|
||||
}
|
||||
|
||||
if (r_iarg.def_param_mode == ArgumentInterface::CONSTANT && r_iarg.type.cname == name_cache.type_Variant && r_iarg.default_argument != "null")
|
||||
@ -2672,7 +2884,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
|
||||
itype.c_type_out = "GDMonoMarshal::M_" #m_type; \
|
||||
itype.cs_in = "ref %s"; \
|
||||
/* in cs_out, im_type_out (%3) includes the 'out ' part */ \
|
||||
itype.cs_out = "%0(%1, %3 argRet); return (%2)argRet;"; \
|
||||
itype.cs_out = "%0(%1, %3 argRet); return argRet;"; \
|
||||
itype.im_type_out = "out " + itype.cs_type; \
|
||||
itype.ret_as_byref_arg = true; \
|
||||
builtin_types.insert(itype.cname, itype); \
|
||||
@ -2749,7 +2961,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
|
||||
itype.im_type_out = "out " + itype.name;
|
||||
itype.cs_in = "ref %0";
|
||||
/* in cs_out, im_type_out (%3) includes the 'out ' part */
|
||||
itype.cs_out = "%0(%1, %3 argRet); return (%2)argRet;";
|
||||
itype.cs_out = "%0(%1, %3 argRet); return argRet;";
|
||||
itype.ret_as_byref_arg = true;
|
||||
builtin_types.insert(itype.cname, itype);
|
||||
|
||||
@ -2766,7 +2978,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
|
||||
itype.im_type_out = "out " + itype.name;
|
||||
itype.cs_in = "ref %0";
|
||||
/* in cs_out, im_type_out (%3) includes the 'out ' part */
|
||||
itype.cs_out = "%0(%1, %3 argRet); return (%2)argRet;";
|
||||
itype.cs_out = "%0(%1, %3 argRet); return argRet;";
|
||||
itype.ret_as_byref_arg = true;
|
||||
builtin_types.insert(itype.cname, itype);
|
||||
}
|
||||
@ -2792,7 +3004,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
|
||||
itype.im_type_out = "out " + itype.proxy_name;
|
||||
itype.cs_in = "ref %0";
|
||||
/* in cs_out, im_type_out (%3) includes the 'out ' part */
|
||||
itype.cs_out = "%0(%1, %3 argRet); return (%2)argRet;";
|
||||
itype.cs_out = "%0(%1, %3 argRet); return argRet;";
|
||||
itype.ret_as_byref_arg = true;
|
||||
builtin_types.insert(itype.cname, itype);
|
||||
|
||||
@ -2814,7 +3026,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
|
||||
itype.im_type_out = "out " + itype.proxy_name;
|
||||
itype.cs_in = "ref %0";
|
||||
/* in cs_out, im_type_out (%3) includes the 'out ' part */
|
||||
itype.cs_out = "%0(%1, %3 argRet); return (%2)argRet;";
|
||||
itype.cs_out = "%0(%1, %3 argRet); return argRet;";
|
||||
itype.ret_as_byref_arg = true;
|
||||
builtin_types.insert(itype.cname, itype);
|
||||
}
|
||||
@ -2835,6 +3047,24 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
|
||||
itype.im_type_out = itype.proxy_name;
|
||||
builtin_types.insert(itype.cname, itype);
|
||||
|
||||
// StringName
|
||||
itype = TypeInterface();
|
||||
itype.name = "StringName";
|
||||
itype.cname = itype.name;
|
||||
itype.proxy_name = "StringName";
|
||||
itype.c_in = "\t%0 %1_in = %1 ? *%1 : StringName();\n";
|
||||
itype.c_out = "\treturn memnew(StringName(%1));\n";
|
||||
itype.c_arg_in = "&%s_in";
|
||||
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 = "StringName." CS_SMETHOD_GETINSTANCE "(%0)";
|
||||
itype.cs_out = "return new %2(%0(%1));";
|
||||
itype.im_type_in = "IntPtr";
|
||||
itype.im_type_out = "IntPtr";
|
||||
builtin_types.insert(itype.cname, itype);
|
||||
|
||||
// NodePath
|
||||
itype = TypeInterface();
|
||||
itype.name = "NodePath";
|
||||
@ -2883,6 +3113,40 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
|
||||
itype.im_type_out = itype.proxy_name;
|
||||
builtin_types.insert(itype.cname, itype);
|
||||
|
||||
// Callable
|
||||
itype = TypeInterface::create_value_type(String("Callable"));
|
||||
itype.c_in = "\t%0 %1_in = " C_METHOD_MANAGED_TO_CALLABLE "(*%1);\n";
|
||||
itype.c_out = "\t*%3 = " C_METHOD_MANAGED_FROM_CALLABLE "(%1);\n";
|
||||
itype.c_arg_in = "&%s_in";
|
||||
itype.c_type_in = "GDMonoMarshal::M_Callable*";
|
||||
itype.c_type_out = "GDMonoMarshal::M_Callable";
|
||||
itype.cs_in = "ref %s";
|
||||
/* in cs_out, im_type_out (%3) includes the 'out ' part */
|
||||
itype.cs_out = "%0(%1, %3 argRet); return argRet;";
|
||||
itype.im_type_out = "out " + itype.cs_type;
|
||||
itype.ret_as_byref_arg = true;
|
||||
builtin_types.insert(itype.cname, itype);
|
||||
|
||||
// Signal
|
||||
itype = TypeInterface();
|
||||
itype.name = "Signal";
|
||||
itype.cname = itype.name;
|
||||
itype.proxy_name = "SignalInfo";
|
||||
itype.c_in = "\t%0 %1_in = " C_METHOD_MANAGED_TO_SIGNAL "(*%1);\n";
|
||||
itype.c_out = "\t*%3 = " C_METHOD_MANAGED_FROM_SIGNAL "(%1);\n";
|
||||
itype.c_arg_in = "&%s_in";
|
||||
itype.c_type = itype.name;
|
||||
itype.c_type_in = "GDMonoMarshal::M_SignalInfo*";
|
||||
itype.c_type_out = "GDMonoMarshal::M_SignalInfo";
|
||||
itype.cs_in = "ref %s";
|
||||
/* in cs_out, im_type_out (%3) includes the 'out ' part */
|
||||
itype.cs_out = "%0(%1, %3 argRet); return argRet;";
|
||||
itype.cs_type = itype.proxy_name;
|
||||
itype.im_type_in = "ref " + itype.cs_type;
|
||||
itype.im_type_out = "out " + itype.cs_type;
|
||||
itype.ret_as_byref_arg = true;
|
||||
builtin_types.insert(itype.cname, itype);
|
||||
|
||||
// VarArg (fictitious type to represent variable arguments)
|
||||
itype = TypeInterface();
|
||||
itype.name = "VarArg";
|
||||
@ -2917,13 +3181,11 @@ 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(PackedInt32Array, int);
|
||||
INSERT_ARRAY(PackedInt64Array, long);
|
||||
INSERT_ARRAY_FULL(PackedByteArray, PackedByteArray, byte);
|
||||
|
||||
#ifdef REAL_T_IS_DOUBLE
|
||||
INSERT_ARRAY(PackedFloat32Array, double);
|
||||
#else
|
||||
INSERT_ARRAY(PackedFloat32Array, float);
|
||||
#endif
|
||||
INSERT_ARRAY(PackedFloat64Array, double);
|
||||
|
||||
INSERT_ARRAY(PackedStringArray, string);
|
||||
|
||||
|
@ -107,9 +107,15 @@ class BindingsGenerator {
|
||||
TypeReference type;
|
||||
|
||||
String name;
|
||||
String default_argument;
|
||||
DefaultParamMode def_param_mode;
|
||||
|
||||
/**
|
||||
* Determines the expression for the parameter default value.
|
||||
* Formatting elements:
|
||||
* %0 or %s: [cs_type] of the argument type
|
||||
*/
|
||||
String default_argument;
|
||||
|
||||
ArgumentInterface() {
|
||||
def_param_mode = CONSTANT;
|
||||
}
|
||||
@ -175,6 +181,32 @@ class BindingsGenerator {
|
||||
}
|
||||
};
|
||||
|
||||
struct SignalInterface {
|
||||
String name;
|
||||
StringName cname;
|
||||
|
||||
/**
|
||||
* Name of the C# method
|
||||
*/
|
||||
String proxy_name;
|
||||
|
||||
List<ArgumentInterface> arguments;
|
||||
|
||||
const DocData::MethodDoc *method_doc;
|
||||
|
||||
bool is_deprecated;
|
||||
String deprecation_message;
|
||||
|
||||
void add_argument(const ArgumentInterface &argument) {
|
||||
arguments.push_back(argument);
|
||||
}
|
||||
|
||||
SignalInterface() {
|
||||
method_doc = NULL;
|
||||
is_deprecated = false;
|
||||
}
|
||||
};
|
||||
|
||||
struct TypeInterface {
|
||||
/**
|
||||
* Identifier name for this type.
|
||||
@ -336,6 +368,7 @@ class BindingsGenerator {
|
||||
List<EnumInterface> enums;
|
||||
List<PropertyInterface> properties;
|
||||
List<MethodInterface> methods;
|
||||
List<SignalInterface> signals_;
|
||||
|
||||
const MethodInterface *find_method_by_name(const StringName &p_cname) const {
|
||||
for (const List<MethodInterface>::Element *E = methods.front(); E; E = E->next()) {
|
||||
@ -364,6 +397,15 @@ class BindingsGenerator {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const MethodInterface *find_method_by_proxy_name(const String &p_proxy_name) const {
|
||||
for (const List<MethodInterface>::Element *E = methods.front(); E; E = E->next()) {
|
||||
if (E->get().proxy_name == p_proxy_name)
|
||||
return &E->get();
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
private:
|
||||
static void _init_value_type(TypeInterface &itype) {
|
||||
itype.proxy_name = itype.name;
|
||||
@ -524,6 +566,8 @@ class BindingsGenerator {
|
||||
StringName type_Reference;
|
||||
StringName type_RID;
|
||||
StringName type_String;
|
||||
StringName type_StringName;
|
||||
StringName type_NodePath;
|
||||
StringName type_at_GlobalScope;
|
||||
StringName enum_Error;
|
||||
|
||||
@ -548,6 +592,8 @@ class BindingsGenerator {
|
||||
type_Reference = StaticCString::create("Reference");
|
||||
type_RID = StaticCString::create("RID");
|
||||
type_String = StaticCString::create("String");
|
||||
type_StringName = StaticCString::create("StringName");
|
||||
type_NodePath = StaticCString::create("NodePath");
|
||||
type_at_GlobalScope = StaticCString::create("@GlobalScope");
|
||||
enum_Error = StaticCString::create("Error");
|
||||
|
||||
@ -623,6 +669,7 @@ class BindingsGenerator {
|
||||
|
||||
Error _generate_cs_property(const TypeInterface &p_itype, const PropertyInterface &p_iprop, StringBuilder &p_output);
|
||||
Error _generate_cs_method(const TypeInterface &p_itype, const MethodInterface &p_imethod, int &p_method_bind_count, StringBuilder &p_output);
|
||||
Error _generate_cs_signal(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::SignalInterface &p_isignal, StringBuilder &p_output);
|
||||
|
||||
void _generate_global_constants(StringBuilder &p_output);
|
||||
|
||||
|
@ -2,7 +2,7 @@ using System;
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Delegate)]
|
||||
[AttributeUsage(AttributeTargets.Delegate | AttributeTargets.Event)]
|
||||
public class SignalAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
31
modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs
Normal file
31
modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs
Normal file
@ -0,0 +1,31 @@
|
||||
using System;
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
public struct Callable
|
||||
{
|
||||
private readonly Object _target;
|
||||
private readonly StringName _method;
|
||||
private readonly Delegate _delegate;
|
||||
|
||||
public Object Target => _target;
|
||||
public StringName Method => _method;
|
||||
public Delegate Delegate => _delegate;
|
||||
|
||||
public static implicit operator Callable(Delegate @delegate) => new Callable(@delegate);
|
||||
|
||||
public Callable(Object target, StringName method)
|
||||
{
|
||||
_target = target;
|
||||
_method = method;
|
||||
_delegate = null;
|
||||
}
|
||||
|
||||
public Callable(Delegate @delegate)
|
||||
{
|
||||
_target = null;
|
||||
_method = null;
|
||||
_delegate = @delegate;
|
||||
}
|
||||
}
|
||||
}
|
395
modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs
Normal file
395
modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs
Normal file
@ -0,0 +1,395 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
internal static class DelegateUtils
|
||||
{
|
||||
private enum TargetKind : uint
|
||||
{
|
||||
Static,
|
||||
GodotObject,
|
||||
CompilerGenerated
|
||||
}
|
||||
|
||||
internal static bool TrySerializeDelegate(Delegate @delegate, Collections.Array serializedData)
|
||||
{
|
||||
if (@delegate is MulticastDelegate multicastDelegate)
|
||||
{
|
||||
bool someDelegatesSerialized = false;
|
||||
|
||||
Delegate[] invocationList = multicastDelegate.GetInvocationList();
|
||||
|
||||
if (invocationList.Length > 1)
|
||||
{
|
||||
var multiCastData = new Collections.Array();
|
||||
|
||||
foreach (Delegate oneDelegate in invocationList)
|
||||
someDelegatesSerialized |= TrySerializeDelegate(oneDelegate, multiCastData);
|
||||
|
||||
if (!someDelegatesSerialized)
|
||||
return false;
|
||||
|
||||
serializedData.Add(multiCastData);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (TrySerializeSingleDelegate(@delegate, out byte[] buffer))
|
||||
{
|
||||
serializedData.Add(buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool TrySerializeSingleDelegate(Delegate @delegate, out byte[] buffer)
|
||||
{
|
||||
buffer = null;
|
||||
|
||||
object target = @delegate.Target;
|
||||
|
||||
switch (target)
|
||||
{
|
||||
case null:
|
||||
{
|
||||
using (var stream = new MemoryStream())
|
||||
using (var writer = new BinaryWriter(stream))
|
||||
{
|
||||
writer.Write((ulong) TargetKind.Static);
|
||||
|
||||
SerializeType(writer, @delegate.GetType());
|
||||
|
||||
if (!TrySerializeMethodInfo(writer, @delegate.Method))
|
||||
return false;
|
||||
|
||||
buffer = stream.ToArray();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
case Godot.Object godotObject:
|
||||
{
|
||||
using (var stream = new MemoryStream())
|
||||
using (var writer = new BinaryWriter(stream))
|
||||
{
|
||||
writer.Write((ulong) TargetKind.GodotObject);
|
||||
writer.Write((ulong) godotObject.GetInstanceId());
|
||||
|
||||
SerializeType(writer, @delegate.GetType());
|
||||
|
||||
if (!TrySerializeMethodInfo(writer, @delegate.Method))
|
||||
return false;
|
||||
|
||||
buffer = stream.ToArray();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
default:
|
||||
{
|
||||
Type targetType = target.GetType();
|
||||
|
||||
if (targetType.GetCustomAttribute(typeof(CompilerGeneratedAttribute), true) != null)
|
||||
{
|
||||
// Compiler generated. Probably a closure. Try to serialize it.
|
||||
|
||||
using (var stream = new MemoryStream())
|
||||
using (var writer = new BinaryWriter(stream))
|
||||
{
|
||||
writer.Write((ulong) TargetKind.CompilerGenerated);
|
||||
SerializeType(writer, targetType);
|
||||
|
||||
SerializeType(writer, @delegate.GetType());
|
||||
|
||||
if (!TrySerializeMethodInfo(writer, @delegate.Method))
|
||||
return false;
|
||||
|
||||
FieldInfo[] fields = targetType.GetFields(BindingFlags.Instance | BindingFlags.Public);
|
||||
|
||||
writer.Write(fields.Length);
|
||||
|
||||
foreach (FieldInfo field in fields)
|
||||
{
|
||||
Type fieldType = field.GetType();
|
||||
|
||||
Variant.Type variantType = GD.TypeToVariantType(fieldType);
|
||||
|
||||
if (variantType == Variant.Type.Nil)
|
||||
return false;
|
||||
|
||||
writer.Write(field.Name);
|
||||
byte[] valueBuffer = GD.Var2Bytes(field.GetValue(target));
|
||||
writer.Write(valueBuffer.Length);
|
||||
writer.Write(valueBuffer);
|
||||
}
|
||||
|
||||
buffer = stream.ToArray();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TrySerializeMethodInfo(BinaryWriter writer, MethodInfo methodInfo)
|
||||
{
|
||||
if (methodInfo == null)
|
||||
return false;
|
||||
|
||||
SerializeType(writer, methodInfo.DeclaringType);
|
||||
|
||||
writer.Write(methodInfo.Name);
|
||||
|
||||
int flags = 0;
|
||||
|
||||
if (methodInfo.IsPublic)
|
||||
flags |= (int) BindingFlags.Public;
|
||||
else
|
||||
flags |= (int) BindingFlags.NonPublic;
|
||||
|
||||
if (methodInfo.IsStatic)
|
||||
flags |= (int) BindingFlags.Static;
|
||||
else
|
||||
flags |= (int) BindingFlags.Instance;
|
||||
|
||||
writer.Write(flags);
|
||||
|
||||
Type returnType = methodInfo.ReturnType;
|
||||
bool hasReturn = methodInfo.ReturnType != typeof(void);
|
||||
|
||||
writer.Write(hasReturn);
|
||||
if (hasReturn)
|
||||
SerializeType(writer, returnType);
|
||||
|
||||
ParameterInfo[] parameters = methodInfo.GetParameters();
|
||||
|
||||
writer.Write(parameters.Length);
|
||||
|
||||
if (parameters.Length > 0)
|
||||
{
|
||||
for (int i = 0; i < parameters.Length; i++)
|
||||
SerializeType(writer, parameters[i].ParameterType);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void SerializeType(BinaryWriter writer, Type type)
|
||||
{
|
||||
if (type == null)
|
||||
{
|
||||
int genericArgumentsCount = -1;
|
||||
writer.Write(genericArgumentsCount);
|
||||
}
|
||||
else if (type.IsGenericType)
|
||||
{
|
||||
Type genericTypeDef = type.GetGenericTypeDefinition();
|
||||
Type[] genericArgs = type.GetGenericArguments();
|
||||
|
||||
int genericArgumentsCount = genericArgs.Length;
|
||||
writer.Write(genericArgumentsCount);
|
||||
|
||||
string assemblyQualifiedName = genericTypeDef.AssemblyQualifiedName;
|
||||
Debug.Assert(assemblyQualifiedName != null);
|
||||
writer.Write(assemblyQualifiedName);
|
||||
|
||||
for (int i = 0; i < genericArgs.Length; i++)
|
||||
SerializeType(writer, genericArgs[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
int genericArgumentsCount = 0;
|
||||
writer.Write(genericArgumentsCount);
|
||||
|
||||
string assemblyQualifiedName = type.AssemblyQualifiedName;
|
||||
Debug.Assert(assemblyQualifiedName != null);
|
||||
writer.Write(assemblyQualifiedName);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryDeserializeDelegate(Collections.Array serializedData, out Delegate @delegate)
|
||||
{
|
||||
if (serializedData.Count == 1)
|
||||
{
|
||||
object elem = serializedData[0];
|
||||
|
||||
if (elem is Collections.Array multiCastData)
|
||||
return TryDeserializeDelegate(multiCastData, out @delegate);
|
||||
|
||||
return TryDeserializeSingleDelegate((byte[])elem, out @delegate);
|
||||
}
|
||||
|
||||
@delegate = null;
|
||||
|
||||
var delegates = new List<Delegate>(serializedData.Count);
|
||||
|
||||
foreach (object elem in serializedData)
|
||||
{
|
||||
if (elem is Collections.Array multiCastData)
|
||||
{
|
||||
if (TryDeserializeDelegate(multiCastData, out Delegate oneDelegate))
|
||||
delegates.Add(oneDelegate);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (TryDeserializeSingleDelegate((byte[]) elem, out Delegate oneDelegate))
|
||||
delegates.Add(oneDelegate);
|
||||
}
|
||||
}
|
||||
|
||||
if (delegates.Count <= 0)
|
||||
return false;
|
||||
|
||||
@delegate = delegates.Count == 1 ? delegates[0] : Delegate.Combine(delegates.ToArray());
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryDeserializeSingleDelegate(byte[] buffer, out Delegate @delegate)
|
||||
{
|
||||
@delegate = null;
|
||||
|
||||
using (var stream = new MemoryStream(buffer, writable: false))
|
||||
using (var reader = new BinaryReader(stream))
|
||||
{
|
||||
var targetKind = (TargetKind) reader.ReadUInt64();
|
||||
|
||||
switch (targetKind)
|
||||
{
|
||||
case TargetKind.Static:
|
||||
{
|
||||
Type delegateType = DeserializeType(reader);
|
||||
if (delegateType == null)
|
||||
return false;
|
||||
|
||||
if (!TryDeserializeMethodInfo(reader, out MethodInfo methodInfo))
|
||||
return false;
|
||||
|
||||
@delegate = Delegate.CreateDelegate(delegateType, null, methodInfo);
|
||||
return true;
|
||||
}
|
||||
case TargetKind.GodotObject:
|
||||
{
|
||||
ulong objectId = reader.ReadUInt64();
|
||||
Godot.Object godotObject = GD.InstanceFromId(objectId);
|
||||
if (godotObject == null)
|
||||
return false;
|
||||
|
||||
Type delegateType = DeserializeType(reader);
|
||||
if (delegateType == null)
|
||||
return false;
|
||||
|
||||
if (!TryDeserializeMethodInfo(reader, out MethodInfo methodInfo))
|
||||
return false;
|
||||
|
||||
@delegate = Delegate.CreateDelegate(delegateType, godotObject, methodInfo);
|
||||
return true;
|
||||
}
|
||||
case TargetKind.CompilerGenerated:
|
||||
{
|
||||
Type targetType = DeserializeType(reader);
|
||||
if (targetType == null)
|
||||
return false;
|
||||
|
||||
Type delegateType = DeserializeType(reader);
|
||||
if (delegateType == null)
|
||||
return false;
|
||||
|
||||
if (!TryDeserializeMethodInfo(reader, out MethodInfo methodInfo))
|
||||
return false;
|
||||
|
||||
int fieldCount = reader.ReadInt32();
|
||||
|
||||
object recreatedTarget = Activator.CreateInstance(targetType);
|
||||
|
||||
for (int i = 0; i < fieldCount; i++)
|
||||
{
|
||||
string name = reader.ReadString();
|
||||
int valueBufferLength = reader.ReadInt32();
|
||||
byte[] valueBuffer = reader.ReadBytes(valueBufferLength);
|
||||
|
||||
FieldInfo fieldInfo = targetType.GetField(name, BindingFlags.Instance | BindingFlags.Public);
|
||||
fieldInfo?.SetValue(recreatedTarget, GD.Bytes2Var(valueBuffer));
|
||||
}
|
||||
|
||||
@delegate = Delegate.CreateDelegate(delegateType, recreatedTarget, methodInfo);
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryDeserializeMethodInfo(BinaryReader reader, out MethodInfo methodInfo)
|
||||
{
|
||||
methodInfo = null;
|
||||
|
||||
Type declaringType = DeserializeType(reader);
|
||||
|
||||
string methodName = reader.ReadString();
|
||||
|
||||
int flags = reader.ReadInt32();
|
||||
|
||||
bool hasReturn = reader.ReadBoolean();
|
||||
Type returnType = hasReturn ? DeserializeType(reader) : typeof(void);
|
||||
|
||||
int parametersCount = reader.ReadInt32();
|
||||
|
||||
if (parametersCount > 0)
|
||||
{
|
||||
var parameterTypes = new Type[parametersCount];
|
||||
|
||||
for (int i = 0; i < parametersCount; i++)
|
||||
{
|
||||
Type parameterType = DeserializeType(reader);
|
||||
if (parameterType == null)
|
||||
return false;
|
||||
parameterTypes[i] = parameterType;
|
||||
}
|
||||
|
||||
methodInfo = declaringType.GetMethod(methodName, (BindingFlags) flags, null, parameterTypes, null);
|
||||
return methodInfo != null && methodInfo.ReturnType == returnType;
|
||||
}
|
||||
|
||||
methodInfo = declaringType.GetMethod(methodName, (BindingFlags) flags);
|
||||
return methodInfo != null && methodInfo.ReturnType == returnType;
|
||||
}
|
||||
|
||||
private static Type DeserializeType(BinaryReader reader)
|
||||
{
|
||||
int genericArgumentsCount = reader.ReadInt32();
|
||||
|
||||
if (genericArgumentsCount == -1)
|
||||
return null;
|
||||
|
||||
string assemblyQualifiedName = reader.ReadString();
|
||||
var type = Type.GetType(assemblyQualifiedName);
|
||||
|
||||
if (type == null)
|
||||
return null; // Type not found
|
||||
|
||||
if (genericArgumentsCount != 0)
|
||||
{
|
||||
var genericArgumentTypes = new Type[genericArgumentsCount];
|
||||
|
||||
for (int i = 0; i < genericArgumentsCount; i++)
|
||||
{
|
||||
Type genericArgumentType = DeserializeType(reader);
|
||||
if (genericArgumentType == null)
|
||||
return null;
|
||||
genericArgumentTypes[i] = genericArgumentType;
|
||||
}
|
||||
|
||||
type = type.MakeGenericType(genericArgumentTypes);
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@ using System.Runtime.CompilerServices;
|
||||
using real_t = System.Double;
|
||||
#else
|
||||
using real_t = System.Single;
|
||||
|
||||
#endif
|
||||
|
||||
// TODO: Add comments describing what this class does. It is not obvious.
|
||||
@ -13,9 +14,9 @@ namespace Godot
|
||||
{
|
||||
public static partial class GD
|
||||
{
|
||||
public static object Bytes2Var(byte[] bytes, bool allow_objects = false)
|
||||
public static object Bytes2Var(byte[] bytes, bool allowObjects = false)
|
||||
{
|
||||
return godot_icall_GD_bytes2var(bytes, allow_objects);
|
||||
return godot_icall_GD_bytes2var(bytes, allowObjects);
|
||||
}
|
||||
|
||||
public static object Convert(object what, Variant.Type type)
|
||||
@ -25,7 +26,7 @@ namespace Godot
|
||||
|
||||
public static real_t Db2Linear(real_t db)
|
||||
{
|
||||
return (real_t)Math.Exp(db * 0.11512925464970228420089957273422);
|
||||
return (real_t) Math.Exp(db * 0.11512925464970228420089957273422);
|
||||
}
|
||||
|
||||
public static real_t DecTime(real_t value, real_t amount, real_t step)
|
||||
@ -38,11 +39,11 @@ namespace Godot
|
||||
return val * sgn;
|
||||
}
|
||||
|
||||
public static FuncRef FuncRef(Object instance, string funcname)
|
||||
public static FuncRef FuncRef(Object instance, StringName funcName)
|
||||
{
|
||||
var ret = new FuncRef();
|
||||
ret.SetInstance(instance);
|
||||
ret.SetFunction(funcname);
|
||||
ret.SetFunction(funcName);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -58,7 +59,7 @@ namespace Godot
|
||||
|
||||
public static real_t Linear2Db(real_t linear)
|
||||
{
|
||||
return (real_t)(Math.Log(linear) * 8.6858896380650365530225783783321);
|
||||
return (real_t) (Math.Log(linear) * 8.6858896380650365530225783783321);
|
||||
}
|
||||
|
||||
public static Resource Load(string path)
|
||||
@ -181,14 +182,14 @@ namespace Godot
|
||||
return godot_icall_GD_str2var(str);
|
||||
}
|
||||
|
||||
public static bool TypeExists(string type)
|
||||
public static bool TypeExists(StringName type)
|
||||
{
|
||||
return godot_icall_GD_type_exists(type);
|
||||
return godot_icall_GD_type_exists(StringName.GetPtr(type));
|
||||
}
|
||||
|
||||
public static byte[] Var2Bytes(object var, bool full_objects = false)
|
||||
public static byte[] Var2Bytes(object var, bool fullObjects = false)
|
||||
{
|
||||
return godot_icall_GD_var2bytes(var, full_objects);
|
||||
return godot_icall_GD_var2bytes(var, fullObjects);
|
||||
}
|
||||
|
||||
public static string Var2Str(object var)
|
||||
@ -196,8 +197,13 @@ namespace Godot
|
||||
return godot_icall_GD_var2str(var);
|
||||
}
|
||||
|
||||
public static Variant.Type TypeToVariantType(Type type)
|
||||
{
|
||||
return godot_icall_TypeToVariantType(type);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static object godot_icall_GD_bytes2var(byte[] bytes, bool allow_objects);
|
||||
internal extern static object godot_icall_GD_bytes2var(byte[] bytes, bool allowObjects);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static object godot_icall_GD_convert(object what, Variant.Type type);
|
||||
@ -206,7 +212,7 @@ namespace Godot
|
||||
internal extern static int godot_icall_GD_hash(object var);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static Object godot_icall_GD_instance_from_id(ulong instance_id);
|
||||
internal extern static Object godot_icall_GD_instance_from_id(ulong instanceId);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static void godot_icall_GD_print(object[] what);
|
||||
@ -249,10 +255,10 @@ namespace Godot
|
||||
internal extern static object godot_icall_GD_str2var(string str);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static bool godot_icall_GD_type_exists(string type);
|
||||
internal extern static bool godot_icall_GD_type_exists(IntPtr type);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static byte[] godot_icall_GD_var2bytes(object what, bool full_objects);
|
||||
internal extern static byte[] godot_icall_GD_var2bytes(object what, bool fullObjects);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static string godot_icall_GD_var2str(object var);
|
||||
@ -262,5 +268,8 @@ namespace Godot
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static void godot_icall_GD_pushwarning(string type);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern Variant.Type godot_icall_TypeToVariantType(Type type);
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ namespace Godot
|
||||
{
|
||||
private bool disposed = false;
|
||||
|
||||
internal IntPtr ptr;
|
||||
private IntPtr ptr;
|
||||
|
||||
internal static IntPtr GetPtr(NodePath instance)
|
||||
{
|
||||
@ -50,104 +50,93 @@ namespace Godot
|
||||
this.ptr = ptr;
|
||||
}
|
||||
|
||||
public IntPtr NativeInstance
|
||||
{
|
||||
get { return ptr; }
|
||||
}
|
||||
|
||||
public NodePath() : this(string.Empty) {}
|
||||
|
||||
public NodePath(string path)
|
||||
{
|
||||
this.ptr = godot_icall_NodePath_Ctor(path);
|
||||
ptr = godot_icall_NodePath_Ctor(path);
|
||||
}
|
||||
|
||||
public static implicit operator NodePath(string from)
|
||||
{
|
||||
return new NodePath(from);
|
||||
}
|
||||
public static implicit operator NodePath(string from) => new NodePath(from);
|
||||
|
||||
public static implicit operator string(NodePath from)
|
||||
{
|
||||
return godot_icall_NodePath_operator_String(NodePath.GetPtr(from));
|
||||
}
|
||||
public static implicit operator string(NodePath from) => from.ToString();
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return (string)this;
|
||||
return godot_icall_NodePath_operator_String(GetPtr(this));
|
||||
}
|
||||
|
||||
public NodePath GetAsPropertyPath()
|
||||
{
|
||||
return new NodePath(godot_icall_NodePath_get_as_property_path(NodePath.GetPtr(this)));
|
||||
return new NodePath(godot_icall_NodePath_get_as_property_path(GetPtr(this)));
|
||||
}
|
||||
|
||||
public string GetConcatenatedSubnames()
|
||||
{
|
||||
return godot_icall_NodePath_get_concatenated_subnames(NodePath.GetPtr(this));
|
||||
return godot_icall_NodePath_get_concatenated_subnames(GetPtr(this));
|
||||
}
|
||||
|
||||
public string GetName(int idx)
|
||||
{
|
||||
return godot_icall_NodePath_get_name(NodePath.GetPtr(this), idx);
|
||||
return godot_icall_NodePath_get_name(GetPtr(this), idx);
|
||||
}
|
||||
|
||||
public int GetNameCount()
|
||||
{
|
||||
return godot_icall_NodePath_get_name_count(NodePath.GetPtr(this));
|
||||
return godot_icall_NodePath_get_name_count(GetPtr(this));
|
||||
}
|
||||
|
||||
public string GetSubname(int idx)
|
||||
{
|
||||
return godot_icall_NodePath_get_subname(NodePath.GetPtr(this), idx);
|
||||
return godot_icall_NodePath_get_subname(GetPtr(this), idx);
|
||||
}
|
||||
|
||||
public int GetSubnameCount()
|
||||
{
|
||||
return godot_icall_NodePath_get_subname_count(NodePath.GetPtr(this));
|
||||
return godot_icall_NodePath_get_subname_count(GetPtr(this));
|
||||
}
|
||||
|
||||
public bool IsAbsolute()
|
||||
{
|
||||
return godot_icall_NodePath_is_absolute(NodePath.GetPtr(this));
|
||||
return godot_icall_NodePath_is_absolute(GetPtr(this));
|
||||
}
|
||||
|
||||
public bool IsEmpty()
|
||||
{
|
||||
return godot_icall_NodePath_is_empty(NodePath.GetPtr(this));
|
||||
return godot_icall_NodePath_is_empty(GetPtr(this));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static IntPtr godot_icall_NodePath_Ctor(string path);
|
||||
private static extern IntPtr godot_icall_NodePath_Ctor(string path);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static void godot_icall_NodePath_Dtor(IntPtr ptr);
|
||||
private static extern void godot_icall_NodePath_Dtor(IntPtr ptr);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static string godot_icall_NodePath_operator_String(IntPtr ptr);
|
||||
private static extern string godot_icall_NodePath_operator_String(IntPtr ptr);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static IntPtr godot_icall_NodePath_get_as_property_path(IntPtr ptr);
|
||||
private static extern IntPtr godot_icall_NodePath_get_as_property_path(IntPtr ptr);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static string godot_icall_NodePath_get_concatenated_subnames(IntPtr ptr);
|
||||
private static extern string godot_icall_NodePath_get_concatenated_subnames(IntPtr ptr);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static string godot_icall_NodePath_get_name(IntPtr ptr, int arg1);
|
||||
private static extern string godot_icall_NodePath_get_name(IntPtr ptr, int arg1);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static int godot_icall_NodePath_get_name_count(IntPtr ptr);
|
||||
private static extern int godot_icall_NodePath_get_name_count(IntPtr ptr);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static string godot_icall_NodePath_get_subname(IntPtr ptr, int arg1);
|
||||
private static extern string godot_icall_NodePath_get_subname(IntPtr ptr, int arg1);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static int godot_icall_NodePath_get_subname_count(IntPtr ptr);
|
||||
private static extern int godot_icall_NodePath_get_subname_count(IntPtr ptr);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static bool godot_icall_NodePath_is_absolute(IntPtr ptr);
|
||||
private static extern bool godot_icall_NodePath_is_absolute(IntPtr ptr);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static bool godot_icall_NodePath_is_empty(IntPtr ptr);
|
||||
private static extern bool godot_icall_NodePath_is_empty(IntPtr ptr);
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ namespace Godot
|
||||
{
|
||||
private bool disposed = false;
|
||||
|
||||
private const string nativeName = "Object";
|
||||
private static StringName nativeName = "Object";
|
||||
|
||||
internal IntPtr ptr;
|
||||
internal bool memoryOwn;
|
||||
@ -15,7 +15,14 @@ namespace Godot
|
||||
public Object() : this(false)
|
||||
{
|
||||
if (ptr == IntPtr.Zero)
|
||||
{
|
||||
ptr = godot_icall_Object_Ctor(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is called inside godot_icall_Object_Ctor, so we must call it as well in this case.
|
||||
godot_icall_Object_ConnectEventSignals(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
internal Object(bool memoryOwn)
|
||||
@ -101,7 +108,7 @@ namespace Godot
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public SignalAwaiter ToSignal(Object source, string signal)
|
||||
public SignalAwaiter ToSignal(Object source, StringName signal)
|
||||
{
|
||||
return new SignalAwaiter(source, signal, this);
|
||||
}
|
||||
@ -111,20 +118,28 @@ namespace Godot
|
||||
/// </summary>
|
||||
public dynamic DynamicObject => new DynamicGodotObject(this);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static IntPtr godot_icall_Object_Ctor(Object obj);
|
||||
internal static IntPtr __ClassDB_get_method(StringName type, string method)
|
||||
{
|
||||
return godot_icall_Object_ClassDB_get_method(StringName.GetPtr(type), method);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static void godot_icall_Object_Disposed(Object obj, IntPtr ptr);
|
||||
internal static extern IntPtr godot_icall_Object_Ctor(Object obj);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static void godot_icall_Reference_Disposed(Object obj, IntPtr ptr, bool isFinalizer);
|
||||
internal static extern void godot_icall_Object_Disposed(Object obj, IntPtr ptr);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static string godot_icall_Object_ToString(IntPtr ptr);
|
||||
internal static extern void godot_icall_Reference_Disposed(Object obj, IntPtr ptr, bool isFinalizer);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal static extern void godot_icall_Object_ConnectEventSignals(IntPtr obj);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal static extern string godot_icall_Object_ToString(IntPtr ptr);
|
||||
|
||||
// Used by the generated API
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static IntPtr godot_icall_Object_ClassDB_get_method(string type, string method);
|
||||
internal static extern IntPtr godot_icall_Object_ClassDB_get_method(IntPtr type, string method);
|
||||
}
|
||||
}
|
||||
|
@ -9,13 +9,13 @@ namespace Godot
|
||||
private object[] result;
|
||||
private Action action;
|
||||
|
||||
public SignalAwaiter(Object source, string signal, Object target)
|
||||
public SignalAwaiter(Object source, StringName signal, Object target)
|
||||
{
|
||||
godot_icall_SignalAwaiter_connect(Object.GetPtr(source), signal, Object.GetPtr(target), this);
|
||||
godot_icall_SignalAwaiter_connect(Object.GetPtr(source), StringName.GetPtr(signal), Object.GetPtr(target), this);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
internal extern static Error godot_icall_SignalAwaiter_connect(IntPtr source, string signal, IntPtr target, SignalAwaiter awaiter);
|
||||
internal extern static Error godot_icall_SignalAwaiter_connect(IntPtr source, IntPtr signal, IntPtr target, SignalAwaiter awaiter);
|
||||
|
||||
public bool IsCompleted
|
||||
{
|
||||
@ -50,11 +50,5 @@ namespace Godot
|
||||
action();
|
||||
}
|
||||
}
|
||||
|
||||
internal void FailureCallback()
|
||||
{
|
||||
action = null;
|
||||
completed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
17
modules/mono/glue/GodotSharp/GodotSharp/Core/SignalInfo.cs
Normal file
17
modules/mono/glue/GodotSharp/GodotSharp/Core/SignalInfo.cs
Normal file
@ -0,0 +1,17 @@
|
||||
namespace Godot
|
||||
{
|
||||
public struct SignalInfo
|
||||
{
|
||||
private readonly Object _owner;
|
||||
private readonly StringName _signalName;
|
||||
|
||||
public Object Owner => _owner;
|
||||
public StringName Name => _signalName;
|
||||
|
||||
public SignalInfo(Object owner, StringName name)
|
||||
{
|
||||
_owner = owner;
|
||||
_signalName = name;
|
||||
}
|
||||
}
|
||||
}
|
82
modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs
Normal file
82
modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs
Normal file
@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
public sealed partial class StringName : IDisposable
|
||||
{
|
||||
private IntPtr ptr;
|
||||
|
||||
internal static IntPtr GetPtr(StringName instance)
|
||||
{
|
||||
if (instance == null)
|
||||
throw new NullReferenceException($"The instance of type {nameof(StringName)} is null.");
|
||||
|
||||
if (instance.ptr == IntPtr.Zero)
|
||||
throw new ObjectDisposedException(instance.GetType().FullName);
|
||||
|
||||
return instance.ptr;
|
||||
}
|
||||
|
||||
~StringName()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (ptr != IntPtr.Zero)
|
||||
{
|
||||
godot_icall_StringName_Dtor(ptr);
|
||||
ptr = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
internal StringName(IntPtr ptr)
|
||||
{
|
||||
this.ptr = ptr;
|
||||
}
|
||||
|
||||
public StringName()
|
||||
{
|
||||
ptr = IntPtr.Zero;
|
||||
}
|
||||
|
||||
public StringName(string path)
|
||||
{
|
||||
ptr = path == null ? IntPtr.Zero : godot_icall_StringName_Ctor(path);
|
||||
}
|
||||
|
||||
public static implicit operator StringName(string from) => new StringName(from);
|
||||
|
||||
public static implicit operator string(StringName from) => from.ToString();
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return ptr == IntPtr.Zero ? string.Empty : godot_icall_StringName_operator_String(GetPtr(this));
|
||||
}
|
||||
|
||||
public bool IsEmpty()
|
||||
{
|
||||
return ptr == IntPtr.Zero || godot_icall_StringName_is_empty(GetPtr(this));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern IntPtr godot_icall_StringName_Ctor(string path);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern void godot_icall_StringName_Dtor(IntPtr ptr);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern string godot_icall_StringName_operator_String(IntPtr ptr);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern bool godot_icall_StringName_is_empty(IntPtr ptr);
|
||||
}
|
||||
}
|
@ -41,9 +41,11 @@
|
||||
<Compile Include="Core\Attributes\SignalAttribute.cs" />
|
||||
<Compile Include="Core\Attributes\ToolAttribute.cs" />
|
||||
<Compile Include="Core\Basis.cs" />
|
||||
<Compile Include="Core\Callable.cs" />
|
||||
<Compile Include="Core\Color.cs" />
|
||||
<Compile Include="Core\Colors.cs" />
|
||||
<Compile Include="Core\DebuggingUtils.cs" />
|
||||
<Compile Include="Core\DelegateUtils.cs" />
|
||||
<Compile Include="Core\Dictionary.cs" />
|
||||
<Compile Include="Core\Dispatcher.cs" />
|
||||
<Compile Include="Core\DynamicObject.cs" />
|
||||
@ -66,8 +68,10 @@
|
||||
<Compile Include="Core\Quat.cs" />
|
||||
<Compile Include="Core\Rect2.cs" />
|
||||
<Compile Include="Core\RID.cs" />
|
||||
<Compile Include="Core\SignalInfo.cs" />
|
||||
<Compile Include="Core\SignalAwaiter.cs" />
|
||||
<Compile Include="Core\StringExtensions.cs" />
|
||||
<Compile Include="Core\StringName.cs" />
|
||||
<Compile Include="Core\Transform.cs" />
|
||||
<Compile Include="Core\Transform2D.cs" />
|
||||
<Compile Include="Core\Vector2.cs" />
|
||||
|
@ -126,18 +126,25 @@ void godot_icall_Reference_Disposed(MonoObject *p_obj, Object *p_ptr, MonoBoolea
|
||||
}
|
||||
}
|
||||
|
||||
MethodBind *godot_icall_Object_ClassDB_get_method(MonoString *p_type, MonoString *p_method) {
|
||||
StringName type(GDMonoMarshal::mono_string_to_godot(p_type));
|
||||
void godot_icall_Object_ConnectEventSignals(Object *p_ptr) {
|
||||
CSharpInstance *csharp_instance = CAST_CSHARP_INSTANCE(p_ptr->get_script_instance());
|
||||
if (csharp_instance) {
|
||||
csharp_instance->connect_event_signals();
|
||||
}
|
||||
}
|
||||
|
||||
MethodBind *godot_icall_Object_ClassDB_get_method(StringName *p_type, MonoString *p_method) {
|
||||
StringName type = p_type ? *p_type : StringName();
|
||||
StringName method(GDMonoMarshal::mono_string_to_godot(p_method));
|
||||
return ClassDB::get_method(type, method);
|
||||
}
|
||||
|
||||
MonoObject *godot_icall_Object_weakref(Object *p_obj) {
|
||||
if (!p_obj)
|
||||
MonoObject *godot_icall_Object_weakref(Object *p_ptr) {
|
||||
if (!p_ptr)
|
||||
return NULL;
|
||||
|
||||
Ref<WeakRef> wref;
|
||||
Reference *ref = Object::cast_to<Reference>(p_obj);
|
||||
Reference *ref = Object::cast_to<Reference>(p_ptr);
|
||||
|
||||
if (ref) {
|
||||
REF r = ref;
|
||||
@ -148,15 +155,15 @@ MonoObject *godot_icall_Object_weakref(Object *p_obj) {
|
||||
wref->set_ref(r);
|
||||
} else {
|
||||
wref.instance();
|
||||
wref->set_obj(p_obj);
|
||||
wref->set_obj(p_ptr);
|
||||
}
|
||||
|
||||
return GDMonoUtils::unmanaged_get_managed(wref.ptr());
|
||||
}
|
||||
|
||||
Error godot_icall_SignalAwaiter_connect(Object *p_source, MonoString *p_signal, Object *p_target, MonoObject *p_awaiter) {
|
||||
String signal = GDMonoMarshal::mono_string_to_godot(p_signal);
|
||||
return SignalAwaiterUtils::connect_signal_awaiter(p_source, signal, p_target, p_awaiter);
|
||||
Error godot_icall_SignalAwaiter_connect(Object *p_source, StringName *p_signal, Object *p_target, MonoObject *p_awaiter) {
|
||||
StringName signal = p_signal ? *p_signal : StringName();
|
||||
return gd_mono_connect_signal_awaiter(p_source, signal, p_target, p_awaiter);
|
||||
}
|
||||
|
||||
MonoArray *godot_icall_DynamicGodotObject_SetMemberList(Object *p_ptr) {
|
||||
@ -225,8 +232,8 @@ MonoString *godot_icall_Object_ToString(Object *p_ptr) {
|
||||
// Cannot happen in C#; would get an ObjectDisposedException instead.
|
||||
CRASH_COND(p_ptr == NULL);
|
||||
#endif
|
||||
|
||||
String result = p_ptr->to_string();
|
||||
// Can't call 'Object::to_string()' here, as that can end up calling 'ToString' again resulting in an endless circular loop.
|
||||
String result = "[" + p_ptr->get_class() + ":" + itos(p_ptr->get_instance_id()) + "]";
|
||||
return GDMonoMarshal::mono_string_from_godot(result);
|
||||
}
|
||||
|
||||
|
@ -44,11 +44,13 @@ void godot_icall_Object_Disposed(MonoObject *p_obj, Object *p_ptr);
|
||||
|
||||
void godot_icall_Reference_Disposed(MonoObject *p_obj, Object *p_ptr, MonoBoolean p_is_finalizer);
|
||||
|
||||
MethodBind *godot_icall_Object_ClassDB_get_method(MonoString *p_type, MonoString *p_method);
|
||||
void godot_icall_Object_ConnectEventSignals(Object *p_ptr);
|
||||
|
||||
MonoObject *godot_icall_Object_weakref(Object *p_obj);
|
||||
MethodBind *godot_icall_Object_ClassDB_get_method(StringName *p_type, MonoString *p_method);
|
||||
|
||||
Error godot_icall_SignalAwaiter_connect(Object *p_source, MonoString *p_signal, Object *p_target, MonoObject *p_awaiter);
|
||||
MonoObject *godot_icall_Object_weakref(Object *p_ptr);
|
||||
|
||||
Error godot_icall_SignalAwaiter_connect(Object *p_source, StringName *p_signal, Object *p_target, MonoObject *p_awaiter);
|
||||
|
||||
// DynamicGodotObject
|
||||
|
||||
|
@ -241,8 +241,9 @@ MonoObject *godot_icall_GD_str2var(MonoString *p_str) {
|
||||
return GDMonoMarshal::variant_to_mono_object(ret);
|
||||
}
|
||||
|
||||
MonoBoolean godot_icall_GD_type_exists(MonoString *p_type) {
|
||||
return ClassDB::class_exists(GDMonoMarshal::mono_string_to_godot(p_type));
|
||||
MonoBoolean godot_icall_GD_type_exists(StringName *p_type) {
|
||||
StringName type = p_type ? *p_type : StringName();
|
||||
return ClassDB::class_exists(type);
|
||||
}
|
||||
|
||||
void godot_icall_GD_pusherror(MonoString *p_str) {
|
||||
@ -273,6 +274,10 @@ MonoString *godot_icall_GD_var2str(MonoObject *p_var) {
|
||||
return GDMonoMarshal::mono_string_from_godot(vars);
|
||||
}
|
||||
|
||||
uint32_t godot_icall_TypeToVariantType(MonoReflectionType *p_refl_type) {
|
||||
return (uint32_t)GDMonoMarshal::managed_to_variant_type(ManagedType::from_reftype(p_refl_type));
|
||||
}
|
||||
|
||||
MonoObject *godot_icall_DefaultGodotTaskScheduler() {
|
||||
return GDMonoCache::cached_data.task_scheduler_handle->get_target();
|
||||
}
|
||||
@ -300,6 +305,7 @@ void godot_register_gd_icalls() {
|
||||
mono_add_internal_call("Godot.GD::godot_icall_GD_type_exists", (void *)godot_icall_GD_type_exists);
|
||||
mono_add_internal_call("Godot.GD::godot_icall_GD_var2bytes", (void *)godot_icall_GD_var2bytes);
|
||||
mono_add_internal_call("Godot.GD::godot_icall_GD_var2str", (void *)godot_icall_GD_var2str);
|
||||
mono_add_internal_call("Godot.GD::godot_icall_TypeToVariantType", (void *)godot_icall_TypeToVariantType);
|
||||
|
||||
// Dispatcher
|
||||
mono_add_internal_call("Godot.Dispatcher::godot_icall_DefaultGodotTaskScheduler", (void *)godot_icall_DefaultGodotTaskScheduler);
|
||||
|
@ -69,7 +69,7 @@ MonoString *godot_icall_GD_str(MonoArray *p_what);
|
||||
|
||||
MonoObject *godot_icall_GD_str2var(MonoString *p_str);
|
||||
|
||||
MonoBoolean godot_icall_GD_type_exists(MonoString *p_type);
|
||||
MonoBoolean godot_icall_GD_type_exists(StringName *p_type);
|
||||
|
||||
MonoArray *godot_icall_GD_var2bytes(MonoObject *p_var, MonoBoolean p_full_objects);
|
||||
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include "nodepath_glue.h"
|
||||
#include "rid_glue.h"
|
||||
#include "string_glue.h"
|
||||
#include "string_name_glue.h"
|
||||
|
||||
/**
|
||||
* Registers internal calls that were not generated. This function is called
|
||||
@ -44,6 +45,7 @@
|
||||
void godot_register_glue_header_icalls() {
|
||||
godot_register_collections_icalls();
|
||||
godot_register_gd_icalls();
|
||||
godot_register_string_name_icalls();
|
||||
godot_register_nodepath_icalls();
|
||||
godot_register_object_icalls();
|
||||
godot_register_rid_icalls();
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*************************************************************************/
|
||||
/* thread_local.cpp */
|
||||
/* string_name_glue.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
@ -28,80 +28,34 @@
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "thread_local.h"
|
||||
#include "string_name_glue.h"
|
||||
|
||||
#ifdef WINDOWS_ENABLED
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
#ifdef MONO_GLUE_ENABLED
|
||||
|
||||
#include "core/os/memory.h"
|
||||
#include "core/print_string.h"
|
||||
#include "core/ustring.h"
|
||||
|
||||
struct ThreadLocalStorage::Impl {
|
||||
|
||||
#ifdef WINDOWS_ENABLED
|
||||
DWORD dwFlsIndex;
|
||||
#else
|
||||
pthread_key_t key;
|
||||
#endif
|
||||
|
||||
void *get_value() const {
|
||||
#ifdef WINDOWS_ENABLED
|
||||
return FlsGetValue(dwFlsIndex);
|
||||
#else
|
||||
return pthread_getspecific(key);
|
||||
#endif
|
||||
}
|
||||
|
||||
void set_value(void *p_value) const {
|
||||
#ifdef WINDOWS_ENABLED
|
||||
FlsSetValue(dwFlsIndex, p_value);
|
||||
#else
|
||||
pthread_setspecific(key, p_value);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef WINDOWS_ENABLED
|
||||
#define _CALLBACK_FUNC_ __stdcall
|
||||
#else
|
||||
#define _CALLBACK_FUNC_
|
||||
#endif
|
||||
|
||||
Impl(void(_CALLBACK_FUNC_ *p_destr_callback_func)(void *)) {
|
||||
#ifdef WINDOWS_ENABLED
|
||||
dwFlsIndex = FlsAlloc(p_destr_callback_func);
|
||||
ERR_FAIL_COND(dwFlsIndex == FLS_OUT_OF_INDEXES);
|
||||
#else
|
||||
pthread_key_create(&key, p_destr_callback_func);
|
||||
#endif
|
||||
}
|
||||
|
||||
~Impl() {
|
||||
#ifdef WINDOWS_ENABLED
|
||||
FlsFree(dwFlsIndex);
|
||||
#else
|
||||
pthread_key_delete(key);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
void *ThreadLocalStorage::get_value() const {
|
||||
return pimpl->get_value();
|
||||
StringName *godot_icall_StringName_Ctor(MonoString *p_path) {
|
||||
return memnew(StringName(GDMonoMarshal::mono_string_to_godot(p_path)));
|
||||
}
|
||||
|
||||
void ThreadLocalStorage::set_value(void *p_value) const {
|
||||
pimpl->set_value(p_value);
|
||||
void godot_icall_StringName_Dtor(StringName *p_ptr) {
|
||||
ERR_FAIL_NULL(p_ptr);
|
||||
memdelete(p_ptr);
|
||||
}
|
||||
|
||||
void ThreadLocalStorage::alloc(void(_CALLBACK_FUNC_ *p_destr_callback)(void *)) {
|
||||
pimpl = memnew(ThreadLocalStorage::Impl(p_destr_callback));
|
||||
MonoString *godot_icall_StringName_operator_String(StringName *p_np) {
|
||||
return GDMonoMarshal::mono_string_from_godot(p_np->operator String());
|
||||
}
|
||||
|
||||
#undef _CALLBACK_FUNC_
|
||||
|
||||
void ThreadLocalStorage::free() {
|
||||
memdelete(pimpl);
|
||||
pimpl = NULL;
|
||||
MonoBoolean godot_icall_StringName_is_empty(StringName *p_ptr) {
|
||||
return (MonoBoolean)(p_ptr == StringName());
|
||||
}
|
||||
|
||||
void godot_register_string_name_icalls() {
|
||||
mono_add_internal_call("Godot.StringName::godot_icall_StringName_Ctor", (void *)godot_icall_StringName_Ctor);
|
||||
mono_add_internal_call("Godot.StringName::godot_icall_StringName_Dtor", (void *)godot_icall_StringName_Dtor);
|
||||
mono_add_internal_call("Godot.StringName::godot_icall_StringName_operator_String", (void *)godot_icall_StringName_operator_String);
|
||||
mono_add_internal_call("Godot.StringName::godot_icall_StringName_is_empty", (void *)godot_icall_StringName_is_empty);
|
||||
}
|
||||
|
||||
#endif // MONO_GLUE_ENABLED
|
54
modules/mono/glue/string_name_glue.h
Normal file
54
modules/mono/glue/string_name_glue.h
Normal file
@ -0,0 +1,54 @@
|
||||
/*************************************************************************/
|
||||
/* string_name_glue.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 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 STRING_NAME_GLUE_H
|
||||
#define STRING_NAME_GLUE_H
|
||||
|
||||
#ifdef MONO_GLUE_ENABLED
|
||||
|
||||
#include "core/string_name.h"
|
||||
|
||||
#include "../mono_gd/gd_mono_marshal.h"
|
||||
|
||||
StringName *godot_icall_StringName_Ctor(MonoString *p_path);
|
||||
|
||||
void godot_icall_StringName_Dtor(StringName *p_ptr);
|
||||
|
||||
MonoString *godot_icall_StringName_operator_String(StringName *p_np);
|
||||
|
||||
MonoBoolean godot_icall_StringName_is_empty(StringName *p_ptr);
|
||||
|
||||
// Register internal calls
|
||||
|
||||
void godot_register_string_name_icalls();
|
||||
|
||||
#endif // MONO_GLUE_ENABLED
|
||||
|
||||
#endif // STRING_NAME_GLUE_H
|
143
modules/mono/managed_callable.cpp
Normal file
143
modules/mono/managed_callable.cpp
Normal file
@ -0,0 +1,143 @@
|
||||
/*************************************************************************/
|
||||
/* managed_callable.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 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 "managed_callable.h"
|
||||
|
||||
#include "csharp_script.h"
|
||||
#include "mono_gd/gd_mono_marshal.h"
|
||||
#include "mono_gd/gd_mono_utils.h"
|
||||
|
||||
#ifdef GD_MONO_HOT_RELOAD
|
||||
SelfList<ManagedCallable>::List ManagedCallable::instances;
|
||||
Map<ManagedCallable *, Array> ManagedCallable::instances_pending_reload;
|
||||
Mutex ManagedCallable::instances_mutex;
|
||||
#endif
|
||||
|
||||
bool ManagedCallable::compare_equal(const CallableCustom *p_a, const CallableCustom *p_b) {
|
||||
const ManagedCallable *a = static_cast<const ManagedCallable *>(p_a);
|
||||
const ManagedCallable *b = static_cast<const ManagedCallable *>(p_b);
|
||||
|
||||
MonoDelegate *delegate_a = (MonoDelegate *)a->delegate_handle->get_target();
|
||||
MonoDelegate *delegate_b = (MonoDelegate *)b->delegate_handle->get_target();
|
||||
|
||||
if (!delegate_a || !delegate_b) {
|
||||
if (!delegate_a && !delegate_b)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Call Delegate's 'Equals'
|
||||
return GDMonoUtils::mono_delegate_equal(delegate_a, delegate_b);
|
||||
}
|
||||
|
||||
bool ManagedCallable::compare_less(const CallableCustom *p_a, const CallableCustom *p_b) {
|
||||
if (compare_equal(p_a, p_b))
|
||||
return false;
|
||||
return p_a < p_b;
|
||||
}
|
||||
|
||||
uint32_t ManagedCallable::hash() const {
|
||||
// hmm
|
||||
uint32_t hash = delegate_invoke->get_name().hash();
|
||||
return hash_djb2_one_64(delegate_handle.ptr()->handle, hash);
|
||||
}
|
||||
|
||||
String ManagedCallable::get_as_text() const {
|
||||
return "Delegate::Invoke";
|
||||
}
|
||||
|
||||
CallableCustom::CompareEqualFunc ManagedCallable::get_compare_equal_func() const {
|
||||
return compare_equal_func_ptr;
|
||||
}
|
||||
|
||||
CallableCustom::CompareLessFunc ManagedCallable::get_compare_less_func() const {
|
||||
return compare_less_func_ptr;
|
||||
}
|
||||
|
||||
ObjectID ManagedCallable::get_object() const {
|
||||
return CSharpLanguage::get_singleton()->get_managed_callable_middleman()->get_instance_id();
|
||||
}
|
||||
|
||||
void ManagedCallable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
|
||||
r_call_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; // Can't find anything better
|
||||
r_return_value = Variant();
|
||||
|
||||
#ifdef GD_MONO_HOT_RELOAD
|
||||
// Lost during hot-reload
|
||||
ERR_FAIL_NULL(delegate_invoke);
|
||||
ERR_FAIL_COND(delegate_handle.is_null());
|
||||
#endif
|
||||
|
||||
ERR_FAIL_COND(delegate_invoke->get_parameters_count() < p_argcount);
|
||||
|
||||
MonoObject *delegate = delegate_handle->get_target();
|
||||
|
||||
MonoException *exc = NULL;
|
||||
MonoObject *ret = delegate_invoke->invoke(delegate, p_arguments, &exc);
|
||||
|
||||
if (exc) {
|
||||
GDMonoUtils::set_pending_exception(exc);
|
||||
} else {
|
||||
r_return_value = GDMonoMarshal::mono_object_to_variant(ret);
|
||||
r_call_error.error = Callable::CallError::CALL_OK;
|
||||
}
|
||||
}
|
||||
|
||||
void ManagedCallable::set_delegate(MonoDelegate *p_delegate) {
|
||||
delegate_handle = MonoGCHandle::create_strong((MonoObject *)p_delegate);
|
||||
MonoMethod *delegate_invoke_raw = mono_get_delegate_invoke(mono_object_get_class((MonoObject *)p_delegate));
|
||||
const StringName &delegate_invoke_name = CSharpLanguage::get_singleton()->get_string_names().delegate_invoke_method_name;
|
||||
delegate_invoke = memnew(GDMonoMethod(delegate_invoke_name, delegate_invoke_raw)); // TODO: Use pooling for this GDMonoMethod instances
|
||||
}
|
||||
|
||||
ManagedCallable::ManagedCallable(MonoDelegate *p_delegate) {
|
||||
#ifdef DEBUG_ENABLED
|
||||
CRASH_COND(p_delegate == NULL);
|
||||
#endif
|
||||
|
||||
set_delegate(p_delegate);
|
||||
|
||||
#ifdef GD_MONO_HOT_RELOAD
|
||||
{
|
||||
MutexLock lock(instances_mutex);
|
||||
instances.add(&self_instance);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef GD_MONO_HOT_RELOAD
|
||||
ManagedCallable::~ManagedCallable() {
|
||||
{
|
||||
MutexLock lock(instances_mutex);
|
||||
instances.remove(&self_instance);
|
||||
instances_pending_reload.erase(this);
|
||||
}
|
||||
}
|
||||
#endif
|
79
modules/mono/managed_callable.h
Normal file
79
modules/mono/managed_callable.h
Normal file
@ -0,0 +1,79 @@
|
||||
/*************************************************************************/
|
||||
/* managed_callable.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 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 MANAGED_CALLABLE_H
|
||||
#define MANAGED_CALLABLE_H
|
||||
|
||||
#include <mono/metadata/object.h>
|
||||
|
||||
#include "core/callable.h"
|
||||
#include "core/os/mutex.h"
|
||||
#include "core/self_list.h"
|
||||
|
||||
#include "mono_gc_handle.h"
|
||||
#include "mono_gd/gd_mono_method.h"
|
||||
|
||||
class ManagedCallable : public CallableCustom {
|
||||
friend class CSharpLanguage;
|
||||
Ref<MonoGCHandle> delegate_handle;
|
||||
GDMonoMethod *delegate_invoke;
|
||||
|
||||
#ifdef GD_MONO_HOT_RELOAD
|
||||
SelfList<ManagedCallable> self_instance = this;
|
||||
static SelfList<ManagedCallable>::List instances;
|
||||
static Map<ManagedCallable *, Array> instances_pending_reload;
|
||||
static Mutex instances_mutex;
|
||||
#endif
|
||||
|
||||
public:
|
||||
uint32_t hash() const override;
|
||||
String get_as_text() const override;
|
||||
CompareEqualFunc get_compare_equal_func() const override;
|
||||
CompareLessFunc get_compare_less_func() const override;
|
||||
ObjectID get_object() const override;
|
||||
void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override;
|
||||
|
||||
_FORCE_INLINE_ MonoDelegate *get_delegate() { return (MonoDelegate *)delegate_handle->get_target(); }
|
||||
|
||||
void set_delegate(MonoDelegate *p_delegate);
|
||||
|
||||
static bool compare_equal(const CallableCustom *p_a, const CallableCustom *p_b);
|
||||
static bool compare_less(const CallableCustom *p_a, const CallableCustom *p_b);
|
||||
|
||||
static constexpr CompareEqualFunc compare_equal_func_ptr = &ManagedCallable::compare_equal;
|
||||
static constexpr CompareEqualFunc compare_less_func_ptr = &ManagedCallable::compare_less;
|
||||
|
||||
ManagedCallable(MonoDelegate *p_delegate);
|
||||
#ifdef GD_MONO_HOT_RELOAD
|
||||
~ManagedCallable();
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif // MANAGED_CALLABLE_H
|
@ -41,6 +41,8 @@ class MonoGCHandle : public Reference {
|
||||
|
||||
bool released;
|
||||
bool weak;
|
||||
|
||||
friend class ManagedCallable;
|
||||
uint32_t handle;
|
||||
|
||||
public:
|
||||
|
@ -203,7 +203,7 @@ public:
|
||||
|
||||
static GDMono *get_singleton() { return singleton; }
|
||||
|
||||
GD_NORETURN static void unhandled_exception_hook(MonoObject *p_exc, void *p_user_data);
|
||||
[[noreturn]] static void unhandled_exception_hook(MonoObject *p_exc, void *p_user_data);
|
||||
|
||||
UnhandledExceptionPolicy get_unhandled_exception_policy() const { return unhandled_exception_policy; }
|
||||
|
||||
|
@ -148,7 +148,7 @@ MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, void *user_d
|
||||
return res ? res->get_assembly() : NULL;
|
||||
}
|
||||
|
||||
static _THREAD_LOCAL_(MonoImage *) image_corlib_loading = NULL;
|
||||
static thread_local MonoImage *image_corlib_loading = NULL;
|
||||
|
||||
MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **, void *user_data, bool refonly) {
|
||||
|
||||
|
@ -112,6 +112,7 @@ void CachedData::clear_godot_api_cache() {
|
||||
class_AABB = NULL;
|
||||
class_Color = NULL;
|
||||
class_Plane = NULL;
|
||||
class_StringName = NULL;
|
||||
class_NodePath = NULL;
|
||||
class_RID = NULL;
|
||||
class_GodotObject = NULL;
|
||||
@ -120,6 +121,8 @@ void CachedData::clear_godot_api_cache() {
|
||||
class_Control = NULL;
|
||||
class_Spatial = NULL;
|
||||
class_WeakRef = NULL;
|
||||
class_Callable = NULL;
|
||||
class_SignalInfo = NULL;
|
||||
class_Array = NULL;
|
||||
class_Dictionary = NULL;
|
||||
class_MarshalUtils = NULL;
|
||||
@ -145,6 +148,7 @@ void CachedData::clear_godot_api_cache() {
|
||||
field_GodotMethodAttribute_methodName = NULL;
|
||||
|
||||
field_GodotObject_ptr = NULL;
|
||||
field_StringName_ptr = NULL;
|
||||
field_NodePath_ptr = NULL;
|
||||
field_Image_ptr = NULL;
|
||||
field_RID_ptr = NULL;
|
||||
@ -153,9 +157,13 @@ void CachedData::clear_godot_api_cache() {
|
||||
methodthunk_Array_GetPtr.nullify();
|
||||
methodthunk_Dictionary_GetPtr.nullify();
|
||||
methodthunk_SignalAwaiter_SignalCallback.nullify();
|
||||
methodthunk_SignalAwaiter_FailureCallback.nullify();
|
||||
methodthunk_GodotTaskScheduler_Activate.nullify();
|
||||
|
||||
methodthunk_Delegate_Equals.nullify();
|
||||
|
||||
methodthunk_DelegateUtils_TrySerializeDelegate.nullify();
|
||||
methodthunk_DelegateUtils_TryDeserializeDelegate.nullify();
|
||||
|
||||
// Start of MarshalUtils methods
|
||||
|
||||
methodthunk_MarshalUtils_TypeIsGenericArray.nullify();
|
||||
@ -211,6 +219,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_METHOD_THUNK_AND_CHECK(Delegate, Equals, GDMono::get_singleton()->get_corlib_assembly()->get_class("System", "Delegate")->get_method_with_desc("System.Delegate:Equals(object)", 1));
|
||||
|
||||
CACHE_CLASS_AND_CHECK(KeyNotFoundException, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections.Generic", "KeyNotFoundException"));
|
||||
|
||||
cached_data.corlib_cache_updated = true;
|
||||
@ -228,6 +238,7 @@ void update_godot_api_cache() {
|
||||
CACHE_CLASS_AND_CHECK(AABB, GODOT_API_CLASS(AABB));
|
||||
CACHE_CLASS_AND_CHECK(Color, GODOT_API_CLASS(Color));
|
||||
CACHE_CLASS_AND_CHECK(Plane, GODOT_API_CLASS(Plane));
|
||||
CACHE_CLASS_AND_CHECK(StringName, GODOT_API_CLASS(StringName));
|
||||
CACHE_CLASS_AND_CHECK(NodePath, GODOT_API_CLASS(NodePath));
|
||||
CACHE_CLASS_AND_CHECK(RID, GODOT_API_CLASS(RID));
|
||||
CACHE_CLASS_AND_CHECK(GodotObject, GODOT_API_CLASS(Object));
|
||||
@ -236,6 +247,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(Callable, GODOT_API_CLASS(Callable));
|
||||
CACHE_CLASS_AND_CHECK(SignalInfo, GODOT_API_CLASS(SignalInfo));
|
||||
CACHE_CLASS_AND_CHECK(Array, GODOT_API_NS_CLASS(BINDINGS_NAMESPACE_COLLECTIONS, Array));
|
||||
CACHE_CLASS_AND_CHECK(Dictionary, GODOT_API_NS_CLASS(BINDINGS_NAMESPACE_COLLECTIONS, Dictionary));
|
||||
CACHE_CLASS_AND_CHECK(MarshalUtils, GODOT_API_CLASS(MarshalUtils));
|
||||
@ -261,6 +274,7 @@ void update_godot_api_cache() {
|
||||
CACHE_FIELD_AND_CHECK(GodotMethodAttribute, methodName, CACHED_CLASS(GodotMethodAttribute)->get_field("methodName"));
|
||||
|
||||
CACHE_FIELD_AND_CHECK(GodotObject, ptr, CACHED_CLASS(GodotObject)->get_field(BINDINGS_PTR_FIELD));
|
||||
CACHE_FIELD_AND_CHECK(StringName, ptr, CACHED_CLASS(StringName)->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));
|
||||
|
||||
@ -268,9 +282,11 @@ void update_godot_api_cache() {
|
||||
CACHE_METHOD_THUNK_AND_CHECK(Array, GetPtr, GODOT_API_NS_CLASS(BINDINGS_NAMESPACE_COLLECTIONS, Array)->get_method("GetPtr", 0));
|
||||
CACHE_METHOD_THUNK_AND_CHECK(Dictionary, GetPtr, GODOT_API_NS_CLASS(BINDINGS_NAMESPACE_COLLECTIONS, Dictionary)->get_method("GetPtr", 0));
|
||||
CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, SignalCallback, GODOT_API_CLASS(SignalAwaiter)->get_method("SignalCallback", 1));
|
||||
CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, FailureCallback, GODOT_API_CLASS(SignalAwaiter)->get_method("FailureCallback", 0));
|
||||
CACHE_METHOD_THUNK_AND_CHECK(GodotTaskScheduler, Activate, GODOT_API_CLASS(GodotTaskScheduler)->get_method("Activate", 0));
|
||||
|
||||
CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, TrySerializeDelegate, GODOT_API_CLASS(DelegateUtils)->get_method("TrySerializeDelegate", 2));
|
||||
CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, TryDeserializeDelegate, GODOT_API_CLASS(DelegateUtils)->get_method("TryDeserializeDelegate", 2));
|
||||
|
||||
// Start of MarshalUtils methods
|
||||
|
||||
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericArray, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericArray", 1));
|
||||
|
@ -82,6 +82,7 @@ struct CachedData {
|
||||
GDMonoClass *class_AABB;
|
||||
GDMonoClass *class_Color;
|
||||
GDMonoClass *class_Plane;
|
||||
GDMonoClass *class_StringName;
|
||||
GDMonoClass *class_NodePath;
|
||||
GDMonoClass *class_RID;
|
||||
GDMonoClass *class_GodotObject;
|
||||
@ -90,6 +91,8 @@ struct CachedData {
|
||||
GDMonoClass *class_Control;
|
||||
GDMonoClass *class_Spatial;
|
||||
GDMonoClass *class_WeakRef;
|
||||
GDMonoClass *class_Callable;
|
||||
GDMonoClass *class_SignalInfo;
|
||||
GDMonoClass *class_Array;
|
||||
GDMonoClass *class_Dictionary;
|
||||
GDMonoClass *class_MarshalUtils;
|
||||
@ -115,6 +118,7 @@ struct CachedData {
|
||||
GDMonoField *field_GodotMethodAttribute_methodName;
|
||||
|
||||
GDMonoField *field_GodotObject_ptr;
|
||||
GDMonoField *field_StringName_ptr;
|
||||
GDMonoField *field_NodePath_ptr;
|
||||
GDMonoField *field_Image_ptr;
|
||||
GDMonoField *field_RID_ptr;
|
||||
@ -123,9 +127,13 @@ struct CachedData {
|
||||
GDMonoMethodThunkR<Array *, MonoObject *> methodthunk_Array_GetPtr;
|
||||
GDMonoMethodThunkR<Dictionary *, MonoObject *> methodthunk_Dictionary_GetPtr;
|
||||
GDMonoMethodThunk<MonoObject *, MonoArray *> methodthunk_SignalAwaiter_SignalCallback;
|
||||
GDMonoMethodThunk<MonoObject *> methodthunk_SignalAwaiter_FailureCallback;
|
||||
GDMonoMethodThunk<MonoObject *> methodthunk_GodotTaskScheduler_Activate;
|
||||
|
||||
GDMonoMethodThunkR<MonoBoolean, MonoObject *, MonoObject *> methodthunk_Delegate_Equals;
|
||||
|
||||
GDMonoMethodThunkR<MonoBoolean, MonoDelegate *, MonoObject *> methodthunk_DelegateUtils_TrySerializeDelegate;
|
||||
GDMonoMethodThunkR<MonoBoolean, MonoObject *, MonoDelegate **> methodthunk_DelegateUtils_TryDeserializeDelegate;
|
||||
|
||||
// Start of MarshalUtils methods
|
||||
|
||||
GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsGenericArray;
|
||||
@ -193,10 +201,4 @@ _FORCE_INLINE_ bool tools_godot_api_check() {
|
||||
#define CACHED_METHOD_THUNK(m_class, m_method) (GDMonoCache::cached_data.methodthunk_##m_class##_##m_method)
|
||||
#define CACHED_PROPERTY(m_class, m_property) (GDMonoCache::cached_data.property_##m_class##_##m_property)
|
||||
|
||||
#ifdef REAL_T_IS_DOUBLE
|
||||
#define REAL_T_MONOCLASS CACHED_CLASS_RAW(double)
|
||||
#else
|
||||
#define REAL_T_MONOCLASS CACHED_CLASS_RAW(float)
|
||||
#endif
|
||||
|
||||
#endif // GD_MONO_CACHE_H
|
||||
|
@ -37,6 +37,10 @@
|
||||
#include "gd_mono_marshal.h"
|
||||
#include "gd_mono_utils.h"
|
||||
|
||||
void GDMonoField::set_value(MonoObject *p_object, MonoObject *p_value) {
|
||||
mono_field_set_value(p_object, mono_field, p_value);
|
||||
}
|
||||
|
||||
void GDMonoField::set_value_raw(MonoObject *p_object, void *p_ptr) {
|
||||
mono_field_set_value(p_object, mono_field, &p_ptr);
|
||||
}
|
||||
@ -173,6 +177,18 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
|
||||
break;
|
||||
}
|
||||
|
||||
if (tclass == CACHED_CLASS(Callable)) {
|
||||
GDMonoMarshal::M_Callable val = GDMonoMarshal::callable_to_managed(p_value.operator Callable());
|
||||
mono_field_set_value(p_object, mono_field, &val);
|
||||
break;
|
||||
}
|
||||
|
||||
if (tclass == CACHED_CLASS(SignalInfo)) {
|
||||
GDMonoMarshal::M_SignalInfo val = GDMonoMarshal::signal_info_to_managed(p_value.operator Signal());
|
||||
mono_field_set_value(p_object, mono_field, &val);
|
||||
break;
|
||||
}
|
||||
|
||||
if (mono_class_is_enum(tclass->get_mono_ptr())) {
|
||||
MonoType *enum_basetype = mono_class_enum_basetype(tclass->get_mono_ptr());
|
||||
switch (mono_type_get_type(enum_basetype)) {
|
||||
@ -256,11 +272,21 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
|
||||
break;
|
||||
}
|
||||
|
||||
if (array_type->eklass == REAL_T_MONOCLASS) {
|
||||
if (array_type->eklass == CACHED_CLASS_RAW(int64_t)) {
|
||||
SET_FROM_ARRAY(PackedInt64Array);
|
||||
break;
|
||||
}
|
||||
|
||||
if (array_type->eklass == CACHED_CLASS_RAW(float)) {
|
||||
SET_FROM_ARRAY(PackedFloat32Array);
|
||||
break;
|
||||
}
|
||||
|
||||
if (array_type->eklass == CACHED_CLASS_RAW(double)) {
|
||||
SET_FROM_ARRAY(PackedFloat64Array);
|
||||
break;
|
||||
}
|
||||
|
||||
if (array_type->eklass == CACHED_CLASS_RAW(String)) {
|
||||
SET_FROM_ARRAY(PackedStringArray);
|
||||
break;
|
||||
@ -294,6 +320,12 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
|
||||
break;
|
||||
}
|
||||
|
||||
if (CACHED_CLASS(StringName) == type_class) {
|
||||
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator StringName());
|
||||
mono_field_set_value(p_object, mono_field, managed);
|
||||
break;
|
||||
}
|
||||
|
||||
if (CACHED_CLASS(NodePath) == type_class) {
|
||||
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator NodePath());
|
||||
mono_field_set_value(p_object, mono_field, managed);
|
||||
@ -413,6 +445,10 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
|
||||
case Variant::COLOR: {
|
||||
SET_FROM_STRUCT(Color);
|
||||
} break;
|
||||
case Variant::STRING_NAME: {
|
||||
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator StringName());
|
||||
mono_field_set_value(p_object, mono_field, managed);
|
||||
} break;
|
||||
case Variant::NODE_PATH: {
|
||||
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator NodePath());
|
||||
mono_field_set_value(p_object, mono_field, managed);
|
||||
@ -424,8 +460,15 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
|
||||
case Variant::OBJECT: {
|
||||
MonoObject *managed = GDMonoUtils::unmanaged_get_managed(p_value.operator Object *());
|
||||
mono_field_set_value(p_object, mono_field, managed);
|
||||
break;
|
||||
}
|
||||
} break;
|
||||
case Variant::CALLABLE: {
|
||||
GDMonoMarshal::M_Callable val = GDMonoMarshal::callable_to_managed(p_value.operator Callable());
|
||||
mono_field_set_value(p_object, mono_field, &val);
|
||||
} break;
|
||||
case Variant::SIGNAL: {
|
||||
GDMonoMarshal::M_SignalInfo val = GDMonoMarshal::signal_info_to_managed(p_value.operator Signal());
|
||||
mono_field_set_value(p_object, mono_field, &val);
|
||||
} break;
|
||||
case Variant::DICTIONARY: {
|
||||
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), CACHED_CLASS(Dictionary));
|
||||
mono_field_set_value(p_object, mono_field, managed);
|
||||
@ -440,9 +483,15 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
|
||||
case Variant::PACKED_INT32_ARRAY: {
|
||||
SET_FROM_ARRAY(PackedInt32Array);
|
||||
} break;
|
||||
case Variant::PACKED_INT64_ARRAY: {
|
||||
SET_FROM_ARRAY(PackedInt64Array);
|
||||
} break;
|
||||
case Variant::PACKED_FLOAT32_ARRAY: {
|
||||
SET_FROM_ARRAY(PackedFloat32Array);
|
||||
} break;
|
||||
case Variant::PACKED_FLOAT64_ARRAY: {
|
||||
SET_FROM_ARRAY(PackedFloat64Array);
|
||||
} break;
|
||||
case Variant::PACKED_STRING_ARRAY: {
|
||||
SET_FROM_ARRAY(PackedStringArray);
|
||||
} break;
|
||||
|
@ -47,21 +47,22 @@ class GDMonoField : public IMonoClassMember {
|
||||
MonoCustomAttrInfo *attributes;
|
||||
|
||||
public:
|
||||
virtual GDMonoClass *get_enclosing_class() const GD_FINAL { return owner; }
|
||||
virtual GDMonoClass *get_enclosing_class() const final { return owner; }
|
||||
|
||||
virtual MemberType get_member_type() const GD_FINAL { return MEMBER_TYPE_FIELD; }
|
||||
virtual MemberType get_member_type() const final { return MEMBER_TYPE_FIELD; }
|
||||
|
||||
virtual StringName get_name() const GD_FINAL { return name; }
|
||||
virtual StringName get_name() const final { return name; }
|
||||
|
||||
virtual bool is_static() GD_FINAL;
|
||||
virtual Visibility get_visibility() GD_FINAL;
|
||||
virtual bool is_static() final;
|
||||
virtual Visibility get_visibility() final;
|
||||
|
||||
virtual bool has_attribute(GDMonoClass *p_attr_class) GD_FINAL;
|
||||
virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) GD_FINAL;
|
||||
virtual bool has_attribute(GDMonoClass *p_attr_class) final;
|
||||
virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) final;
|
||||
void fetch_attributes();
|
||||
|
||||
_FORCE_INLINE_ ManagedType get_type() const { return type; }
|
||||
|
||||
void set_value(MonoObject *p_object, MonoObject *p_value);
|
||||
void set_value_raw(MonoObject *p_object, void *p_ptr);
|
||||
void set_value_from_variant(MonoObject *p_object, const Variant &p_value);
|
||||
|
||||
|
@ -33,7 +33,6 @@
|
||||
#include "../csharp_script.h"
|
||||
#include "../mono_gc_handle.h"
|
||||
#include "../utils/macros.h"
|
||||
#include "../utils/thread_local.h"
|
||||
#include "gd_mono_class.h"
|
||||
#include "gd_mono_marshal.h"
|
||||
#include "gd_mono_utils.h"
|
||||
@ -108,9 +107,11 @@ void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) {
|
||||
|
||||
CRASH_COND(script.is_null());
|
||||
|
||||
ScriptInstance *si = CSharpInstance::create_for_managed_type(unmanaged, script.ptr(), gchandle);
|
||||
CSharpInstance *csharp_instance = CSharpInstance::create_for_managed_type(unmanaged, script.ptr(), gchandle);
|
||||
|
||||
unmanaged->set_script_and_instance(script, si);
|
||||
unmanaged->set_script_and_instance(script, csharp_instance);
|
||||
|
||||
csharp_instance->connect_event_signals();
|
||||
}
|
||||
|
||||
void unhandled_exception(MonoException *p_exc) {
|
||||
|
@ -30,13 +30,14 @@
|
||||
|
||||
#include "gd_mono_marshal.h"
|
||||
|
||||
#include "../signal_awaiter_utils.h"
|
||||
#include "gd_mono.h"
|
||||
#include "gd_mono_cache.h"
|
||||
#include "gd_mono_class.h"
|
||||
|
||||
namespace GDMonoMarshal {
|
||||
|
||||
Variant::Type managed_to_variant_type(const ManagedType &p_type) {
|
||||
Variant::Type managed_to_variant_type(const ManagedType &p_type, bool *r_nil_is_variant) {
|
||||
switch (p_type.type_encoding) {
|
||||
case MONO_TYPE_BOOLEAN:
|
||||
return Variant::BOOL;
|
||||
@ -101,6 +102,12 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) {
|
||||
if (vtclass == CACHED_CLASS(Plane))
|
||||
return Variant::PLANE;
|
||||
|
||||
if (vtclass == CACHED_CLASS(Callable))
|
||||
return Variant::CALLABLE;
|
||||
|
||||
if (vtclass == CACHED_CLASS(SignalInfo))
|
||||
return Variant::SIGNAL;
|
||||
|
||||
if (mono_class_is_enum(vtclass->get_mono_ptr()))
|
||||
return Variant::INT;
|
||||
} break;
|
||||
@ -118,9 +125,15 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) {
|
||||
if (array_type->eklass == CACHED_CLASS_RAW(int32_t))
|
||||
return Variant::PACKED_INT32_ARRAY;
|
||||
|
||||
if (array_type->eklass == REAL_T_MONOCLASS)
|
||||
if (array_type->eklass == CACHED_CLASS_RAW(int64_t))
|
||||
return Variant::PACKED_INT64_ARRAY;
|
||||
|
||||
if (array_type->eklass == CACHED_CLASS_RAW(float))
|
||||
return Variant::PACKED_FLOAT32_ARRAY;
|
||||
|
||||
if (array_type->eklass == CACHED_CLASS_RAW(double))
|
||||
return Variant::PACKED_FLOAT64_ARRAY;
|
||||
|
||||
if (array_type->eklass == CACHED_CLASS_RAW(String))
|
||||
return Variant::PACKED_STRING_ARRAY;
|
||||
|
||||
@ -142,6 +155,10 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) {
|
||||
return Variant::OBJECT;
|
||||
}
|
||||
|
||||
if (CACHED_CLASS(StringName) == type_class) {
|
||||
return Variant::STRING_NAME;
|
||||
}
|
||||
|
||||
if (CACHED_CLASS(NodePath) == type_class) {
|
||||
return Variant::NODE_PATH;
|
||||
}
|
||||
@ -179,6 +196,12 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) {
|
||||
}
|
||||
} break;
|
||||
|
||||
case MONO_TYPE_OBJECT: {
|
||||
if (r_nil_is_variant)
|
||||
*r_nil_is_variant = true;
|
||||
return Variant::NIL;
|
||||
} break;
|
||||
|
||||
case MONO_TYPE_GENERICINST: {
|
||||
MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type());
|
||||
|
||||
@ -211,6 +234,9 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) {
|
||||
} break;
|
||||
}
|
||||
|
||||
if (r_nil_is_variant)
|
||||
*r_nil_is_variant = false;
|
||||
|
||||
// Unknown
|
||||
return Variant::NIL;
|
||||
}
|
||||
@ -432,6 +458,16 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
|
||||
return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Plane), &from);
|
||||
}
|
||||
|
||||
if (vtclass == CACHED_CLASS(Callable)) {
|
||||
GDMonoMarshal::M_Callable from = GDMonoMarshal::callable_to_managed(p_var->operator Callable());
|
||||
return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Callable), &from);
|
||||
}
|
||||
|
||||
if (vtclass == CACHED_CLASS(SignalInfo)) {
|
||||
GDMonoMarshal::M_SignalInfo from = GDMonoMarshal::signal_info_to_managed(p_var->operator Signal());
|
||||
return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(SignalInfo), &from);
|
||||
}
|
||||
|
||||
if (mono_class_is_enum(vtclass->get_mono_ptr())) {
|
||||
MonoType *enum_basetype = mono_class_enum_basetype(vtclass->get_mono_ptr());
|
||||
MonoClass *enum_baseclass = mono_class_from_mono_type(enum_basetype);
|
||||
@ -496,9 +532,15 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
|
||||
if (array_type->eklass == CACHED_CLASS_RAW(int32_t))
|
||||
return (MonoObject *)PackedInt32Array_to_mono_array(p_var->operator PackedInt32Array());
|
||||
|
||||
if (array_type->eklass == REAL_T_MONOCLASS)
|
||||
if (array_type->eklass == CACHED_CLASS_RAW(int64_t))
|
||||
return (MonoObject *)PackedInt64Array_to_mono_array(p_var->operator PackedInt64Array());
|
||||
|
||||
if (array_type->eklass == CACHED_CLASS_RAW(float))
|
||||
return (MonoObject *)PackedFloat32Array_to_mono_array(p_var->operator PackedFloat32Array());
|
||||
|
||||
if (array_type->eklass == CACHED_CLASS_RAW(double))
|
||||
return (MonoObject *)PackedFloat64Array_to_mono_array(p_var->operator PackedFloat64Array());
|
||||
|
||||
if (array_type->eklass == CACHED_CLASS_RAW(String))
|
||||
return (MonoObject *)PackedStringArray_to_mono_array(p_var->operator PackedStringArray());
|
||||
|
||||
@ -522,6 +564,10 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
|
||||
return GDMonoUtils::unmanaged_get_managed(p_var->operator Object *());
|
||||
}
|
||||
|
||||
if (CACHED_CLASS(StringName) == type_class) {
|
||||
return GDMonoUtils::create_managed_from(p_var->operator StringName());
|
||||
}
|
||||
|
||||
if (CACHED_CLASS(NodePath) == type_class) {
|
||||
return GDMonoUtils::create_managed_from(p_var->operator NodePath());
|
||||
}
|
||||
@ -628,12 +674,22 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
|
||||
GDMonoMarshal::M_Color from = MARSHALLED_OUT(Color, p_var->operator ::Color());
|
||||
return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Color), &from);
|
||||
}
|
||||
case Variant::STRING_NAME:
|
||||
return GDMonoUtils::create_managed_from(p_var->operator StringName());
|
||||
case Variant::NODE_PATH:
|
||||
return GDMonoUtils::create_managed_from(p_var->operator NodePath());
|
||||
case Variant::_RID:
|
||||
return GDMonoUtils::create_managed_from(p_var->operator RID());
|
||||
case Variant::OBJECT:
|
||||
return GDMonoUtils::unmanaged_get_managed(p_var->operator Object *());
|
||||
case Variant::CALLABLE: {
|
||||
GDMonoMarshal::M_Callable from = GDMonoMarshal::callable_to_managed(p_var->operator Callable());
|
||||
return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Callable), &from);
|
||||
}
|
||||
case Variant::SIGNAL: {
|
||||
GDMonoMarshal::M_SignalInfo from = GDMonoMarshal::signal_info_to_managed(p_var->operator Signal());
|
||||
return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(SignalInfo), &from);
|
||||
}
|
||||
case Variant::DICTIONARY:
|
||||
return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary));
|
||||
case Variant::ARRAY:
|
||||
@ -642,8 +698,12 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
|
||||
return (MonoObject *)PackedByteArray_to_mono_array(p_var->operator PackedByteArray());
|
||||
case Variant::PACKED_INT32_ARRAY:
|
||||
return (MonoObject *)PackedInt32Array_to_mono_array(p_var->operator PackedInt32Array());
|
||||
case Variant::PACKED_INT64_ARRAY:
|
||||
return (MonoObject *)PackedInt64Array_to_mono_array(p_var->operator PackedInt64Array());
|
||||
case Variant::PACKED_FLOAT32_ARRAY:
|
||||
return (MonoObject *)PackedFloat32Array_to_mono_array(p_var->operator PackedFloat32Array());
|
||||
case Variant::PACKED_FLOAT64_ARRAY:
|
||||
return (MonoObject *)PackedFloat64Array_to_mono_array(p_var->operator PackedFloat64Array());
|
||||
case Variant::PACKED_STRING_ARRAY:
|
||||
return (MonoObject *)PackedStringArray_to_mono_array(p_var->operator PackedStringArray());
|
||||
case Variant::PACKED_VECTOR2_ARRAY:
|
||||
@ -744,34 +804,40 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type
|
||||
GDMonoClass *vtclass = p_type.type_class;
|
||||
|
||||
if (vtclass == CACHED_CLASS(Vector2))
|
||||
return MARSHALLED_IN(Vector2, (GDMonoMarshal::M_Vector2 *)mono_object_unbox(p_obj));
|
||||
return MARSHALLED_IN(Vector2, unbox_addr<GDMonoMarshal::M_Vector2>(p_obj));
|
||||
|
||||
if (vtclass == CACHED_CLASS(Rect2))
|
||||
return MARSHALLED_IN(Rect2, (GDMonoMarshal::M_Rect2 *)mono_object_unbox(p_obj));
|
||||
return MARSHALLED_IN(Rect2, unbox_addr<GDMonoMarshal::M_Rect2>(p_obj));
|
||||
|
||||
if (vtclass == CACHED_CLASS(Transform2D))
|
||||
return MARSHALLED_IN(Transform2D, (GDMonoMarshal::M_Transform2D *)mono_object_unbox(p_obj));
|
||||
return MARSHALLED_IN(Transform2D, unbox_addr<GDMonoMarshal::M_Transform2D>(p_obj));
|
||||
|
||||
if (vtclass == CACHED_CLASS(Vector3))
|
||||
return MARSHALLED_IN(Vector3, (GDMonoMarshal::M_Vector3 *)mono_object_unbox(p_obj));
|
||||
return MARSHALLED_IN(Vector3, unbox_addr<GDMonoMarshal::M_Vector3>(p_obj));
|
||||
|
||||
if (vtclass == CACHED_CLASS(Basis))
|
||||
return MARSHALLED_IN(Basis, (GDMonoMarshal::M_Basis *)mono_object_unbox(p_obj));
|
||||
return MARSHALLED_IN(Basis, unbox_addr<GDMonoMarshal::M_Basis>(p_obj));
|
||||
|
||||
if (vtclass == CACHED_CLASS(Quat))
|
||||
return MARSHALLED_IN(Quat, (GDMonoMarshal::M_Quat *)mono_object_unbox(p_obj));
|
||||
return MARSHALLED_IN(Quat, unbox_addr<GDMonoMarshal::M_Quat>(p_obj));
|
||||
|
||||
if (vtclass == CACHED_CLASS(Transform))
|
||||
return MARSHALLED_IN(Transform, (GDMonoMarshal::M_Transform *)mono_object_unbox(p_obj));
|
||||
return MARSHALLED_IN(Transform, unbox_addr<GDMonoMarshal::M_Transform>(p_obj));
|
||||
|
||||
if (vtclass == CACHED_CLASS(AABB))
|
||||
return MARSHALLED_IN(AABB, (GDMonoMarshal::M_AABB *)mono_object_unbox(p_obj));
|
||||
return MARSHALLED_IN(AABB, unbox_addr<GDMonoMarshal::M_AABB>(p_obj));
|
||||
|
||||
if (vtclass == CACHED_CLASS(Color))
|
||||
return MARSHALLED_IN(Color, (GDMonoMarshal::M_Color *)mono_object_unbox(p_obj));
|
||||
return MARSHALLED_IN(Color, unbox_addr<GDMonoMarshal::M_Color>(p_obj));
|
||||
|
||||
if (vtclass == CACHED_CLASS(Plane))
|
||||
return MARSHALLED_IN(Plane, (GDMonoMarshal::M_Plane *)mono_object_unbox(p_obj));
|
||||
return MARSHALLED_IN(Plane, unbox_addr<GDMonoMarshal::M_Plane>(p_obj));
|
||||
|
||||
if (vtclass == CACHED_CLASS(Callable))
|
||||
return managed_to_callable(unbox<GDMonoMarshal::M_Callable>(p_obj));
|
||||
|
||||
if (vtclass == CACHED_CLASS(SignalInfo))
|
||||
return managed_to_signal_info(unbox<GDMonoMarshal::M_SignalInfo>(p_obj));
|
||||
|
||||
if (mono_class_is_enum(vtclass->get_mono_ptr()))
|
||||
return unbox<int32_t>(p_obj);
|
||||
@ -790,9 +856,15 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type
|
||||
if (array_type->eklass == CACHED_CLASS_RAW(int32_t))
|
||||
return mono_array_to_PackedInt32Array((MonoArray *)p_obj);
|
||||
|
||||
if (array_type->eklass == REAL_T_MONOCLASS)
|
||||
if (array_type->eklass == CACHED_CLASS_RAW(int64_t))
|
||||
return mono_array_to_PackedInt64Array((MonoArray *)p_obj);
|
||||
|
||||
if (array_type->eklass == CACHED_CLASS_RAW(float))
|
||||
return mono_array_to_PackedFloat32Array((MonoArray *)p_obj);
|
||||
|
||||
if (array_type->eklass == CACHED_CLASS_RAW(double))
|
||||
return mono_array_to_PackedFloat64Array((MonoArray *)p_obj);
|
||||
|
||||
if (array_type->eklass == CACHED_CLASS_RAW(String))
|
||||
return mono_array_to_PackedStringArray((MonoArray *)p_obj);
|
||||
|
||||
@ -825,6 +897,11 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type
|
||||
return Variant();
|
||||
}
|
||||
|
||||
if (CACHED_CLASS(StringName) == type_class) {
|
||||
StringName *ptr = unbox<StringName *>(CACHED_FIELD(StringName, ptr)->get_value(p_obj));
|
||||
return ptr ? Variant(*ptr) : Variant();
|
||||
}
|
||||
|
||||
if (CACHED_CLASS(NodePath) == type_class) {
|
||||
NodePath *ptr = unbox<NodePath *>(CACHED_FIELD(NodePath, ptr)->get_value(p_obj));
|
||||
return ptr ? Variant(*ptr) : Variant();
|
||||
@ -960,9 +1037,10 @@ String mono_object_to_variant_string(MonoObject *p_obj, MonoException **r_exc) {
|
||||
}
|
||||
|
||||
MonoArray *Array_to_mono_array(const Array &p_array) {
|
||||
MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), p_array.size());
|
||||
int length = p_array.size();
|
||||
MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), length);
|
||||
|
||||
for (int i = 0; i < p_array.size(); i++) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
MonoObject *boxed = variant_to_mono_object(p_array[i]);
|
||||
mono_array_setref(ret, i, boxed);
|
||||
}
|
||||
@ -985,16 +1063,14 @@ Array mono_array_to_Array(MonoArray *p_array) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// TODO: Use memcpy where possible
|
||||
|
||||
MonoArray *PackedInt32Array_to_mono_array(const PackedInt32Array &p_array) {
|
||||
const int *r = p_array.ptr();
|
||||
const int32_t *src = p_array.ptr();
|
||||
int length = p_array.size();
|
||||
|
||||
MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(int32_t), p_array.size());
|
||||
MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(int32_t), length);
|
||||
|
||||
for (int i = 0; i < p_array.size(); i++) {
|
||||
mono_array_set(ret, int32_t, i, r[i]);
|
||||
}
|
||||
int32_t *dst = (int32_t *)mono_array_addr(ret, int32_t, 0);
|
||||
memcpy(dst, src, length);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -1005,23 +1081,48 @@ PackedInt32Array mono_array_to_PackedInt32Array(MonoArray *p_array) {
|
||||
return ret;
|
||||
int length = mono_array_length(p_array);
|
||||
ret.resize(length);
|
||||
int *w = ret.ptrw();
|
||||
int32_t *dst = ret.ptrw();
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
w[i] = mono_array_get(p_array, int32_t, i);
|
||||
}
|
||||
const int32_t *src = (const int32_t *)mono_array_addr(p_array, int32_t, 0);
|
||||
memcpy(dst, src, length);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
MonoArray *PackedInt64Array_to_mono_array(const PackedInt64Array &p_array) {
|
||||
const int64_t *src = p_array.ptr();
|
||||
int length = p_array.size();
|
||||
|
||||
MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(int64_t), length);
|
||||
|
||||
int64_t *dst = (int64_t *)mono_array_addr(ret, int64_t, 0);
|
||||
memcpy(dst, src, length);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
PackedInt64Array mono_array_to_PackedInt64Array(MonoArray *p_array) {
|
||||
PackedInt64Array ret;
|
||||
if (!p_array)
|
||||
return ret;
|
||||
int length = mono_array_length(p_array);
|
||||
ret.resize(length);
|
||||
int64_t *dst = ret.ptrw();
|
||||
|
||||
const int64_t *src = (const int64_t *)mono_array_addr(p_array, int64_t, 0);
|
||||
memcpy(dst, src, length);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
MonoArray *PackedByteArray_to_mono_array(const PackedByteArray &p_array) {
|
||||
const uint8_t *r = p_array.ptr();
|
||||
const uint8_t *src = p_array.ptr();
|
||||
int length = p_array.size();
|
||||
|
||||
MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(uint8_t), p_array.size());
|
||||
MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(uint8_t), length);
|
||||
|
||||
for (int i = 0; i < p_array.size(); i++) {
|
||||
mono_array_set(ret, uint8_t, i, r[i]);
|
||||
}
|
||||
uint8_t *dst = (uint8_t *)mono_array_addr(ret, uint8_t, 0);
|
||||
memcpy(dst, src, length);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -1032,23 +1133,22 @@ PackedByteArray mono_array_to_PackedByteArray(MonoArray *p_array) {
|
||||
return ret;
|
||||
int length = mono_array_length(p_array);
|
||||
ret.resize(length);
|
||||
uint8_t *w = ret.ptrw();
|
||||
uint8_t *dst = ret.ptrw();
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
w[i] = mono_array_get(p_array, uint8_t, i);
|
||||
}
|
||||
const uint8_t *src = (const uint8_t *)mono_array_addr(p_array, uint8_t, 0);
|
||||
memcpy(dst, src, length);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
MonoArray *PackedFloat32Array_to_mono_array(const PackedFloat32Array &p_array) {
|
||||
const real_t *r = p_array.ptr();
|
||||
const float *src = p_array.ptr();
|
||||
int length = p_array.size();
|
||||
|
||||
MonoArray *ret = mono_array_new(mono_domain_get(), REAL_T_MONOCLASS, p_array.size());
|
||||
MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(float), length);
|
||||
|
||||
for (int i = 0; i < p_array.size(); i++) {
|
||||
mono_array_set(ret, real_t, i, r[i]);
|
||||
}
|
||||
float *dst = (float *)mono_array_addr(ret, float, 0);
|
||||
memcpy(dst, src, length);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -1059,21 +1159,47 @@ PackedFloat32Array mono_array_to_PackedFloat32Array(MonoArray *p_array) {
|
||||
return ret;
|
||||
int length = mono_array_length(p_array);
|
||||
ret.resize(length);
|
||||
real_t *w = ret.ptrw();
|
||||
float *dst = ret.ptrw();
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
w[i] = mono_array_get(p_array, real_t, i);
|
||||
}
|
||||
const float *src = (const float *)mono_array_addr(p_array, float, 0);
|
||||
memcpy(dst, src, length);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
MonoArray *PackedFloat64Array_to_mono_array(const PackedFloat64Array &p_array) {
|
||||
const double *src = p_array.ptr();
|
||||
int length = p_array.size();
|
||||
|
||||
MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(double), length);
|
||||
|
||||
double *dst = (double *)mono_array_addr(ret, double, 0);
|
||||
memcpy(dst, src, length);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
PackedFloat64Array mono_array_to_PackedFloat64Array(MonoArray *p_array) {
|
||||
PackedFloat64Array ret;
|
||||
if (!p_array)
|
||||
return ret;
|
||||
int length = mono_array_length(p_array);
|
||||
ret.resize(length);
|
||||
double *dst = ret.ptrw();
|
||||
|
||||
const double *src = (const double *)mono_array_addr(p_array, double, 0);
|
||||
memcpy(dst, src, length);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
MonoArray *PackedStringArray_to_mono_array(const PackedStringArray &p_array) {
|
||||
const String *r = p_array.ptr();
|
||||
int length = p_array.size();
|
||||
|
||||
MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(String), p_array.size());
|
||||
MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(String), length);
|
||||
|
||||
for (int i = 0; i < p_array.size(); i++) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
MonoString *boxed = mono_string_from_godot(r[i]);
|
||||
mono_array_setref(ret, i, boxed);
|
||||
}
|
||||
@ -1098,13 +1224,19 @@ PackedStringArray mono_array_to_PackedStringArray(MonoArray *p_array) {
|
||||
}
|
||||
|
||||
MonoArray *PackedColorArray_to_mono_array(const PackedColorArray &p_array) {
|
||||
const Color *r = p_array.ptr();
|
||||
const Color *src = p_array.ptr();
|
||||
int length = p_array.size();
|
||||
|
||||
MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Color), p_array.size());
|
||||
MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Color), length);
|
||||
|
||||
for (int i = 0; i < p_array.size(); i++) {
|
||||
M_Color *raw = (M_Color *)mono_array_addr_with_size(ret, sizeof(M_Color), i);
|
||||
*raw = MARSHALLED_OUT(Color, r[i]);
|
||||
if constexpr (InteropLayout::MATCHES_Color) {
|
||||
Color *dst = (Color *)mono_array_addr(ret, Color, 0);
|
||||
memcpy(dst, src, length);
|
||||
} else {
|
||||
for (int i = 0; i < length; i++) {
|
||||
M_Color *raw = (M_Color *)mono_array_addr_with_size(ret, sizeof(M_Color), i);
|
||||
*raw = MARSHALLED_OUT(Color, src[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -1116,23 +1248,34 @@ PackedColorArray mono_array_to_PackedColorArray(MonoArray *p_array) {
|
||||
return ret;
|
||||
int length = mono_array_length(p_array);
|
||||
ret.resize(length);
|
||||
Color *w = ret.ptrw();
|
||||
Color *dst = ret.ptrw();
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
w[i] = MARSHALLED_IN(Color, (M_Color *)mono_array_addr_with_size(p_array, sizeof(M_Color), i));
|
||||
if constexpr (InteropLayout::MATCHES_Color) {
|
||||
const Color *src = (const Color *)mono_array_addr(p_array, Color, 0);
|
||||
memcpy(dst, src, length);
|
||||
} else {
|
||||
for (int i = 0; i < length; i++) {
|
||||
dst[i] = MARSHALLED_IN(Color, (M_Color *)mono_array_addr_with_size(p_array, sizeof(M_Color), i));
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
MonoArray *PackedVector2Array_to_mono_array(const PackedVector2Array &p_array) {
|
||||
const Vector2 *r = p_array.ptr();
|
||||
const Vector2 *src = p_array.ptr();
|
||||
int length = p_array.size();
|
||||
|
||||
MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Vector2), p_array.size());
|
||||
MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Vector2), length);
|
||||
|
||||
for (int i = 0; i < p_array.size(); i++) {
|
||||
M_Vector2 *raw = (M_Vector2 *)mono_array_addr_with_size(ret, sizeof(M_Vector2), i);
|
||||
*raw = MARSHALLED_OUT(Vector2, r[i]);
|
||||
if constexpr (InteropLayout::MATCHES_Vector2) {
|
||||
Vector2 *dst = (Vector2 *)mono_array_addr(ret, Vector2, 0);
|
||||
memcpy(dst, src, length);
|
||||
} else {
|
||||
for (int i = 0; i < length; i++) {
|
||||
M_Vector2 *raw = (M_Vector2 *)mono_array_addr_with_size(ret, sizeof(M_Vector2), i);
|
||||
*raw = MARSHALLED_OUT(Vector2, src[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -1144,23 +1287,34 @@ PackedVector2Array mono_array_to_PackedVector2Array(MonoArray *p_array) {
|
||||
return ret;
|
||||
int length = mono_array_length(p_array);
|
||||
ret.resize(length);
|
||||
Vector2 *w = ret.ptrw();
|
||||
Vector2 *dst = ret.ptrw();
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
w[i] = MARSHALLED_IN(Vector2, (M_Vector2 *)mono_array_addr_with_size(p_array, sizeof(M_Vector2), i));
|
||||
if constexpr (InteropLayout::MATCHES_Vector2) {
|
||||
const Vector2 *src = (const Vector2 *)mono_array_addr(p_array, Vector2, 0);
|
||||
memcpy(dst, src, length);
|
||||
} else {
|
||||
for (int i = 0; i < length; i++) {
|
||||
dst[i] = MARSHALLED_IN(Vector2, (M_Vector2 *)mono_array_addr_with_size(p_array, sizeof(M_Vector2), i));
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
MonoArray *PackedVector3Array_to_mono_array(const PackedVector3Array &p_array) {
|
||||
const Vector3 *r = p_array.ptr();
|
||||
const Vector3 *src = p_array.ptr();
|
||||
int length = p_array.size();
|
||||
|
||||
MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Vector3), p_array.size());
|
||||
MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Vector3), length);
|
||||
|
||||
for (int i = 0; i < p_array.size(); i++) {
|
||||
M_Vector3 *raw = (M_Vector3 *)mono_array_addr_with_size(ret, sizeof(M_Vector3), i);
|
||||
*raw = MARSHALLED_OUT(Vector3, r[i]);
|
||||
if constexpr (InteropLayout::MATCHES_Vector3) {
|
||||
Vector3 *dst = (Vector3 *)mono_array_addr(ret, Vector3, 0);
|
||||
memcpy(dst, src, length);
|
||||
} else {
|
||||
for (int i = 0; i < length; i++) {
|
||||
M_Vector3 *raw = (M_Vector3 *)mono_array_addr_with_size(ret, sizeof(M_Vector3), i);
|
||||
*raw = MARSHALLED_OUT(Vector3, src[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -1172,13 +1326,85 @@ PackedVector3Array mono_array_to_PackedVector3Array(MonoArray *p_array) {
|
||||
return ret;
|
||||
int length = mono_array_length(p_array);
|
||||
ret.resize(length);
|
||||
Vector3 *w = ret.ptrw();
|
||||
Vector3 *dst = ret.ptrw();
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
w[i] = MARSHALLED_IN(Vector3, (M_Vector3 *)mono_array_addr_with_size(p_array, sizeof(M_Vector3), i));
|
||||
if constexpr (InteropLayout::MATCHES_Vector3) {
|
||||
const Vector3 *src = (const Vector3 *)mono_array_addr(p_array, Vector3, 0);
|
||||
memcpy(dst, src, length);
|
||||
} else {
|
||||
for (int i = 0; i < length; i++) {
|
||||
dst[i] = MARSHALLED_IN(Vector3, (M_Vector3 *)mono_array_addr_with_size(p_array, sizeof(M_Vector3), i));
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Callable managed_to_callable(const M_Callable &p_managed_callable) {
|
||||
if (p_managed_callable.delegate) {
|
||||
// TODO: Use pooling for ManagedCallable instances.
|
||||
CallableCustom *managed_callable = memnew(ManagedCallable(p_managed_callable.delegate));
|
||||
return Callable(managed_callable);
|
||||
} else {
|
||||
Object *target = p_managed_callable.target ?
|
||||
unbox<Object *>(CACHED_FIELD(GodotObject, ptr)->get_value(p_managed_callable.target)) :
|
||||
NULL;
|
||||
StringName *method_ptr = unbox<StringName *>(CACHED_FIELD(StringName, ptr)->get_value(p_managed_callable.method_string_name));
|
||||
StringName method = method_ptr ? *method_ptr : StringName();
|
||||
return Callable(target, method);
|
||||
}
|
||||
}
|
||||
|
||||
M_Callable callable_to_managed(const Callable &p_callable) {
|
||||
if (p_callable.is_custom()) {
|
||||
CallableCustom *custom = p_callable.get_custom();
|
||||
CallableCustom::CompareEqualFunc compare_equal_func = custom->get_compare_equal_func();
|
||||
|
||||
if (compare_equal_func == ManagedCallable::compare_equal_func_ptr) {
|
||||
ManagedCallable *managed_callable = static_cast<ManagedCallable *>(custom);
|
||||
return {
|
||||
NULL, NULL,
|
||||
managed_callable->get_delegate()
|
||||
};
|
||||
} else if (compare_equal_func == SignalAwaiterCallable::compare_equal_func_ptr) {
|
||||
SignalAwaiterCallable *signal_awaiter_callable = static_cast<SignalAwaiterCallable *>(custom);
|
||||
return {
|
||||
GDMonoUtils::unmanaged_get_managed(ObjectDB::get_instance(signal_awaiter_callable->get_object())),
|
||||
GDMonoUtils::create_managed_from(signal_awaiter_callable->get_signal()),
|
||||
NULL
|
||||
};
|
||||
} else if (compare_equal_func == EventSignalCallable::compare_equal_func_ptr) {
|
||||
EventSignalCallable *event_signal_callable = static_cast<EventSignalCallable *>(custom);
|
||||
return {
|
||||
GDMonoUtils::unmanaged_get_managed(ObjectDB::get_instance(event_signal_callable->get_object())),
|
||||
GDMonoUtils::create_managed_from(event_signal_callable->get_signal()),
|
||||
NULL
|
||||
};
|
||||
}
|
||||
|
||||
// Some other CallableCustom. We only support ManagedCallable.
|
||||
return { NULL, NULL, NULL };
|
||||
} else {
|
||||
MonoObject *target_managed = GDMonoUtils::unmanaged_get_managed(p_callable.get_object());
|
||||
MonoObject *method_string_name_managed = GDMonoUtils::create_managed_from(p_callable.get_method());
|
||||
return { target_managed, method_string_name_managed, NULL };
|
||||
}
|
||||
}
|
||||
|
||||
Signal managed_to_signal_info(const M_SignalInfo &p_managed_signal) {
|
||||
Object *owner = p_managed_signal.owner ?
|
||||
unbox<Object *>(CACHED_FIELD(GodotObject, ptr)->get_value(p_managed_signal.owner)) :
|
||||
NULL;
|
||||
StringName *name_ptr = unbox<StringName *>(CACHED_FIELD(StringName, ptr)->get_value(p_managed_signal.name_string_name));
|
||||
StringName name = name_ptr ? *name_ptr : StringName();
|
||||
return Signal(owner, name);
|
||||
}
|
||||
|
||||
M_SignalInfo signal_info_to_managed(const Signal &p_signal) {
|
||||
Object *owner = p_signal.get_object();
|
||||
MonoObject *owner_managed = GDMonoUtils::unmanaged_get_managed(owner);
|
||||
MonoObject *name_string_name_managed = GDMonoUtils::create_managed_from(p_signal.get_name());
|
||||
return { owner_managed, name_string_name_managed };
|
||||
}
|
||||
|
||||
} // namespace GDMonoMarshal
|
||||
|
@ -33,6 +33,7 @@
|
||||
|
||||
#include "core/variant.h"
|
||||
|
||||
#include "../managed_callable.h"
|
||||
#include "gd_mono.h"
|
||||
#include "gd_mono_utils.h"
|
||||
|
||||
@ -62,7 +63,7 @@ T *unbox_addr(MonoObject *p_obj) {
|
||||
#define BOX_PTR(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(IntPtr), x)
|
||||
#define BOX_ENUM(m_enum_class, x) mono_value_box(mono_domain_get(), m_enum_class, &x)
|
||||
|
||||
Variant::Type managed_to_variant_type(const ManagedType &p_type);
|
||||
Variant::Type managed_to_variant_type(const ManagedType &p_type, bool *r_nil_is_variant = NULL);
|
||||
|
||||
bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_elem_type);
|
||||
bool try_get_dictionary_key_value_types(const ManagedType &p_dictionary_type, ManagedType &r_key_type, ManagedType &r_value_type);
|
||||
@ -132,6 +133,11 @@ Array mono_array_to_Array(MonoArray *p_array);
|
||||
MonoArray *PackedInt32Array_to_mono_array(const PackedInt32Array &p_array);
|
||||
PackedInt32Array mono_array_to_PackedInt32Array(MonoArray *p_array);
|
||||
|
||||
// PackedInt64Array
|
||||
|
||||
MonoArray *PackedInt64Array_to_mono_array(const PackedInt64Array &p_array);
|
||||
PackedInt64Array mono_array_to_PackedInt64Array(MonoArray *p_array);
|
||||
|
||||
// PackedByteArray
|
||||
|
||||
MonoArray *PackedByteArray_to_mono_array(const PackedByteArray &p_array);
|
||||
@ -142,6 +148,11 @@ PackedByteArray mono_array_to_PackedByteArray(MonoArray *p_array);
|
||||
MonoArray *PackedFloat32Array_to_mono_array(const PackedFloat32Array &p_array);
|
||||
PackedFloat32Array mono_array_to_PackedFloat32Array(MonoArray *p_array);
|
||||
|
||||
// PackedFloat64Array
|
||||
|
||||
MonoArray *PackedFloat64Array_to_mono_array(const PackedFloat64Array &p_array);
|
||||
PackedFloat64Array mono_array_to_PackedFloat64Array(MonoArray *p_array);
|
||||
|
||||
// PackedStringArray
|
||||
|
||||
MonoArray *PackedStringArray_to_mono_array(const PackedStringArray &p_array);
|
||||
@ -162,6 +173,29 @@ PackedVector2Array mono_array_to_PackedVector2Array(MonoArray *p_array);
|
||||
MonoArray *PackedVector3Array_to_mono_array(const PackedVector3Array &p_array);
|
||||
PackedVector3Array mono_array_to_PackedVector3Array(MonoArray *p_array);
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
struct M_Callable {
|
||||
MonoObject *target;
|
||||
MonoObject *method_string_name;
|
||||
MonoDelegate *delegate;
|
||||
};
|
||||
|
||||
struct M_SignalInfo {
|
||||
MonoObject *owner;
|
||||
MonoObject *name_string_name;
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
// Callable
|
||||
Callable managed_to_callable(const M_Callable &p_managed_callable);
|
||||
M_Callable callable_to_managed(const Callable &p_callable);
|
||||
|
||||
// SignalInfo
|
||||
Signal managed_to_signal_info(const M_SignalInfo &p_managed_signal);
|
||||
M_SignalInfo signal_info_to_managed(const Signal &p_signal);
|
||||
|
||||
// Structures
|
||||
|
||||
namespace InteropLayout {
|
||||
@ -222,8 +256,8 @@ enum {
|
||||
// In the future we may force this if we want to ref return these structs
|
||||
#ifdef GD_MONO_FORCE_INTEROP_STRUCT_COPY
|
||||
/* clang-format off */
|
||||
GD_STATIC_ASSERT(MATCHES_Vector2 && MATCHES_Rect2 && MATCHES_Transform2D && MATCHES_Vector3 &&
|
||||
MATCHES_Basis && MATCHES_Quat && MATCHES_Transform && MATCHES_AABB && MATCHES_Color &&MATCHES_Plane);
|
||||
static_assert(MATCHES_Vector2 && MATCHES_Rect2 && MATCHES_Transform2D && MATCHES_Vector3 &&
|
||||
MATCHES_Basis && MATCHES_Quat && MATCHES_Transform && MATCHES_AABB && MATCHES_Color && MATCHES_Plane);
|
||||
/* clang-format on */
|
||||
#endif
|
||||
|
||||
|
@ -101,50 +101,41 @@ IMonoClassMember::Visibility GDMonoMethod::get_visibility() {
|
||||
}
|
||||
}
|
||||
|
||||
MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params, MonoException **r_exc) {
|
||||
if (get_return_type().type_encoding != MONO_TYPE_VOID || get_parameters_count() > 0) {
|
||||
MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), get_parameters_count());
|
||||
MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params, MonoException **r_exc) const {
|
||||
MonoException *exc = NULL;
|
||||
MonoObject *ret;
|
||||
|
||||
if (params_count > 0) {
|
||||
MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), params_count);
|
||||
|
||||
for (int i = 0; i < params_count; i++) {
|
||||
MonoObject *boxed_param = GDMonoMarshal::variant_to_mono_object(p_params[i], param_types[i]);
|
||||
mono_array_setref(params, i, boxed_param);
|
||||
}
|
||||
|
||||
MonoException *exc = NULL;
|
||||
MonoObject *ret = GDMonoUtils::runtime_invoke_array(mono_method, p_object, params, &exc);
|
||||
|
||||
if (exc) {
|
||||
ret = NULL;
|
||||
if (r_exc) {
|
||||
*r_exc = exc;
|
||||
} else {
|
||||
GDMonoUtils::set_pending_exception(exc);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
ret = GDMonoUtils::runtime_invoke_array(mono_method, p_object, params, &exc);
|
||||
} else {
|
||||
MonoException *exc = NULL;
|
||||
GDMonoUtils::runtime_invoke(mono_method, p_object, NULL, &exc);
|
||||
|
||||
if (exc) {
|
||||
if (r_exc) {
|
||||
*r_exc = exc;
|
||||
} else {
|
||||
GDMonoUtils::set_pending_exception(exc);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
ret = GDMonoUtils::runtime_invoke(mono_method, p_object, NULL, &exc);
|
||||
}
|
||||
|
||||
if (exc) {
|
||||
ret = NULL;
|
||||
if (r_exc) {
|
||||
*r_exc = exc;
|
||||
} else {
|
||||
GDMonoUtils::set_pending_exception(exc);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
MonoObject *GDMonoMethod::invoke(MonoObject *p_object, MonoException **r_exc) {
|
||||
MonoObject *GDMonoMethod::invoke(MonoObject *p_object, MonoException **r_exc) const {
|
||||
ERR_FAIL_COND_V(get_parameters_count() > 0, NULL);
|
||||
return invoke_raw(p_object, NULL, r_exc);
|
||||
}
|
||||
|
||||
MonoObject *GDMonoMethod::invoke_raw(MonoObject *p_object, void **p_params, MonoException **r_exc) {
|
||||
MonoObject *GDMonoMethod::invoke_raw(MonoObject *p_object, void **p_params, MonoException **r_exc) const {
|
||||
MonoException *exc = NULL;
|
||||
MonoObject *ret = GDMonoUtils::runtime_invoke(mono_method, p_object, p_params, &exc);
|
||||
|
||||
@ -247,7 +238,7 @@ void GDMonoMethod::get_parameter_names(Vector<StringName> &names) const {
|
||||
}
|
||||
|
||||
void GDMonoMethod::get_parameter_types(Vector<ManagedType> &types) const {
|
||||
for (int i = 0; i < param_types.size(); ++i) {
|
||||
for (int i = 0; i < params_count; ++i) {
|
||||
types.push_back(param_types[i]);
|
||||
}
|
||||
}
|
||||
@ -256,13 +247,22 @@ const MethodInfo &GDMonoMethod::get_method_info() {
|
||||
|
||||
if (!method_info_fetched) {
|
||||
method_info.name = name;
|
||||
method_info.return_val = PropertyInfo(GDMonoMarshal::managed_to_variant_type(return_type), "");
|
||||
|
||||
bool nil_is_variant = false;
|
||||
method_info.return_val = PropertyInfo(GDMonoMarshal::managed_to_variant_type(return_type, &nil_is_variant), "");
|
||||
if (method_info.return_val.type == Variant::NIL && nil_is_variant)
|
||||
method_info.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
|
||||
|
||||
Vector<StringName> names;
|
||||
get_parameter_names(names);
|
||||
|
||||
for (int i = 0; i < params_count; ++i) {
|
||||
method_info.arguments.push_back(PropertyInfo(GDMonoMarshal::managed_to_variant_type(param_types[i]), names[i]));
|
||||
nil_is_variant = false;
|
||||
PropertyInfo arg_info = PropertyInfo(GDMonoMarshal::managed_to_variant_type(param_types[i], &nil_is_variant), names[i]);
|
||||
if (arg_info.type == Variant::NIL && nil_is_variant)
|
||||
arg_info.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
|
||||
|
||||
method_info.arguments.push_back(arg_info);
|
||||
}
|
||||
|
||||
// TODO: default arguments
|
||||
|
@ -57,28 +57,28 @@ class GDMonoMethod : public IMonoClassMember {
|
||||
MonoMethod *mono_method;
|
||||
|
||||
public:
|
||||
virtual GDMonoClass *get_enclosing_class() const GD_FINAL;
|
||||
virtual GDMonoClass *get_enclosing_class() const final;
|
||||
|
||||
virtual MemberType get_member_type() const GD_FINAL { return MEMBER_TYPE_METHOD; }
|
||||
virtual MemberType get_member_type() const final { return MEMBER_TYPE_METHOD; }
|
||||
|
||||
virtual StringName get_name() const GD_FINAL { return name; }
|
||||
virtual StringName get_name() const final { return name; }
|
||||
|
||||
virtual bool is_static() GD_FINAL;
|
||||
virtual bool is_static() final;
|
||||
|
||||
virtual Visibility get_visibility() GD_FINAL;
|
||||
virtual Visibility get_visibility() final;
|
||||
|
||||
virtual bool has_attribute(GDMonoClass *p_attr_class) GD_FINAL;
|
||||
virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) GD_FINAL;
|
||||
virtual bool has_attribute(GDMonoClass *p_attr_class) final;
|
||||
virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) final;
|
||||
void fetch_attributes();
|
||||
|
||||
_FORCE_INLINE_ MonoMethod *get_mono_ptr() { return mono_method; }
|
||||
_FORCE_INLINE_ MonoMethod *get_mono_ptr() const { return mono_method; }
|
||||
|
||||
_FORCE_INLINE_ int get_parameters_count() { return params_count; }
|
||||
_FORCE_INLINE_ ManagedType get_return_type() { return return_type; }
|
||||
_FORCE_INLINE_ int get_parameters_count() const { return params_count; }
|
||||
_FORCE_INLINE_ ManagedType get_return_type() const { return return_type; }
|
||||
|
||||
MonoObject *invoke(MonoObject *p_object, const Variant **p_params, MonoException **r_exc = NULL);
|
||||
MonoObject *invoke(MonoObject *p_object, MonoException **r_exc = NULL);
|
||||
MonoObject *invoke_raw(MonoObject *p_object, void **p_params, MonoException **r_exc = NULL);
|
||||
MonoObject *invoke(MonoObject *p_object, const Variant **p_params, MonoException **r_exc = NULL) const;
|
||||
MonoObject *invoke(MonoObject *p_object, MonoException **r_exc = NULL) const;
|
||||
MonoObject *invoke_raw(MonoObject *p_object, void **p_params, MonoException **r_exc = NULL) const;
|
||||
|
||||
String get_full_name(bool p_signature = false) const;
|
||||
String get_full_name_no_class() const;
|
||||
|
@ -47,17 +47,17 @@ class GDMonoProperty : public IMonoClassMember {
|
||||
MonoCustomAttrInfo *attributes;
|
||||
|
||||
public:
|
||||
virtual GDMonoClass *get_enclosing_class() const GD_FINAL { return owner; }
|
||||
virtual GDMonoClass *get_enclosing_class() const final { return owner; }
|
||||
|
||||
virtual MemberType get_member_type() const GD_FINAL { return MEMBER_TYPE_PROPERTY; }
|
||||
virtual MemberType get_member_type() const final { return MEMBER_TYPE_PROPERTY; }
|
||||
|
||||
virtual StringName get_name() const GD_FINAL { return name; }
|
||||
virtual StringName get_name() const final { return name; }
|
||||
|
||||
virtual bool is_static() GD_FINAL;
|
||||
virtual Visibility get_visibility() GD_FINAL;
|
||||
virtual bool is_static() final;
|
||||
virtual Visibility get_visibility() final;
|
||||
|
||||
virtual bool has_attribute(GDMonoClass *p_attr_class) GD_FINAL;
|
||||
virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) GD_FINAL;
|
||||
virtual bool has_attribute(GDMonoClass *p_attr_class) final;
|
||||
virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) final;
|
||||
void fetch_attributes();
|
||||
|
||||
bool has_getter();
|
||||
|
@ -162,6 +162,13 @@ void runtime_object_init(MonoObject *p_this_obj, GDMonoClass *p_class, MonoExcep
|
||||
ctor->invoke_raw(p_this_obj, NULL, r_exc);
|
||||
}
|
||||
|
||||
bool mono_delegate_equal(MonoDelegate *p_a, MonoDelegate *p_b) {
|
||||
MonoException *exc = NULL;
|
||||
MonoBoolean res = CACHED_METHOD_THUNK(Delegate, Equals).invoke((MonoObject *)p_a, (MonoObject *)p_b, &exc);
|
||||
UNHANDLED_EXCEPTION(exc);
|
||||
return (bool)res;
|
||||
}
|
||||
|
||||
GDMonoClass *get_object_class(MonoObject *p_object) {
|
||||
return GDMono::get_singleton()->get_class(mono_object_get_class(p_object));
|
||||
}
|
||||
@ -220,6 +227,18 @@ MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringNa
|
||||
return mono_object;
|
||||
}
|
||||
|
||||
MonoObject *create_managed_from(const StringName &p_from) {
|
||||
MonoObject *mono_object = mono_object_new(mono_domain_get(), CACHED_CLASS_RAW(StringName));
|
||||
ERR_FAIL_NULL_V(mono_object, NULL);
|
||||
|
||||
// Construct
|
||||
GDMonoUtils::runtime_object_init(mono_object, CACHED_CLASS(StringName));
|
||||
|
||||
CACHED_FIELD(StringName, ptr)->set_value_raw(mono_object, memnew(StringName(p_from)));
|
||||
|
||||
return mono_object;
|
||||
}
|
||||
|
||||
MonoObject *create_managed_from(const NodePath &p_from) {
|
||||
MonoObject *mono_object = mono_object_new(mono_domain_get(), CACHED_CLASS_RAW(NodePath));
|
||||
ERR_FAIL_NULL_V(mono_object, NULL);
|
||||
@ -362,7 +381,11 @@ void debug_send_unhandled_exception_error(MonoException *p_exc) {
|
||||
return;
|
||||
}
|
||||
|
||||
_TLS_RECURSION_GUARD_;
|
||||
static thread_local bool _recursion_flag_ = false;
|
||||
if (_recursion_flag_)
|
||||
return;
|
||||
_recursion_flag_ = true;
|
||||
SCOPE_EXIT { _recursion_flag_ = false; };
|
||||
|
||||
ScriptLanguage::StackInfo separator;
|
||||
separator.file = String();
|
||||
@ -439,8 +462,7 @@ void set_pending_exception(MonoException *p_exc) {
|
||||
#endif
|
||||
}
|
||||
|
||||
_THREAD_LOCAL_(int)
|
||||
current_invoke_count = 0;
|
||||
thread_local int current_invoke_count = 0;
|
||||
|
||||
MonoObject *runtime_invoke(MonoMethod *p_method, void *p_obj, void **p_params, MonoException **r_exc) {
|
||||
GD_MONO_BEGIN_RUNTIME_INVOKE;
|
||||
@ -644,6 +666,10 @@ ScopeThreadAttach::~ScopeThreadAttach() {
|
||||
}
|
||||
}
|
||||
|
||||
// namespace Marshal
|
||||
StringName get_native_godot_class_name(GDMonoClass *p_class) {
|
||||
MonoObject *native_name_obj = p_class->get_field(BINDINGS_NATIVE_NAME_FIELD)->get_value(NULL);
|
||||
StringName *ptr = GDMonoMarshal::unbox<StringName *>(CACHED_FIELD(StringName, ptr)->get_value(native_name_obj));
|
||||
return ptr ? *ptr : StringName();
|
||||
}
|
||||
|
||||
} // namespace GDMonoUtils
|
||||
|
@ -35,7 +35,6 @@
|
||||
|
||||
#include "../mono_gc_handle.h"
|
||||
#include "../utils/macros.h"
|
||||
#include "../utils/thread_local.h"
|
||||
#include "gd_mono_header.h"
|
||||
|
||||
#include "core/object.h"
|
||||
@ -95,12 +94,15 @@ _FORCE_INLINE_ bool is_main_thread() {
|
||||
|
||||
void runtime_object_init(MonoObject *p_this_obj, GDMonoClass *p_class, MonoException **r_exc = NULL);
|
||||
|
||||
bool mono_delegate_equal(MonoDelegate *p_a, MonoDelegate *p_b);
|
||||
|
||||
GDMonoClass *get_object_class(MonoObject *p_object);
|
||||
GDMonoClass *type_get_proxy_class(const StringName &p_type);
|
||||
GDMonoClass *get_class_native_base(GDMonoClass *p_class);
|
||||
|
||||
MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringName &p_native, Object *p_object);
|
||||
|
||||
MonoObject *create_managed_from(const StringName &p_from);
|
||||
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);
|
||||
@ -123,7 +125,7 @@ void print_unhandled_exception(MonoException *p_exc);
|
||||
*/
|
||||
void set_pending_exception(MonoException *p_exc);
|
||||
|
||||
extern _THREAD_LOCAL_(int) current_invoke_count;
|
||||
extern thread_local int current_invoke_count;
|
||||
|
||||
_FORCE_INLINE_ int get_runtime_invoke_count() {
|
||||
return current_invoke_count;
|
||||
@ -152,9 +154,11 @@ private:
|
||||
MonoThread *mono_thread;
|
||||
};
|
||||
|
||||
StringName get_native_godot_class_name(GDMonoClass *p_class);
|
||||
|
||||
} // namespace GDMonoUtils
|
||||
|
||||
#define NATIVE_GDMONOCLASS_NAME(m_class) (GDMonoMarshal::mono_string_to_godot((MonoString *)m_class->get_field(BINDINGS_NATIVE_NAME_FIELD)->get_value(NULL)))
|
||||
#define NATIVE_GDMONOCLASS_NAME(m_class) (GDMonoUtils::get_native_godot_class_name(m_class))
|
||||
|
||||
#define GD_MONO_BEGIN_RUNTIME_INVOKE \
|
||||
int &_runtime_invoke_count_ref = GDMonoUtils::get_runtime_invoke_count_ref(); \
|
||||
|
@ -36,103 +36,182 @@
|
||||
#include "mono_gd/gd_mono_marshal.h"
|
||||
#include "mono_gd/gd_mono_utils.h"
|
||||
|
||||
namespace SignalAwaiterUtils {
|
||||
|
||||
Error connect_signal_awaiter(Object *p_source, const String &p_signal, Object *p_target, MonoObject *p_awaiter) {
|
||||
|
||||
Error gd_mono_connect_signal_awaiter(Object *p_source, const StringName &p_signal, Object *p_target, MonoObject *p_awaiter) {
|
||||
ERR_FAIL_NULL_V(p_source, ERR_INVALID_DATA);
|
||||
ERR_FAIL_NULL_V(p_target, ERR_INVALID_DATA);
|
||||
|
||||
Ref<SignalAwaiterHandle> sa_con = memnew(SignalAwaiterHandle(p_awaiter));
|
||||
#ifdef DEBUG_ENABLED
|
||||
sa_con->set_connection_target(p_target);
|
||||
#endif
|
||||
// TODO: Use pooling for ManagedCallable instances.
|
||||
SignalAwaiterCallable *awaiter_callable = memnew(SignalAwaiterCallable(p_target, p_awaiter, p_signal));
|
||||
Callable callable = Callable(awaiter_callable);
|
||||
|
||||
Vector<Variant> binds;
|
||||
binds.push_back(sa_con);
|
||||
|
||||
Error err = p_source->connect_compat(p_signal, sa_con.ptr(),
|
||||
CSharpLanguage::get_singleton()->get_string_names()._signal_callback,
|
||||
binds, Object::CONNECT_ONESHOT);
|
||||
|
||||
if (err != OK) {
|
||||
// Set it as completed to prevent it from calling the failure callback when released.
|
||||
// The awaiter will be aware of the failure by checking the returned error.
|
||||
sa_con->set_completed(true);
|
||||
}
|
||||
|
||||
return err;
|
||||
return p_source->connect(p_signal, callable, Vector<Variant>(), Object::CONNECT_ONESHOT);
|
||||
}
|
||||
} // namespace SignalAwaiterUtils
|
||||
|
||||
Variant SignalAwaiterHandle::_signal_callback(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
|
||||
bool SignalAwaiterCallable::compare_equal(const CallableCustom *p_a, const CallableCustom *p_b) {
|
||||
const SignalAwaiterCallable *a = static_cast<const SignalAwaiterCallable *>(p_a);
|
||||
const SignalAwaiterCallable *b = static_cast<const SignalAwaiterCallable *>(p_b);
|
||||
|
||||
if (a->target_id != b->target_id)
|
||||
return false;
|
||||
|
||||
if (a->signal != b->signal)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SignalAwaiterCallable::compare_less(const CallableCustom *p_a, const CallableCustom *p_b) {
|
||||
if (compare_equal(p_a, p_b))
|
||||
return false;
|
||||
return p_a < p_b;
|
||||
}
|
||||
|
||||
uint32_t SignalAwaiterCallable::hash() const {
|
||||
uint32_t hash = signal.hash();
|
||||
return hash_djb2_one_64(target_id, hash);
|
||||
}
|
||||
|
||||
String SignalAwaiterCallable::get_as_text() const {
|
||||
Object *base = ObjectDB::get_instance(target_id);
|
||||
if (base) {
|
||||
String class_name = base->get_class();
|
||||
Ref<Script> script = base->get_script();
|
||||
if (script.is_valid() && script->get_path().is_resource_file()) {
|
||||
|
||||
class_name += "(" + script->get_path().get_file() + ")";
|
||||
}
|
||||
return class_name + "::SignalAwaiterMiddleman::" + String(signal);
|
||||
} else {
|
||||
return "null::SignalAwaiterMiddleman::" + String(signal);
|
||||
}
|
||||
}
|
||||
|
||||
CallableCustom::CompareEqualFunc SignalAwaiterCallable::get_compare_equal_func() const {
|
||||
return compare_equal_func_ptr;
|
||||
}
|
||||
|
||||
CallableCustom::CompareLessFunc SignalAwaiterCallable::get_compare_less_func() const {
|
||||
return compare_less_func_ptr;
|
||||
}
|
||||
|
||||
ObjectID SignalAwaiterCallable::get_object() const {
|
||||
return target_id;
|
||||
}
|
||||
|
||||
void SignalAwaiterCallable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
|
||||
r_call_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; // Can't find anything better
|
||||
r_return_value = Variant();
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
ERR_FAIL_COND_V_MSG(conn_target_id.is_valid() && !ObjectDB::get_instance(conn_target_id), Variant(),
|
||||
ERR_FAIL_COND_MSG(target_id.is_valid() && !ObjectDB::get_instance(target_id),
|
||||
"Resumed after await, but class instance is gone.");
|
||||
#endif
|
||||
|
||||
if (p_argcount < 1) {
|
||||
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
|
||||
r_error.argument = 1;
|
||||
return Variant();
|
||||
}
|
||||
MonoArray *signal_args = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), p_argcount);
|
||||
|
||||
Ref<SignalAwaiterHandle> self = *p_args[p_argcount - 1];
|
||||
|
||||
if (self.is_null()) {
|
||||
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
|
||||
r_error.argument = p_argcount - 1;
|
||||
r_error.expected = Variant::OBJECT;
|
||||
return Variant();
|
||||
}
|
||||
|
||||
set_completed(true);
|
||||
|
||||
int signal_argc = p_argcount - 1;
|
||||
MonoArray *signal_args = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), signal_argc);
|
||||
|
||||
for (int i = 0; i < signal_argc; i++) {
|
||||
MonoObject *boxed = GDMonoMarshal::variant_to_mono_object(*p_args[i]);
|
||||
for (int i = 0; i < p_argcount; i++) {
|
||||
MonoObject *boxed = GDMonoMarshal::variant_to_mono_object(*p_arguments[i]);
|
||||
mono_array_setref(signal_args, i, boxed);
|
||||
}
|
||||
|
||||
MonoException *exc = NULL;
|
||||
GD_MONO_BEGIN_RUNTIME_INVOKE;
|
||||
CACHED_METHOD_THUNK(SignalAwaiter, SignalCallback).invoke(get_target(), signal_args, &exc);
|
||||
GD_MONO_END_RUNTIME_INVOKE;
|
||||
CACHED_METHOD_THUNK(SignalAwaiter, SignalCallback).invoke(awaiter_handle->get_target(), signal_args, &exc);
|
||||
|
||||
if (exc) {
|
||||
GDMonoUtils::set_pending_exception(exc);
|
||||
ERR_FAIL_V(Variant());
|
||||
}
|
||||
|
||||
return Variant();
|
||||
}
|
||||
|
||||
void SignalAwaiterHandle::_bind_methods() {
|
||||
|
||||
ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "_signal_callback", &SignalAwaiterHandle::_signal_callback, MethodInfo("_signal_callback"));
|
||||
}
|
||||
|
||||
SignalAwaiterHandle::SignalAwaiterHandle(MonoObject *p_managed) :
|
||||
MonoGCHandle(MonoGCHandle::new_strong_handle(p_managed), STRONG_HANDLE) {}
|
||||
|
||||
SignalAwaiterHandle::~SignalAwaiterHandle() {
|
||||
|
||||
if (!completed) {
|
||||
MonoObject *awaiter = get_target();
|
||||
|
||||
if (awaiter) {
|
||||
MonoException *exc = NULL;
|
||||
GD_MONO_BEGIN_RUNTIME_INVOKE;
|
||||
CACHED_METHOD_THUNK(SignalAwaiter, FailureCallback).invoke(awaiter, &exc);
|
||||
GD_MONO_END_RUNTIME_INVOKE;
|
||||
|
||||
if (exc) {
|
||||
GDMonoUtils::set_pending_exception(exc);
|
||||
ERR_FAIL();
|
||||
}
|
||||
}
|
||||
ERR_FAIL();
|
||||
} else {
|
||||
r_call_error.error = Callable::CallError::CALL_OK;
|
||||
}
|
||||
}
|
||||
|
||||
SignalAwaiterCallable::SignalAwaiterCallable(Object *p_target, MonoObject *p_awaiter, const StringName &p_signal) :
|
||||
target_id(p_target->get_instance_id()),
|
||||
awaiter_handle(MonoGCHandle::create_strong(p_awaiter)),
|
||||
signal(p_signal) {
|
||||
}
|
||||
|
||||
bool EventSignalCallable::compare_equal(const CallableCustom *p_a, const CallableCustom *p_b) {
|
||||
const EventSignalCallable *a = static_cast<const EventSignalCallable *>(p_a);
|
||||
const EventSignalCallable *b = static_cast<const EventSignalCallable *>(p_b);
|
||||
|
||||
if (a->owner != b->owner)
|
||||
return false;
|
||||
|
||||
if (a->event_signal != b->event_signal)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EventSignalCallable::compare_less(const CallableCustom *p_a, const CallableCustom *p_b) {
|
||||
if (compare_equal(p_a, p_b))
|
||||
return false;
|
||||
return p_a < p_b;
|
||||
}
|
||||
|
||||
uint32_t EventSignalCallable::hash() const {
|
||||
uint32_t hash = event_signal->field->get_name().hash();
|
||||
return hash_djb2_one_64(owner->get_instance_id(), hash);
|
||||
}
|
||||
|
||||
String EventSignalCallable::get_as_text() const {
|
||||
String class_name = owner->get_class();
|
||||
Ref<Script> script = owner->get_script();
|
||||
if (script.is_valid() && script->get_path().is_resource_file()) {
|
||||
|
||||
class_name += "(" + script->get_path().get_file() + ")";
|
||||
}
|
||||
StringName signal = event_signal->field->get_name();
|
||||
return class_name + "::EventSignalMiddleman::" + String(signal);
|
||||
}
|
||||
|
||||
CallableCustom::CompareEqualFunc EventSignalCallable::get_compare_equal_func() const {
|
||||
return compare_equal_func_ptr;
|
||||
}
|
||||
|
||||
CallableCustom::CompareLessFunc EventSignalCallable::get_compare_less_func() const {
|
||||
return compare_less_func_ptr;
|
||||
}
|
||||
|
||||
ObjectID EventSignalCallable::get_object() const {
|
||||
return owner->get_instance_id();
|
||||
}
|
||||
|
||||
StringName EventSignalCallable::get_signal() const {
|
||||
return event_signal->field->get_name();
|
||||
}
|
||||
|
||||
void EventSignalCallable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
|
||||
r_call_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; // Can't find anything better
|
||||
r_return_value = Variant();
|
||||
|
||||
ERR_FAIL_COND(p_argcount < event_signal->invoke_method->get_parameters_count());
|
||||
|
||||
CSharpInstance *csharp_instance = CAST_CSHARP_INSTANCE(owner->get_script_instance());
|
||||
ERR_FAIL_NULL(csharp_instance);
|
||||
|
||||
MonoObject *owner_managed = csharp_instance->get_mono_object();
|
||||
ERR_FAIL_NULL(owner_managed);
|
||||
|
||||
MonoObject *delegate_field_value = event_signal->field->get_value(owner_managed);
|
||||
if (!delegate_field_value) {
|
||||
r_call_error.error = Callable::CallError::CALL_OK;
|
||||
return;
|
||||
}
|
||||
|
||||
MonoException *exc = NULL;
|
||||
event_signal->invoke_method->invoke(delegate_field_value, p_arguments, &exc);
|
||||
|
||||
if (exc) {
|
||||
GDMonoUtils::set_pending_exception(exc);
|
||||
ERR_FAIL();
|
||||
} else {
|
||||
r_call_error.error = Callable::CallError::CALL_OK;
|
||||
}
|
||||
}
|
||||
|
||||
EventSignalCallable::EventSignalCallable(Object *p_owner, const CSharpScript::EventSignal *p_event_signal) :
|
||||
owner(p_owner),
|
||||
event_signal(p_event_signal) {
|
||||
}
|
||||
|
@ -32,40 +32,65 @@
|
||||
#define SIGNAL_AWAITER_UTILS_H
|
||||
|
||||
#include "core/reference.h"
|
||||
|
||||
#include "csharp_script.h"
|
||||
#include "mono_gc_handle.h"
|
||||
|
||||
namespace SignalAwaiterUtils {
|
||||
Error gd_mono_connect_signal_awaiter(Object *p_source, const StringName &p_signal, Object *p_target, MonoObject *p_awaiter);
|
||||
|
||||
Error connect_signal_awaiter(Object *p_source, const String &p_signal, Object *p_target, MonoObject *p_awaiter);
|
||||
}
|
||||
|
||||
class SignalAwaiterHandle : public MonoGCHandle {
|
||||
|
||||
GDCLASS(SignalAwaiterHandle, MonoGCHandle);
|
||||
|
||||
bool completed;
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
ObjectID conn_target_id;
|
||||
#endif
|
||||
|
||||
Variant _signal_callback(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
class SignalAwaiterCallable : public CallableCustom {
|
||||
ObjectID target_id;
|
||||
Ref<MonoGCHandle> awaiter_handle;
|
||||
StringName signal;
|
||||
|
||||
public:
|
||||
_FORCE_INLINE_ bool is_completed() { return completed; }
|
||||
_FORCE_INLINE_ void set_completed(bool p_completed) { completed = p_completed; }
|
||||
static bool compare_equal(const CallableCustom *p_a, const CallableCustom *p_b);
|
||||
static bool compare_less(const CallableCustom *p_a, const CallableCustom *p_b);
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
_FORCE_INLINE_ void set_connection_target(Object *p_target) {
|
||||
conn_target_id = p_target->get_instance_id();
|
||||
}
|
||||
#endif
|
||||
static constexpr CompareEqualFunc compare_equal_func_ptr = &SignalAwaiterCallable::compare_equal;
|
||||
static constexpr CompareEqualFunc compare_less_func_ptr = &SignalAwaiterCallable::compare_less;
|
||||
|
||||
SignalAwaiterHandle(MonoObject *p_managed);
|
||||
~SignalAwaiterHandle();
|
||||
uint32_t hash() const override;
|
||||
|
||||
String get_as_text() const override;
|
||||
|
||||
CompareEqualFunc get_compare_equal_func() const override;
|
||||
CompareLessFunc get_compare_less_func() const override;
|
||||
|
||||
ObjectID get_object() const override;
|
||||
|
||||
_FORCE_INLINE_ StringName get_signal() const { return signal; }
|
||||
|
||||
void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override;
|
||||
|
||||
SignalAwaiterCallable(Object *p_target, MonoObject *p_awaiter, const StringName &p_signal);
|
||||
};
|
||||
|
||||
class EventSignalCallable : public CallableCustom {
|
||||
Object *owner;
|
||||
const CSharpScript::EventSignal *event_signal;
|
||||
|
||||
public:
|
||||
static bool compare_equal(const CallableCustom *p_a, const CallableCustom *p_b);
|
||||
static bool compare_less(const CallableCustom *p_a, const CallableCustom *p_b);
|
||||
|
||||
static constexpr CompareEqualFunc compare_equal_func_ptr = &EventSignalCallable::compare_equal;
|
||||
static constexpr CompareEqualFunc compare_less_func_ptr = &EventSignalCallable::compare_less;
|
||||
|
||||
uint32_t hash() const override;
|
||||
|
||||
String get_as_text() const override;
|
||||
|
||||
CompareEqualFunc get_compare_equal_func() const override;
|
||||
CompareLessFunc get_compare_less_func() const override;
|
||||
|
||||
ObjectID get_object() const override;
|
||||
|
||||
StringName get_signal() const;
|
||||
|
||||
void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override;
|
||||
|
||||
EventSignalCallable(Object *p_owner, const CSharpScript::EventSignal *p_event_signal);
|
||||
};
|
||||
|
||||
#endif // SIGNAL_AWAITER_UTILS_H
|
||||
|
@ -36,38 +36,6 @@
|
||||
#define _GD_VARNAME_CONCAT_(m_a, m_b, m_c) _GD_VARNAME_CONCAT_A_(m_a, m_b, m_c)
|
||||
#define GD_UNIQUE_NAME(m_name) _GD_VARNAME_CONCAT_(m_name, _, __COUNTER__)
|
||||
|
||||
// static assert
|
||||
// TODO: Get rid of this macro once we upgrade to C++11
|
||||
|
||||
#ifdef __cpp_static_assert
|
||||
#define GD_STATIC_ASSERT(m_cond) static_assert((m_cond), "Condition '" #m_cond "' failed")
|
||||
#else
|
||||
#define GD_STATIC_ASSERT(m_cond) typedef int GD_UNIQUE_NAME(godot_static_assert)[((m_cond) ? 1 : -1)]
|
||||
#endif
|
||||
|
||||
// final
|
||||
// TODO: Get rid of this macro once we upgrade to C++11
|
||||
|
||||
#if (__cplusplus >= 201103L)
|
||||
#define GD_FINAL final
|
||||
#else
|
||||
#define GD_FINAL
|
||||
#endif
|
||||
|
||||
// noreturn
|
||||
// TODO: Get rid of this macro once we upgrade to C++11
|
||||
|
||||
#if (__cplusplus >= 201103L)
|
||||
#define GD_NORETURN [[noreturn]]
|
||||
#elif defined(__GNUC__)
|
||||
#define GD_NORETURN __attribute__((noreturn))
|
||||
#elif defined(_MSC_VER)
|
||||
#define GD_NORETURN __declspec(noreturn)
|
||||
#else
|
||||
#define GD_NORETURN
|
||||
#pragma message "Macro GD_NORETURN will have no effect"
|
||||
#endif
|
||||
|
||||
// unreachable
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
@ -81,4 +49,25 @@
|
||||
} while (true);
|
||||
#endif
|
||||
|
||||
namespace gdmono {
|
||||
|
||||
template <typename F>
|
||||
struct ScopeExit {
|
||||
ScopeExit(F p_exit_func) :
|
||||
exit_func(p_exit_func) {}
|
||||
~ScopeExit() { exit_func(); }
|
||||
F exit_func;
|
||||
};
|
||||
|
||||
class ScopeExitAux {
|
||||
public:
|
||||
template <typename F>
|
||||
ScopeExit<F> operator+(F p_exit_func) { return ScopeExit<F>(p_exit_func); }
|
||||
};
|
||||
|
||||
} // namespace gdmono
|
||||
|
||||
#define SCOPE_EXIT \
|
||||
auto GD_UNIQUE_NAME(gd_scope_exit) = gdmono::ScopeExitAux() + [=]()
|
||||
|
||||
#endif // UTIL_MACROS_H
|
||||
|
@ -1,177 +0,0 @@
|
||||
/*************************************************************************/
|
||||
/* thread_local.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 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 THREAD_LOCAL_H
|
||||
#define THREAD_LOCAL_H
|
||||
|
||||
#ifdef HAVE_CXX11_THREAD_LOCAL
|
||||
#define _THREAD_LOCAL_(m_t) thread_local m_t
|
||||
#else
|
||||
|
||||
#if !defined(__GNUC__) && !defined(_MSC_VER)
|
||||
#error Platform or compiler not supported
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__)
|
||||
|
||||
#ifdef HAVE_GCC___THREAD
|
||||
#define _THREAD_LOCAL_(m_t) __thread m_t
|
||||
#else
|
||||
#define USE_CUSTOM_THREAD_LOCAL
|
||||
#endif
|
||||
|
||||
#elif defined(_MSC_VER)
|
||||
|
||||
#ifdef HAVE_DECLSPEC_THREAD
|
||||
#define _THREAD_LOCAL_(m_t) __declspec(thread) m_t
|
||||
#else
|
||||
#define USE_CUSTOM_THREAD_LOCAL
|
||||
#endif
|
||||
|
||||
#endif // __GNUC__ _MSC_VER
|
||||
|
||||
#endif // HAVE_CXX11_THREAD_LOCAL
|
||||
|
||||
#ifdef USE_CUSTOM_THREAD_LOCAL
|
||||
#define _THREAD_LOCAL_(m_t) ThreadLocal<m_t>
|
||||
#endif
|
||||
|
||||
#include "core/typedefs.h"
|
||||
|
||||
#ifdef WINDOWS_ENABLED
|
||||
#define _CALLBACK_FUNC_ __stdcall
|
||||
#else
|
||||
#define _CALLBACK_FUNC_
|
||||
#endif
|
||||
|
||||
struct ThreadLocalStorage {
|
||||
|
||||
void *get_value() const;
|
||||
void set_value(void *p_value) const;
|
||||
|
||||
void alloc(void(_CALLBACK_FUNC_ *p_destr_callback)(void *));
|
||||
void free();
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
Impl *pimpl;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class ThreadLocal {
|
||||
|
||||
ThreadLocalStorage storage;
|
||||
|
||||
T init_val;
|
||||
|
||||
static void _CALLBACK_FUNC_ destr_callback(void *tls_data) {
|
||||
memdelete(static_cast<T *>(tls_data));
|
||||
}
|
||||
|
||||
T *_tls_get_value() const {
|
||||
void *tls_data = storage.get_value();
|
||||
|
||||
if (tls_data)
|
||||
return static_cast<T *>(tls_data);
|
||||
|
||||
T *data = memnew(T(init_val));
|
||||
|
||||
storage.set_value(data);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void _initialize(const T &p_init_val) {
|
||||
init_val = p_init_val;
|
||||
storage.alloc(&destr_callback);
|
||||
}
|
||||
|
||||
public:
|
||||
ThreadLocal() {
|
||||
_initialize(T());
|
||||
}
|
||||
|
||||
ThreadLocal(const T &p_init_val) {
|
||||
_initialize(p_init_val);
|
||||
}
|
||||
|
||||
ThreadLocal(const ThreadLocal &other) {
|
||||
_initialize(*other._tls_get_value());
|
||||
}
|
||||
|
||||
~ThreadLocal() {
|
||||
storage.free();
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ T *operator&() const {
|
||||
return _tls_get_value();
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ operator T &() const {
|
||||
return *_tls_get_value();
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ ThreadLocal &operator=(const T &val) {
|
||||
T *ptr = _tls_get_value();
|
||||
*ptr = val;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
struct FlagScopeGuard {
|
||||
|
||||
FlagScopeGuard(bool &p_flag) :
|
||||
flag(p_flag) {
|
||||
flag = !flag;
|
||||
}
|
||||
|
||||
~FlagScopeGuard() {
|
||||
flag = !flag;
|
||||
}
|
||||
|
||||
private:
|
||||
bool &flag;
|
||||
};
|
||||
|
||||
#undef _CALLBACK_FUNC_
|
||||
|
||||
#define _TLS_RECURSION_GUARD_V_(m_ret) \
|
||||
static _THREAD_LOCAL_(bool) _recursion_flag_ = false; \
|
||||
if (_recursion_flag_) \
|
||||
return m_ret; \
|
||||
FlagScopeGuard _recursion_guard_(_recursion_flag_);
|
||||
|
||||
#define _TLS_RECURSION_GUARD_ \
|
||||
static _THREAD_LOCAL_(bool) _recursion_flag_ = false; \
|
||||
if (_recursion_flag_) \
|
||||
return; \
|
||||
FlagScopeGuard _recursion_guard_(_recursion_flag_);
|
||||
|
||||
#endif // THREAD_LOCAL_H
|
Loading…
Reference in New Issue
Block a user