diff options
Diffstat (limited to 'modules/mono')
-rw-r--r-- | modules/mono/csharp_script.cpp | 33 | ||||
-rw-r--r-- | modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj | 1 | ||||
-rw-r--r-- | modules/mono/editor/GodotSharpTools/Project/IdentifierUtils.cs | 199 | ||||
-rw-r--r-- | modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs | 5 | ||||
-rw-r--r-- | modules/mono/editor/script_class_parser.cpp | 25 | ||||
-rw-r--r-- | modules/mono/glue/Managed/Files/Array.cs | 13 | ||||
-rw-r--r-- | modules/mono/glue/collections_glue.cpp | 5 | ||||
-rw-r--r-- | modules/mono/glue/collections_glue.h | 2 |
8 files changed, 265 insertions, 18 deletions
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index a7ac7f46c5..47be3a9959 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -1528,6 +1528,15 @@ MonoObject *CSharpInstance::_internal_new_managed() { CRASH_COND(!gchandle.is_valid()); #endif + // Search the constructor first, to fail with an error if it's not found before allocating anything else. + GDMonoMethod *ctor = script->script_class->get_method(CACHED_STRING_NAME(dotctor), 0); + if (ctor == NULL) { + ERR_PRINTS("Cannot create script instance because the class does not define a default constructor: " + script->get_path()); + + ERR_EXPLAIN("Constructor not found"); + ERR_FAIL_V(NULL); + } + CSharpLanguage::get_singleton()->release_script_gchandle(gchandle); ERR_FAIL_NULL_V(owner, NULL); @@ -1557,7 +1566,6 @@ MonoObject *CSharpInstance::_internal_new_managed() { CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, owner); // Construct - GDMonoMethod *ctor = script->script_class->get_method(CACHED_STRING_NAME(dotctor), 0); ctor->invoke_raw(mono_object, NULL); return mono_object; @@ -1900,13 +1908,21 @@ bool CSharpScript::_update_exports() { MonoObject *tmp_object = mono_object_new(SCRIPTS_DOMAIN, script_class->get_mono_ptr()); if (!tmp_object) { - ERR_PRINT("Failed to create temporary MonoObject"); + ERR_PRINT("Failed to allocate temporary MonoObject"); return false; } uint32_t tmp_pinned_gchandle = MonoGCHandle::new_strong_handle_pinned(tmp_object); // pin it (not sure if needed) GDMonoMethod *ctor = script_class->get_method(CACHED_STRING_NAME(dotctor), 0); + + if (ctor == NULL) { + ERR_PRINTS("Cannot construct temporary MonoObject because the class does not define a default constructor: " + get_path()); + + ERR_EXPLAIN("Constructor not found"); + ERR_FAIL_V(NULL); + } + MonoException *ctor_exc = NULL; ctor->invoke(tmp_object, NULL, &ctor_exc); @@ -2387,6 +2403,18 @@ StringName CSharpScript::get_instance_base_type() const { CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_isref, Variant::CallError &r_error) { /* STEP 1, CREATE */ + + // Search the constructor first, to fail with an error if it's not found before allocating anything else. + GDMonoMethod *ctor = script_class->get_method(CACHED_STRING_NAME(dotctor), p_argcount); + if (ctor == NULL) { + if (p_argcount == 0) { + ERR_PRINTS("Cannot create script instance because the class does not define a default constructor: " + get_path()); + } + + ERR_EXPLAIN("Constructor not found"); + ERR_FAIL_V(NULL); + } + Ref<Reference> ref; if (p_isref) { // Hold it alive. Important if we have to dispose a script instance binding before creating the CSharpInstance. @@ -2453,7 +2481,6 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, instance->owner); // Construct - GDMonoMethod *ctor = script_class->get_method(CACHED_STRING_NAME(dotctor), p_argcount); ctor->invoke(mono_object, p_args); /* STEP 3, PARTY */ diff --git a/modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj b/modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj index 9a5dd24bb1..2871c041f5 100644 --- a/modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj +++ b/modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj @@ -41,6 +41,7 @@ <Compile Include="Build\BuildSystem.cs" /> <Compile Include="Editor\MonoDevelopInstance.cs" /> <Compile Include="Project\ProjectExtensions.cs" /> + <Compile Include="Project\IdentifierUtils.cs" /> <Compile Include="Project\ProjectGenerator.cs" /> <Compile Include="Project\ProjectUtils.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> diff --git a/modules/mono/editor/GodotSharpTools/Project/IdentifierUtils.cs b/modules/mono/editor/GodotSharpTools/Project/IdentifierUtils.cs new file mode 100644 index 0000000000..83e2d2cf8d --- /dev/null +++ b/modules/mono/editor/GodotSharpTools/Project/IdentifierUtils.cs @@ -0,0 +1,199 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; + +namespace GodotSharpTools.Project +{ + public static class IdentifierUtils + { + public static string SanitizeQualifiedIdentifier(string qualifiedIdentifier, bool allowEmptyIdentifiers) + { + if (string.IsNullOrEmpty(qualifiedIdentifier)) + throw new ArgumentException($"{nameof(qualifiedIdentifier)} cannot be empty", nameof(qualifiedIdentifier)); + + string[] identifiers = qualifiedIdentifier.Split(new[] { '.' }); + + for (int i = 0; i < identifiers.Length; i++) + { + identifiers[i] = SanitizeIdentifier(identifiers[i], allowEmpty: allowEmptyIdentifiers); + } + + return string.Join(".", identifiers); + } + + public static string SanitizeIdentifier(string identifier, bool allowEmpty) + { + if (string.IsNullOrEmpty(identifier)) + { + if (allowEmpty) + return "Empty"; // Default value for empty identifiers + + throw new ArgumentException($"{nameof(identifier)} cannot be empty if {nameof(allowEmpty)} is false", nameof(identifier)); + } + + if (identifier.Length > 511) + identifier = identifier.Substring(0, 511); + + var identifierBuilder = new StringBuilder(); + int startIndex = 0; + + if (identifier[0] == '@') + { + identifierBuilder.Append('@'); + startIndex += 1; + } + + for (int i = startIndex; i < identifier.Length; i++) + { + char @char = identifier[i]; + + switch (Char.GetUnicodeCategory(@char)) + { + case UnicodeCategory.UppercaseLetter: + case UnicodeCategory.LowercaseLetter: + case UnicodeCategory.TitlecaseLetter: + case UnicodeCategory.ModifierLetter: + case UnicodeCategory.LetterNumber: + case UnicodeCategory.OtherLetter: + identifierBuilder.Append(@char); + break; + case UnicodeCategory.NonSpacingMark: + case UnicodeCategory.SpacingCombiningMark: + case UnicodeCategory.ConnectorPunctuation: + case UnicodeCategory.DecimalDigitNumber: + // Identifiers may start with underscore + if (identifierBuilder.Length > startIndex || @char == '_') + identifierBuilder.Append(@char); + break; + default: + break; + } + } + + if (identifierBuilder.Length == startIndex) + { + // All characters were invalid so now it's empty. Fill it with something. + identifierBuilder.Append("Empty"); + } + + identifier = identifierBuilder.ToString(); + + if (identifier[0] != '@' && IsKeyword(identifier, anyDoubleUnderscore: true)) + identifier = '@' + identifier; + + return identifier; + } + + static bool IsKeyword(string value, bool anyDoubleUnderscore) + { + // Identifiers that start with double underscore are meant to be used for reserved keywords. + // Only existing keywords are enforced, but it may be useful to forbid any identifier + // that begins with double underscore to prevent issues with future C# versions. + if (anyDoubleUnderscore) + { + if (value.Length > 2 && value[0] == '_' && value[1] == '_' && value[2] != '_') + return true; + } + else + { + if (_doubleUnderscoreKeywords.Contains(value)) + return true; + } + + return _keywords.Contains(value); + } + + static HashSet<string> _doubleUnderscoreKeywords = new HashSet<string> + { + "__arglist", + "__makeref", + "__reftype", + "__refvalue", + }; + + static HashSet<string> _keywords = new HashSet<string> + { + "as", + "do", + "if", + "in", + "is", + "for", + "int", + "new", + "out", + "ref", + "try", + "base", + "bool", + "byte", + "case", + "char", + "else", + "enum", + "goto", + "lock", + "long", + "null", + "this", + "true", + "uint", + "void", + "break", + "catch", + "class", + "const", + "event", + "false", + "fixed", + "float", + "sbyte", + "short", + "throw", + "ulong", + "using", + "where", + "while", + "yield", + "double", + "extern", + "object", + "params", + "public", + "return", + "sealed", + "sizeof", + "static", + "string", + "struct", + "switch", + "typeof", + "unsafe", + "ushort", + "checked", + "decimal", + "default", + "finally", + "foreach", + "partial", + "private", + "virtual", + "abstract", + "continue", + "delegate", + "explicit", + "implicit", + "internal", + "operator", + "override", + "readonly", + "volatile", + "interface", + "namespace", + "protected", + "unchecked", + "stackalloc", + }; + } +} diff --git a/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs b/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs index 9135006172..89279c69a6 100644 --- a/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs +++ b/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs @@ -140,6 +140,9 @@ namespace GodotSharpTools.Project public static ProjectRootElement CreateLibraryProject(string name, out ProjectPropertyGroupElement mainGroup) { + if (string.IsNullOrEmpty(name)) + throw new ArgumentException($"{nameof(name)} cannot be empty", nameof(name)); + var root = ProjectRootElement.Create(); root.DefaultTargets = "Build"; @@ -149,7 +152,7 @@ namespace GodotSharpTools.Project mainGroup.AddProperty("ProjectGuid", "{" + Guid.NewGuid().ToString().ToUpper() + "}"); mainGroup.AddProperty("OutputType", "Library"); mainGroup.AddProperty("OutputPath", Path.Combine("bin", "$(Configuration)")); - mainGroup.AddProperty("RootNamespace", name); + mainGroup.AddProperty("RootNamespace", IdentifierUtils.SanitizeQualifiedIdentifier(name, allowEmptyIdentifiers: true)); mainGroup.AddProperty("AssemblyName", name); mainGroup.AddProperty("TargetFrameworkVersion", "v4.5"); diff --git a/modules/mono/editor/script_class_parser.cpp b/modules/mono/editor/script_class_parser.cpp index fcc58c22e8..6b2ec5cc20 100644 --- a/modules/mono/editor/script_class_parser.cpp +++ b/modules/mono/editor/script_class_parser.cpp @@ -322,6 +322,15 @@ Error ScriptClassParser::_parse_type_full_name(String &r_full_name) { r_full_name += String(value); + if (code[idx] == '<') { + idx++; + + // We don't mind if the base is generic, but we skip it any ways since this information is not needed + Error err = _skip_generic_type_params(); + if (err) + return err; + } + if (code[idx] != '.') // We only want to take the next token if it's a period return OK; @@ -344,16 +353,6 @@ Error ScriptClassParser::_parse_class_base(Vector<String> &r_base) { Token tk = get_token(); - bool generic = false; - if (tk == TK_OP_LESS) { - err = _skip_generic_type_params(); - if (err) - return err; - // We don't add it to the base list if it's generic - generic = true; - tk = get_token(); - } - if (tk == TK_COMMA) { err = _parse_class_base(r_base); if (err) @@ -373,9 +372,7 @@ Error ScriptClassParser::_parse_class_base(Vector<String> &r_base) { return ERR_PARSE_ERROR; } - if (!generic) { - r_base.push_back(name); - } + r_base.push_back(name); return OK; } @@ -567,7 +564,7 @@ Error ScriptClassParser::parse(const String &p_code) { if (full_name.length()) full_name += "."; full_name += class_decl.name; - OS::get_singleton()->print("%s", String("Ignoring generic class declaration: " + class_decl.name).utf8().get_data()); + OS::get_singleton()->print("Ignoring generic class declaration: %s\n", class_decl.name.utf8().get_data()); } } } else if (tk == TK_IDENTIFIER && String(value) == "struct") { diff --git a/modules/mono/glue/Managed/Files/Array.cs b/modules/mono/glue/Managed/Files/Array.cs index 9c804eba64..1ee64f3b71 100644 --- a/modules/mono/glue/Managed/Files/Array.cs +++ b/modules/mono/glue/Managed/Files/Array.cs @@ -155,6 +155,11 @@ namespace Godot.Collections godot_icall_Array_RemoveAt(GetPtr(), index); } + public Error Resize(int newSize) + { + return godot_icall_Array_Resize(GetPtr(), newSize); + } + IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); @@ -203,6 +208,9 @@ namespace Godot.Collections internal extern static void godot_icall_Array_RemoveAt(IntPtr ptr, int index); [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static Error godot_icall_Array_Resize(IntPtr ptr, int newSize); + + [MethodImpl(MethodImplOptions.InternalCall)] internal extern static void godot_icall_Array_Generic_GetElementTypeInfo(Type elemType, out int elemTypeEncoding, out IntPtr elemTypeClass); } @@ -339,6 +347,11 @@ namespace Godot.Collections objectArray.RemoveAt(index); } + public Error Resize(int newSize) + { + return objectArray.Resize(newSize); + } + IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); diff --git a/modules/mono/glue/collections_glue.cpp b/modules/mono/glue/collections_glue.cpp index d905810d66..1065ff0868 100644 --- a/modules/mono/glue/collections_glue.cpp +++ b/modules/mono/glue/collections_glue.cpp @@ -130,6 +130,10 @@ void godot_icall_Array_RemoveAt(Array *ptr, int index) { ptr->remove(index); } +Error godot_icall_Array_Resize(Array *ptr, int new_size) { + return ptr->resize(new_size); +} + void godot_icall_Array_Generic_GetElementTypeInfo(MonoReflectionType *refltype, uint32_t *type_encoding, GDMonoClass **type_class) { MonoType *elem_type = mono_reflection_type_get_type(refltype); @@ -274,6 +278,7 @@ void godot_register_collections_icalls() { mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Insert", (void *)godot_icall_Array_Insert); mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Remove", (void *)godot_icall_Array_Remove); mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_RemoveAt", (void *)godot_icall_Array_RemoveAt); + mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Resize", (void *)godot_icall_Array_Resize); mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Generic_GetElementTypeInfo", (void *)godot_icall_Array_Generic_GetElementTypeInfo); mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Ctor", (void *)godot_icall_Dictionary_Ctor); diff --git a/modules/mono/glue/collections_glue.h b/modules/mono/glue/collections_glue.h index 52ca98b7f9..c0056d3bce 100644 --- a/modules/mono/glue/collections_glue.h +++ b/modules/mono/glue/collections_glue.h @@ -67,6 +67,8 @@ bool godot_icall_Array_Remove(Array *ptr, MonoObject *item); void godot_icall_Array_RemoveAt(Array *ptr, int index); +Error godot_icall_Array_Resize(Array *ptr, int new_size); + void godot_icall_Array_Generic_GetElementTypeInfo(MonoReflectionType *refltype, uint32_t *type_encoding, GDMonoClass **type_class); // Dictionary |