Fix C# bindings after recent breaking changes

Implementation for new Variant types Callable, Signal, StringName.
Added support for PackedInt64Array and PackedFloat64Array.

Add generation of signal members as events, as well as support for
user created signals as events.
NOTE: As of now, raising such events will not emit the signal. As such,
one must use `EmitSignal` instead of raising the event directly.

Removed old ThreadLocal fallback class. It's safe to use thread_local now since
it's supported on all minimum versions of compilers we support.
This commit is contained in:
Ignacio Etcheverry 2020-03-14 19:20:17 +01:00
parent 0159787864
commit 6a85cdf640
51 changed files with 2448 additions and 856 deletions

View file

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

View file

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

View file

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

View file

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

View file

@ -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'])

View file

@ -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> &params) {
bool CSharpScript::_get_signal(GDMonoClass *p_class, GDMonoMethod *p_delegate_invoke, Vector<SignalParameter> &params) {
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> &params = E->value();
for (int i = 0; i < params.size(); i++) {
const SignalParameter &param = 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> &params = event_signal.parameters;
for (int i = 0; i < params.size(); i++) {
const SignalParameter &param = 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");
}

View file

@ -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> &params);
bool _get_signal(GDMonoClass *p_class, GDMonoMethod *p_delegate_invoke, Vector<SignalParameter> &params);
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;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -2,7 +2,7 @@ using System;
namespace Godot
{
[AttributeUsage(AttributeTargets.Delegate)]
[AttributeUsage(AttributeTargets.Delegate | AttributeTargets.Event)]
public class SignalAttribute : Attribute
{
}

View 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;
}
}
}

View 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;
}
}
}

View file

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

View file

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

View file

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

View file

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

View 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;
}
}
}

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View 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

View 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

View 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

View file

@ -41,6 +41,8 @@ class MonoGCHandle : public Reference {
bool released;
bool weak;
friend class ManagedCallable;
uint32_t handle;
public:

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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