diff options
Diffstat (limited to 'modules/mono/csharp_script.cpp')
-rw-r--r-- | modules/mono/csharp_script.cpp | 564 |
1 files changed, 428 insertions, 136 deletions
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index f95a3dab2d..bbe245951e 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "csharp_script.h" #include <mono/metadata/threads.h> @@ -117,6 +118,8 @@ void CSharpLanguage::init() { #ifdef TOOLS_ENABLED EditorNode::add_init_callback(&gdsharp_editor_init_callback); + + GLOBAL_DEF("mono/export/include_scripts_content", true); #endif } @@ -175,7 +178,7 @@ void CSharpLanguage::get_reserved_words(List<String> *p_words) const { "fixed", "float", "for", - "forech", + "foreach", "goto", "if", "implicit", @@ -221,14 +224,17 @@ void CSharpLanguage::get_reserved_words(List<String> *p_words) const { "ushort", "using", "virtual", - "volatile", "void", + "volatile", "while", // Contextual keywords. Not reserved words, but I guess we should include // them because this seems to be used only for syntax highlighting. "add", + "alias", "ascending", + "async", + "await", "by", "descending", "dynamic", @@ -237,10 +243,10 @@ void CSharpLanguage::get_reserved_words(List<String> *p_words) const { "get", "global", "group", - "in", "into", "join", "let", + "nameof", "on", "orderby", "partial", @@ -249,6 +255,7 @@ void CSharpLanguage::get_reserved_words(List<String> *p_words) const { "set", "value", "var", + "when", "where", "yield", 0 @@ -275,6 +282,15 @@ void CSharpLanguage::get_string_delimiters(List<String> *p_delimiters) const { p_delimiters->push_back("@\" \""); // verbatim string literal } +static String get_base_class_name(const String &p_base_class_name, const String p_class_name) { + + String base_class = p_base_class_name; + if (p_class_name == base_class) { + base_class = "Godot." + base_class; + } + return base_class; +} + Ref<Script> CSharpLanguage::get_template(const String &p_class_name, const String &p_base_class_name) const { String script_template = "using " BINDINGS_NAMESPACE ";\n" @@ -289,7 +305,7 @@ Ref<Script> CSharpLanguage::get_template(const String &p_class_name, const Strin " public override void _Ready()\n" " {\n" " // Called every time the node is added to the scene.\n" - " // Initialization here\n" + " // Initialization here.\n" " \n" " }\n" "\n" @@ -301,7 +317,8 @@ Ref<Script> CSharpLanguage::get_template(const String &p_class_name, const Strin "// }\n" "}\n"; - script_template = script_template.replace("%BASE_CLASS_NAME%", p_base_class_name) + String base_class_name = get_base_class_name(p_base_class_name, p_class_name); + script_template = script_template.replace("%BASE_CLASS_NAME%", base_class_name) .replace("%CLASS_NAME%", p_class_name); Ref<CSharpScript> script; @@ -320,12 +337,24 @@ bool CSharpLanguage::is_using_templates() { void CSharpLanguage::make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script) { String src = p_script->get_source_code(); - src = src.replace("%BASE%", p_base_class_name) + String base_class_name = get_base_class_name(p_base_class_name, p_class_name); + src = src.replace("%BASE%", base_class_name) .replace("%CLASS%", p_class_name) .replace("%TS%", _get_indentation()); p_script->set_source_code(src); } +String CSharpLanguage::validate_path(const String &p_path) const { + + String class_name = p_path.get_file().get_basename(); + List<String> keywords; + get_reserved_words(&keywords); + if (keywords.find(class_name)) { + return TTR("Class name can't be a reserved keyword"); + } + return ""; +} + Script *CSharpLanguage::create_script() const { return memnew(CSharpScript); @@ -444,6 +473,82 @@ String CSharpLanguage::_get_indentation() const { return "\t"; } +Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info() { + +#ifdef DEBUG_ENABLED + // Printing an error here will result in endless recursion, so we must be careful + + if (!gdmono->is_runtime_initialized() || !GDMono::get_singleton()->get_core_api_assembly() || !GDMonoUtils::mono_cache.corlib_cache_updated) + return Vector<StackInfo>(); + + MonoObject *stack_trace = mono_object_new(mono_domain_get(), CACHED_CLASS(System_Diagnostics_StackTrace)->get_mono_ptr()); + + MonoBoolean need_file_info = true; + void *ctor_args[1] = { &need_file_info }; + + CACHED_METHOD(System_Diagnostics_StackTrace, ctor_bool)->invoke_raw(stack_trace, ctor_args); + + Vector<StackInfo> si; + si = stack_trace_get_info(stack_trace); + + return si; +#else + return Vector<StackInfo>(); +#endif +} + +#ifdef DEBUG_ENABLED +Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObject *p_stack_trace) { + + // Printing an error here could result in endless recursion, so we must be careful + + MonoObject *exc = NULL; + + GDMonoUtils::StackTrace_GetFrames st_get_frames = CACHED_METHOD_THUNK(System_Diagnostics_StackTrace, GetFrames); + MonoArray *frames = st_get_frames(p_stack_trace, &exc); + + if (exc) { + GDMonoUtils::print_unhandled_exception(exc, true /* fail silently to avoid endless recursion */); + return Vector<StackInfo>(); + } + + int frame_count = mono_array_length(frames); + + if (frame_count <= 0) + return Vector<StackInfo>(); + + GDMonoUtils::DebugUtils_StackFrameInfo get_sf_info = CACHED_METHOD_THUNK(DebuggingUtils, GetStackFrameInfo); + + Vector<StackInfo> si; + si.resize(frame_count); + + for (int i = 0; i < frame_count; i++) { + StackInfo &sif = si[i]; + MonoObject *frame = mono_array_get(frames, MonoObject *, i); + + MonoString *file_name; + int file_line_num; + MonoString *method_decl; + get_sf_info(frame, &file_name, &file_line_num, &method_decl, &exc); + + if (exc) { + GDMonoUtils::print_unhandled_exception(exc, true /* fail silently to avoid endless recursion */); + return Vector<StackInfo>(); + } + + // TODO + // what if the StackFrame method is null (method_decl is empty). should we skip this frame? + // can reproduce with a MissingMethodException on internal calls + + sif.file = GDMonoMarshal::mono_string_to_godot(file_name); + sif.line = file_line_num; + sif.func = GDMonoMarshal::mono_string_to_godot(method_decl); + } + + return si; +} +#endif + void CSharpLanguage::frame() { const Ref<MonoGCHandle> &task_scheduler_handle = GDMonoUtils::mono_cache.task_scheduler_handle; @@ -640,8 +745,10 @@ void CSharpLanguage::reload_assemblies_if_needed(bool p_soft_reload) { for (Map<Ref<CSharpScript>, Map<ObjectID, List<Pair<StringName, Variant> > > >::Element *E = to_reload.front(); E; E = E->next()) { Ref<CSharpScript> scr = E->key(); + scr->signals_invalidated = true; scr->exports_invalidated = true; scr->reload(p_soft_reload); + scr->update_signals(); scr->update_exports(); //restore state if saved @@ -674,8 +781,10 @@ void CSharpLanguage::reload_assemblies_if_needed(bool p_soft_reload) { //if instance states were saved, set them! } - if (Engine::get_singleton()->is_editor_hint()) + if (Engine::get_singleton()->is_editor_hint()) { EditorNode::get_singleton()->get_property_editor()->update_tree(); + NodeDock::singleton->update_lists(); + } } #endif @@ -870,19 +979,6 @@ void CSharpLanguage::free_instance_binding_data(void *p_data) { #endif } -void CSharpInstance::_ml_call_reversed(MonoObject *p_mono_object, GDMonoClass *p_klass, const StringName &p_method, const Variant **p_args, int p_argcount) { - - GDMonoClass *base = p_klass->get_parent_class(); - if (base && base != script->native) - _ml_call_reversed(p_mono_object, base, p_method, p_args, p_argcount); - - GDMonoMethod *method = p_klass->get_method(p_method, p_argcount); - - if (method) { - method->invoke(p_mono_object, p_args); - } -} - CSharpInstance *CSharpInstance::create_for_managed_type(Object *p_owner, CSharpScript *p_script, const Ref<MonoGCHandle> &p_gchandle) { CSharpInstance *instance = memnew(CSharpInstance); @@ -913,19 +1009,23 @@ bool CSharpInstance::set(const StringName &p_name, const Variant &p_value) { ERR_FAIL_COND_V(!script.is_valid(), false); + MonoObject *mono_object = get_mono_object(); + ERR_FAIL_NULL_V(mono_object, false); + GDMonoClass *top = script->script_class; while (top && top != script->native) { GDMonoField *field = script->script_class->get_field(p_name); if (field) { - MonoObject *mono_object = get_mono_object(); - - ERR_EXPLAIN("Reference has been garbage collected?"); - ERR_FAIL_NULL_V(mono_object, false); + field->set_value_from_variant(mono_object, p_value); + return true; + } - field->set_value(mono_object, p_value); + GDMonoProperty *property = script->script_class->get_property(p_name); + if (property) { + property->set_value(mono_object, GDMonoMarshal::variant_to_mono_object(p_value)); return true; } @@ -934,20 +1034,21 @@ bool CSharpInstance::set(const StringName &p_name, const Variant &p_value) { // Call _set - Variant name = p_name; - const Variant *args[2] = { &name, &p_value }; - - MonoObject *mono_object = get_mono_object(); top = script->script_class; while (top && top != script->native) { GDMonoMethod *method = top->get_method(CACHED_STRING_NAME(_set), 2); if (method) { + Variant name = p_name; + const Variant *args[2] = { &name, &p_value }; + MonoObject *ret = method->invoke(mono_object, args); if (ret && GDMonoMarshal::unbox<MonoBoolean>(ret) == true) return true; + + break; } top = top->get_parent_class(); @@ -960,36 +1061,56 @@ bool CSharpInstance::get(const StringName &p_name, Variant &r_ret) const { ERR_FAIL_COND_V(!script.is_valid(), false); + MonoObject *mono_object = get_mono_object(); + ERR_FAIL_NULL_V(mono_object, false); + GDMonoClass *top = script->script_class; while (top && top != script->native) { GDMonoField *field = top->get_field(p_name); if (field) { - MonoObject *mono_object = get_mono_object(); + MonoObject *value = field->get_value(mono_object); + r_ret = GDMonoMarshal::mono_object_to_variant(value); + return true; + } - ERR_EXPLAIN("Reference has been garbage collected?"); - ERR_FAIL_NULL_V(mono_object, false); + GDMonoProperty *property = top->get_property(p_name); - MonoObject *value = field->get_value(mono_object); - r_ret = GDMonoMarshal::mono_object_to_variant(value, field->get_type()); + if (property) { + MonoObject *exc = NULL; + MonoObject *value = property->get_value(mono_object, &exc); + if (exc) { + r_ret = Variant(); + GDMonoUtils::print_unhandled_exception(exc); + } else { + r_ret = GDMonoMarshal::mono_object_to_variant(value); + } return true; } - // Call _get + top = top->get_parent_class(); + } + + // Call _get + top = script->script_class; + + while (top && top != script->native) { GDMonoMethod *method = top->get_method(CACHED_STRING_NAME(_get), 1); if (method) { Variant name = p_name; const Variant *args[1] = { &name }; - MonoObject *ret = method->invoke(get_mono_object(), args); + MonoObject *ret = method->invoke(mono_object, args); if (ret) { r_ret = GDMonoMarshal::mono_object_to_variant(ret); return true; } + + break; } top = top->get_parent_class(); @@ -1027,7 +1148,7 @@ bool CSharpInstance::has_method(const StringName &p_method) const { GDMonoClass *top = script->script_class; while (top && top != script->native) { - if (top->has_method(p_method)) { + if (top->has_fetched_method_unknown_params(p_method)) { return true; } @@ -1041,10 +1162,13 @@ Variant CSharpInstance::call(const StringName &p_method, const Variant **p_args, MonoObject *mono_object = get_mono_object(); - ERR_FAIL_NULL_V(mono_object, Variant()); + if (!mono_object) { + r_error.error = Variant::CallError::CALL_ERROR_INSTANCE_IS_NULL; + ERR_FAIL_V(Variant()); + } if (!script.is_valid()) - return Variant(); + ERR_FAIL_V(Variant()); GDMonoClass *top = script->script_class; @@ -1054,8 +1178,10 @@ Variant CSharpInstance::call(const StringName &p_method, const Variant **p_args, if (method) { MonoObject *return_value = method->invoke(mono_object, p_args); + r_error.error = Variant::CallError::CALL_OK; + if (return_value) { - return GDMonoMarshal::mono_object_to_variant(return_value, method->get_return_type()); + return GDMonoMarshal::mono_object_to_variant(return_value); } else { return Variant(); } @@ -1087,8 +1213,10 @@ void CSharpInstance::_call_multilevel(MonoObject *p_mono_object, const StringNam while (top && top != script->native) { GDMonoMethod *method = top->get_method(p_method, p_argcount); - if (method) + if (method) { method->invoke(p_mono_object, p_args); + return; + } top = top->get_parent_class(); } @@ -1096,13 +1224,9 @@ void CSharpInstance::_call_multilevel(MonoObject *p_mono_object, const StringNam void CSharpInstance::call_multilevel_reversed(const StringName &p_method, const Variant **p_args, int p_argcount) { - if (script.is_valid()) { - MonoObject *mono_object = get_mono_object(); - - ERR_FAIL_NULL(mono_object); + // Sorry, the method is the one that controls the call order - _ml_call_reversed(mono_object, script->script_class, p_method, p_args, p_argcount); - } + call_multilevel(p_method, p_args, p_argcount); } void CSharpInstance::_reference_owner_unsafe() { @@ -1186,24 +1310,29 @@ bool CSharpInstance::refcount_decremented() { return ref_dying; } +ScriptInstance::RPCMode CSharpInstance::_member_get_rpc_mode(GDMonoClassMember *p_member) const { + + if (p_member->has_attribute(CACHED_CLASS(RemoteAttribute))) + return RPC_MODE_REMOTE; + if (p_member->has_attribute(CACHED_CLASS(SyncAttribute))) + return RPC_MODE_SYNC; + if (p_member->has_attribute(CACHED_CLASS(MasterAttribute))) + return RPC_MODE_MASTER; + if (p_member->has_attribute(CACHED_CLASS(SlaveAttribute))) + return RPC_MODE_SLAVE; + + return RPC_MODE_DISABLED; +} + ScriptInstance::RPCMode CSharpInstance::get_rpc_mode(const StringName &p_method) const { GDMonoClass *top = script->script_class; while (top && top != script->native) { - GDMonoMethod *method = top->get_method(p_method); - - if (method) { // TODO should we reject static methods? - // TODO cache result - if (method->has_attribute(CACHED_CLASS(RemoteAttribute))) - return RPC_MODE_REMOTE; - if (method->has_attribute(CACHED_CLASS(SyncAttribute))) - return RPC_MODE_SYNC; - if (method->has_attribute(CACHED_CLASS(MasterAttribute))) - return RPC_MODE_MASTER; - if (method->has_attribute(CACHED_CLASS(SlaveAttribute))) - return RPC_MODE_SLAVE; - } + GDMonoMethod *method = top->get_fetched_method_unknown_params(p_method); + + if (method && !method->is_static()) + return _member_get_rpc_mode(method); top = top->get_parent_class(); } @@ -1218,17 +1347,13 @@ ScriptInstance::RPCMode CSharpInstance::get_rset_mode(const StringName &p_variab while (top && top != script->native) { GDMonoField *field = top->get_field(p_variable); - if (field) { // TODO should we reject static fields? - // TODO cache result - if (field->has_attribute(CACHED_CLASS(RemoteAttribute))) - return RPC_MODE_REMOTE; - if (field->has_attribute(CACHED_CLASS(SyncAttribute))) - return RPC_MODE_SYNC; - if (field->has_attribute(CACHED_CLASS(MasterAttribute))) - return RPC_MODE_MASTER; - if (field->has_attribute(CACHED_CLASS(SlaveAttribute))) - return RPC_MODE_SLAVE; - } + if (field && !field->is_static()) + return _member_get_rpc_mode(field); + + GDMonoProperty *property = top->get_property(p_variable); + + if (property && !property->is_static()) + return _member_get_rpc_mode(property); top = top->get_parent_class(); } @@ -1353,7 +1478,7 @@ bool CSharpScript::_update_exports() { // We are creating a temporary new instance of the class here to get the default value // TODO Workaround. Should be replaced with IL opcodes analysis - MonoObject *tmp_object = mono_object_new(SCRIPTS_DOMAIN, script_class->get_raw()); + MonoObject *tmp_object = mono_object_new(SCRIPTS_DOMAIN, script_class->get_mono_ptr()); if (tmp_object) { CACHED_FIELD(GodotObject, ptr)->set_value_raw(tmp_object, tmp_object); // FIXME WTF is this workaround @@ -1376,65 +1501,55 @@ bool CSharpScript::_update_exports() { GDMonoClass *top = script_class; while (top && top != native) { + PropertyInfo prop_info; + bool exported; + const Vector<GDMonoField *> &fields = top->get_all_fields(); for (int i = fields.size() - 1; i >= 0; i--) { GDMonoField *field = fields[i]; - if (field->is_static()) { - if (field->has_attribute(CACHED_CLASS(ExportAttribute))) - ERR_PRINTS("Cannot export field because it is static: " + top->get_full_name() + "." + field->get_name()); - continue; - } - - String name = field->get_name(); - StringName cname = name; + if (_get_member_export(top, field, prop_info, exported)) { + StringName name = field->get_name(); - if (member_info.has(cname)) - continue; + if (exported) { + member_info[name] = prop_info; + exported_members_cache.push_front(prop_info); - ManagedType field_type = field->get_type(); - Variant::Type type = GDMonoMarshal::managed_to_variant_type(field_type); + if (tmp_object) { + exported_members_defval_cache[name] = GDMonoMarshal::mono_object_to_variant(field->get_value(tmp_object)); + } + } else { + member_info[name] = prop_info; + } + } + } - if (field->has_attribute(CACHED_CLASS(ExportAttribute))) { - // Field has Export attribute - MonoObject *attr = field->get_attribute(CACHED_CLASS(ExportAttribute)); + const Vector<GDMonoProperty *> &properties = top->get_all_properties(); - PropertyHint hint; - String hint_string; + for (int i = properties.size() - 1; i >= 0; i--) { + GDMonoProperty *property = properties[i]; - if (type == Variant::NIL) { - ERR_PRINTS("Unknown type of exported field: " + top->get_full_name() + "." + field->get_name()); - continue; - } else if (type == Variant::INT && field_type.type_encoding == MONO_TYPE_VALUETYPE && mono_class_is_enum(field_type.type_class->get_raw())) { - type = Variant::INT; - hint = PROPERTY_HINT_ENUM; + if (_get_member_export(top, property, prop_info, exported)) { + StringName name = property->get_name(); - Vector<MonoClassField *> fields = field_type.type_class->get_enum_fields(); + if (exported) { + member_info[name] = prop_info; + exported_members_cache.push_front(prop_info); - for (int i = 0; i < fields.size(); i++) { - if (i > 0) - hint_string += ","; - hint_string += mono_field_get_name(fields[i]); + if (tmp_object) { + MonoObject *exc = NULL; + MonoObject *ret = property->get_value(tmp_object, &exc); + if (exc) { + exported_members_defval_cache[name] = Variant(); + GDMonoUtils::print_unhandled_exception(exc); + } else { + exported_members_defval_cache[name] = GDMonoMarshal::mono_object_to_variant(ret); + } } - } else if (type == Variant::OBJECT && CACHED_CLASS(GodotReference)->is_assignable_from(field_type.type_class)) { - hint = PROPERTY_HINT_RESOURCE_TYPE; - hint_string = NATIVE_GDMONOCLASS_NAME(field_type.type_class); } else { - hint = PropertyHint(CACHED_FIELD(ExportAttribute, hint)->get_int_value(attr)); - hint_string = CACHED_FIELD(ExportAttribute, hintString)->get_string_value(attr); + member_info[name] = prop_info; } - - PropertyInfo prop_info = PropertyInfo(type, name, hint, hint_string, PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE); - - member_info[cname] = prop_info; - exported_members_cache.push_front(prop_info); - - if (tmp_object) { - exported_members_defval_cache[cname] = GDMonoMarshal::mono_object_to_variant(field->get_value(tmp_object)); - } - } else { - member_info[cname] = PropertyInfo(type, name, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_SCRIPT_VARIABLE); } } @@ -1458,6 +1573,147 @@ bool CSharpScript::_update_exports() { return false; } +bool CSharpScript::_update_signals() { + if (!valid) + return false; + + bool changed = false; + + if (signals_invalidated) { + signals_invalidated = false; + + GDMonoClass *top = script_class; + + _signals.clear(); + changed = true; // TODO Do a real check for change + + while (top && top != native) { + 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)) { + _signals[delegate->get_name()] = parameters; + } + } + + top = top->get_parent_class(); + } + } + + return changed; +} + +bool CSharpScript::_get_signal(GDMonoClass *p_class, GDMonoClass *p_delegate, Vector<Argument> ¶ms) { + if (p_delegate->has_attribute(CACHED_CLASS(SignalAttribute))) { + MonoType *raw_type = GDMonoClass::get_raw_type(p_delegate); + + 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); + + Vector<StringName> names; + Vector<ManagedType> types; + invoke->get_parameter_names(names); + invoke->get_parameter_types(types); + + 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_PRINTS("Unknown type of signal parameter: " + arg.name + " in " + p_class->get_full_name()); + return false; + } + + params.push_back(arg); + } + + return true; + } + } + } + + return false; +} + +#ifdef TOOLS_ENABLED +bool CSharpScript::_get_member_export(GDMonoClass *p_class, GDMonoClassMember *p_member, PropertyInfo &r_prop_info, bool &r_exported) { + + StringName name = p_member->get_name(); + + if (p_member->is_static()) { + if (p_member->has_attribute(CACHED_CLASS(ExportAttribute))) + ERR_PRINTS("Cannot export member because it is static: " + p_class->get_full_name() + "." + name.operator String()); + return false; + } + + if (member_info.has(name)) + return false; + + ManagedType type; + + if (p_member->get_member_type() == GDMonoClassMember::MEMBER_TYPE_FIELD) { + type = static_cast<GDMonoField *>(p_member)->get_type(); + } else if (p_member->get_member_type() == GDMonoClassMember::MEMBER_TYPE_PROPERTY) { + type = static_cast<GDMonoProperty *>(p_member)->get_type(); + } else { + CRASH_NOW(); + } + + Variant::Type variant_type = GDMonoMarshal::managed_to_variant_type(type); + + if (p_member->has_attribute(CACHED_CLASS(ExportAttribute))) { + if (p_member->get_member_type() == GDMonoClassMember::MEMBER_TYPE_PROPERTY) { + GDMonoProperty *property = static_cast<GDMonoProperty *>(p_member); + if (!property->has_getter() || !property->has_setter()) { + ERR_PRINTS("Cannot export property because it does not provide a getter or a setter: " + p_class->get_full_name() + "." + name.operator String()); + return false; + } + } + + MonoObject *attr = p_member->get_attribute(CACHED_CLASS(ExportAttribute)); + + PropertyHint hint; + String hint_string; + + if (variant_type == Variant::NIL) { + ERR_PRINTS("Unknown type of exported member: " + p_class->get_full_name() + "." + name.operator String()); + return false; + } else if (variant_type == Variant::INT && type.type_encoding == MONO_TYPE_VALUETYPE && mono_class_is_enum(type.type_class->get_mono_ptr())) { + variant_type = Variant::INT; + hint = PROPERTY_HINT_ENUM; + + Vector<MonoClassField *> fields = type.type_class->get_enum_fields(); + + for (int i = 0; i < fields.size(); i++) { + if (i > 0) + hint_string += ","; + hint_string += mono_field_get_name(fields[i]); + } + } else if (variant_type == Variant::OBJECT && CACHED_CLASS(GodotReference)->is_assignable_from(type.type_class)) { + hint = PROPERTY_HINT_RESOURCE_TYPE; + hint_string = NATIVE_GDMONOCLASS_NAME(type.type_class); + } else { + hint = PropertyHint(CACHED_FIELD(ExportAttribute, hint)->get_int_value(attr)); + hint_string = CACHED_FIELD(ExportAttribute, hintString)->get_string_value(attr); + } + + r_prop_info = PropertyInfo(variant_type, name.operator String(), hint, hint_string, PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE); + r_exported = true; + } else { + r_prop_info = PropertyInfo(variant_type, name.operator String(), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_SCRIPT_VARIABLE); + r_exported = false; + } + + return true; +} +#endif + void CSharpScript::_clear() { tool = false; @@ -1479,7 +1735,7 @@ Variant CSharpScript::call(const StringName &p_method, const Variant **p_args, i MonoObject *result = method->invoke(NULL, p_args); if (result) { - return GDMonoMarshal::mono_object_to_variant(result, method->get_return_type()); + return GDMonoMarshal::mono_object_to_variant(result); } else { return Variant(); } @@ -1526,7 +1782,7 @@ bool CSharpScript::_set(const StringName &p_name, const Variant &p_value) { void CSharpScript::_get_property_list(List<PropertyInfo> *p_properties) const { - p_properties->push_back(PropertyInfo(Variant::STRING, CSharpLanguage::singleton->string_names._script_source, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + p_properties->push_back(PropertyInfo(Variant::STRING, CSharpLanguage::singleton->string_names._script_source, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); } void CSharpScript::_bind_methods() { @@ -1626,7 +1882,7 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg /* STEP 2, INITIALIZE AND CONSTRUCT */ - MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, script_class->get_raw()); + MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, script_class->get_mono_ptr()); if (!mono_object) { instance->script = Ref<CSharpScript>(); @@ -1698,20 +1954,27 @@ Variant CSharpScript::_new(const Variant **p_args, int p_argcount, Variant::Call ScriptInstance *CSharpScript::instance_create(Object *p_this) { - if (!valid) - return NULL; + if (!script_class) { + ERR_EXPLAIN("Cannot find class " + name + " for script " + get_path()); + ERR_FAIL_V(NULL); + } + + ERR_FAIL_COND_V(!valid, NULL); if (!tool && !ScriptServer::is_scripting_enabled()) { #ifdef TOOLS_ENABLED PlaceHolderScriptInstance *si = memnew(PlaceHolderScriptInstance(CSharpLanguage::get_singleton(), Ref<Script>(this), p_this)); placeholders.insert(si); _update_exports(); + _update_signals(); return si; #else return NULL; #endif } + update_signals(); + if (native) { String native_name = native->get_name(); if (!ClassDB::is_parent_class(p_this->get_class_name(), native_name)) { @@ -1764,7 +2027,7 @@ void CSharpScript::set_source_code(const String &p_code) { bool CSharpScript::has_method(const StringName &p_method) const { - return script_class->has_method(p_method); + return script_class->has_fetched_method_unknown_params(p_method); } Error CSharpScript::reload(bool p_keep_state) { @@ -1786,20 +2049,15 @@ Error CSharpScript::reload(bool p_keep_state) { if (project_assembly) { script_class = project_assembly->get_object_derived_class(name); - if (!script_class) { - ERR_PRINTS("Cannot find class " + name + " for script " + get_path()); - } + valid = script_class != NULL; + + if (script_class) { #ifdef DEBUG_ENABLED - else if (OS::get_singleton()->is_stdout_verbose()) { OS::get_singleton()->print(String("Found class " + script_class->get_namespace() + "." + script_class->get_name() + " for script " + get_path() + "\n") .utf8()); - } #endif - valid = script_class != NULL; - - if (script_class) { tool = script_class->has_attribute(CACHED_CLASS(ToolAttribute)); native = GDMonoUtils::get_class_native_base(script_class); @@ -1875,6 +2133,31 @@ void CSharpScript::update_exports() { #endif } +bool CSharpScript::has_script_signal(const StringName &p_signal) const { + if (_signals.has(p_signal)) + return true; + + return false; +} + +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; + + 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); + } + r_signals->push_back(mi); + } +} + +void CSharpScript::update_signals() { + _update_signals(); +} + Ref<Script> CSharpScript::get_base_script() const { // TODO search in metadata file once we have it, not important any way? @@ -1939,6 +2222,7 @@ CSharpScript::CSharpScript() : #ifdef TOOLS_ENABLED source_changed_cache = false; exports_invalidated = true; + signals_invalidated = true; #endif _resource_path_changed(); @@ -2003,7 +2287,9 @@ RES ResourceFormatLoaderCSharpScript::load(const String &p_path, const String &p CRASH_COND(mono_domain_get() == NULL); #endif -#else +#endif + +#ifdef TOOLS_ENABLED if (Engine::get_singleton()->is_editor_hint() && mono_domain_get() == NULL) { CRASH_COND(Thread::get_caller_id() == Thread::get_main_id()); @@ -2012,14 +2298,20 @@ RES ResourceFormatLoaderCSharpScript::load(const String &p_path, const String &p // because this may be called by one of the editor's worker threads. // Attach this thread temporarily to reload the script. - MonoThread *mono_thread = mono_thread_attach(SCRIPTS_DOMAIN); - CRASH_COND(mono_thread == NULL); + if (SCRIPTS_DOMAIN) { + MonoThread *mono_thread = mono_thread_attach(SCRIPTS_DOMAIN); + CRASH_COND(mono_thread == NULL); + script->reload(); + mono_thread_detach(mono_thread); + } + + } else { // just reload it normally +#endif script->reload(); - mono_thread_detach(mono_thread); - } else // just reload it normally +#ifdef TOOLS_ENABLED + } #endif - script->reload(); if (r_error) *r_error = OK; |