diff options
author | Ignacio Etcheverry <ignalfonsore@gmail.com> | 2021-03-06 00:12:42 +0100 |
---|---|---|
committer | Ignacio Etcheverry <ignalfonsore@gmail.com> | 2021-03-06 21:50:32 +0100 |
commit | e2afe700f62aaa503daed0519ff88b0aa1a559c1 (patch) | |
tree | 8bedb525e7a671531138d5f15ad2b27c97069a58 /modules/mono/mono_gd | |
parent | d4191e48c5f4af920e87a70fe3c5ae9219a3332b (diff) |
Add C# source generator for a new ScriptPath attribute
This source generator adds a newly introduced attribute,
`ScriptPath` to all classes that:
- Are top-level classes (not inner/nested).
- Have the `partial` modifier.
- Inherit `Godot.Object`.
- The class name matches the file name.
A build error is thrown if the generator finds a class that meets these
conditions but is not declared `partial`, unless the class is annotated
with the `DisableGodotGenerators` attribute.
We also generate an `AssemblyHasScripts` assembly attribute which Godot
uses to get all the script classes in the assembly, eliminating the need
for Godot to search them. We can also avoid searching in assemblies that
don't have this attribute. This will be good for performance in the
future once we support multiple assemblies with Godot script classes.
This is an example of what the generated code looks like:
```
using Godot;
namespace Foo {
[ScriptPathAttribute("res://Player.cs")]
// Multiple partial declarations are allowed
[ScriptPathAttribute("res://Foo/Player.cs")]
partial class Player {}
}
[assembly:AssemblyHasScripts(new System.Type[] { typeof(Foo.Player) })]
```
The new attributes replace script metadata which we were generating by
determining the namespace of script classes with a very simple parser.
This fixes several issues with the old approach related to parser
errors and conditional compilation.
It also makes the task part of the MSBuild project build, rather than
a separate step executed by the Godot editor.
Diffstat (limited to 'modules/mono/mono_gd')
-rw-r--r-- | modules/mono/mono_gd/gd_mono.cpp | 1 | ||||
-rw-r--r-- | modules/mono/mono_gd/gd_mono_assembly.cpp | 103 | ||||
-rw-r--r-- | modules/mono/mono_gd/gd_mono_assembly.h | 13 | ||||
-rw-r--r-- | modules/mono/mono_gd/gd_mono_cache.cpp | 10 | ||||
-rw-r--r-- | modules/mono/mono_gd/gd_mono_cache.h | 5 |
5 files changed, 63 insertions, 69 deletions
diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index 43a39a4966..560788fb3a 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -1006,6 +1006,7 @@ bool GDMono::_load_project_assembly() { if (success) { mono_assembly_set_main(project_assembly->get_assembly()); + CSharpLanguage::get_singleton()->lookup_scripts_in_assembly(project_assembly); } return success; diff --git a/modules/mono/mono_gd/gd_mono_assembly.cpp b/modules/mono/mono_gd/gd_mono_assembly.cpp index 1fe06bfbee..a1556bace5 100644 --- a/modules/mono/mono_gd/gd_mono_assembly.cpp +++ b/modules/mono/mono_gd/gd_mono_assembly.cpp @@ -345,6 +345,45 @@ String GDMonoAssembly::get_path() const { return String::utf8(mono_image_get_filename(image)); } +bool GDMonoAssembly::has_attribute(GDMonoClass *p_attr_class) { +#ifdef DEBUG_ENABLED + ERR_FAIL_NULL_V(p_attr_class, false); +#endif + + if (!attrs_fetched) { + fetch_attributes(); + } + + if (!attributes) { + return false; + } + + return mono_custom_attrs_has_attr(attributes, p_attr_class->get_mono_ptr()); +} + +MonoObject *GDMonoAssembly::get_attribute(GDMonoClass *p_attr_class) { +#ifdef DEBUG_ENABLED + ERR_FAIL_NULL_V(p_attr_class, nullptr); +#endif + + if (!attrs_fetched) { + fetch_attributes(); + } + + if (!attributes) { + return nullptr; + } + + return mono_custom_attrs_get_attr(attributes, p_attr_class->get_mono_ptr()); +} + +void GDMonoAssembly::fetch_attributes() { + ERR_FAIL_COND(attributes != nullptr); + + attributes = mono_custom_attrs_from_assembly(assembly); + attrs_fetched = true; +} + GDMonoClass *GDMonoAssembly::get_class(const StringName &p_namespace, const StringName &p_name) { ERR_FAIL_NULL_V(image, nullptr); @@ -390,70 +429,6 @@ GDMonoClass *GDMonoAssembly::get_class(MonoClass *p_mono_class) { return wrapped_class; } -GDMonoClass *GDMonoAssembly::get_object_derived_class(const StringName &p_class) { - GDMonoClass *match = nullptr; - - if (gdobject_class_cache_updated) { - Map<StringName, GDMonoClass *>::Element *result = gdobject_class_cache.find(p_class); - - if (result) { - match = result->get(); - } - } else { - List<GDMonoClass *> nested_classes; - - int rows = mono_image_get_table_rows(image, MONO_TABLE_TYPEDEF); - - for (int i = 1; i < rows; i++) { - MonoClass *mono_class = mono_class_get(image, (i + 1) | MONO_TOKEN_TYPE_DEF); - - if (!mono_class_is_assignable_from(CACHED_CLASS_RAW(GodotObject), mono_class)) { - continue; - } - - GDMonoClass *current = get_class(mono_class); - - if (!current) { - continue; - } - - nested_classes.push_back(current); - - if (!match && current->get_name() == p_class) { - match = current; - } - - while (!nested_classes.is_empty()) { - GDMonoClass *current_nested = nested_classes.front()->get(); - nested_classes.pop_front(); - - void *iter = nullptr; - - while (true) { - MonoClass *raw_nested = mono_class_get_nested_types(current_nested->get_mono_ptr(), &iter); - - if (!raw_nested) { - break; - } - - GDMonoClass *nested_class = get_class(raw_nested); - - if (nested_class) { - gdobject_class_cache.insert(nested_class->get_name(), nested_class); - nested_classes.push_back(nested_class); - } - } - } - - gdobject_class_cache.insert(current->get_name(), current); - } - - gdobject_class_cache_updated = true; - } - - return match; -} - GDMonoAssembly *GDMonoAssembly::load(const String &p_name, MonoAssemblyName *p_aname, bool p_refonly, const Vector<String> &p_search_dirs) { if (GDMono::get_singleton()->get_corlib_assembly() && (p_name == "mscorlib" || p_name == "mscorlib.dll")) { return GDMono::get_singleton()->get_corlib_assembly(); diff --git a/modules/mono/mono_gd/gd_mono_assembly.h b/modules/mono/mono_gd/gd_mono_assembly.h index 350fcf3210..6191c491f4 100644 --- a/modules/mono/mono_gd/gd_mono_assembly.h +++ b/modules/mono/mono_gd/gd_mono_assembly.h @@ -71,13 +71,13 @@ class GDMonoAssembly { MonoImage *image; MonoAssembly *assembly; + bool attrs_fetched = false; + MonoCustomAttrInfo *attributes = nullptr; + #ifdef GD_MONO_HOT_RELOAD uint64_t modified_time = 0; #endif - bool gdobject_class_cache_updated = false; - Map<StringName, GDMonoClass *> gdobject_class_cache; - HashMap<ClassKey, GDMonoClass *, ClassKey::Hasher> cached_classes; Map<MonoClass *, GDMonoClass *> cached_raw; @@ -111,11 +111,14 @@ public: String get_path() const; + bool has_attribute(GDMonoClass *p_attr_class); + MonoObject *get_attribute(GDMonoClass *p_attr_class); + + void fetch_attributes(); + GDMonoClass *get_class(const StringName &p_namespace, const StringName &p_name); GDMonoClass *get_class(MonoClass *p_mono_class); - GDMonoClass *get_object_derived_class(const StringName &p_class); - static String find_assembly(const String &p_name); static void fill_search_dirs(Vector<String> &r_search_dirs, const String &p_custom_config = String(), const String &p_custom_bcl_dir = String()); diff --git a/modules/mono/mono_gd/gd_mono_cache.cpp b/modules/mono/mono_gd/gd_mono_cache.cpp index aea467660f..d66cc29b9a 100644 --- a/modules/mono/mono_gd/gd_mono_cache.cpp +++ b/modules/mono/mono_gd/gd_mono_cache.cpp @@ -148,6 +148,11 @@ void CachedData::clear_godot_api_cache() { class_PuppetSyncAttribute = nullptr; class_GodotMethodAttribute = nullptr; field_GodotMethodAttribute_methodName = nullptr; + class_ScriptPathAttribute = nullptr; + field_ScriptPathAttribute_path = nullptr; + class_AssemblyHasScriptsAttribute = nullptr; + field_AssemblyHasScriptsAttribute_requiresLookup = nullptr; + field_AssemblyHasScriptsAttribute_scriptTypes = nullptr; field_GodotObject_ptr = nullptr; field_StringName_ptr = nullptr; @@ -272,6 +277,11 @@ void update_godot_api_cache() { CACHE_CLASS_AND_CHECK(PuppetSyncAttribute, GODOT_API_CLASS(PuppetSyncAttribute)); CACHE_CLASS_AND_CHECK(GodotMethodAttribute, GODOT_API_CLASS(GodotMethodAttribute)); CACHE_FIELD_AND_CHECK(GodotMethodAttribute, methodName, CACHED_CLASS(GodotMethodAttribute)->get_field("methodName")); + CACHE_CLASS_AND_CHECK(ScriptPathAttribute, GODOT_API_CLASS(ScriptPathAttribute)); + CACHE_FIELD_AND_CHECK(ScriptPathAttribute, path, CACHED_CLASS(ScriptPathAttribute)->get_field("path")); + CACHE_CLASS_AND_CHECK(AssemblyHasScriptsAttribute, GODOT_API_CLASS(AssemblyHasScriptsAttribute)); + CACHE_FIELD_AND_CHECK(AssemblyHasScriptsAttribute, requiresLookup, CACHED_CLASS(AssemblyHasScriptsAttribute)->get_field("requiresLookup")); + CACHE_FIELD_AND_CHECK(AssemblyHasScriptsAttribute, scriptTypes, CACHED_CLASS(AssemblyHasScriptsAttribute)->get_field("scriptTypes")); 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)); diff --git a/modules/mono/mono_gd/gd_mono_cache.h b/modules/mono/mono_gd/gd_mono_cache.h index fb75cb4b1c..51370da452 100644 --- a/modules/mono/mono_gd/gd_mono_cache.h +++ b/modules/mono/mono_gd/gd_mono_cache.h @@ -119,6 +119,11 @@ struct CachedData { GDMonoClass *class_PuppetSyncAttribute; GDMonoClass *class_GodotMethodAttribute; GDMonoField *field_GodotMethodAttribute_methodName; + GDMonoClass *class_ScriptPathAttribute; + GDMonoField *field_ScriptPathAttribute_path; + GDMonoClass *class_AssemblyHasScriptsAttribute; + GDMonoField *field_AssemblyHasScriptsAttribute_requiresLookup; + GDMonoField *field_AssemblyHasScriptsAttribute_scriptTypes; GDMonoField *field_GodotObject_ptr; GDMonoField *field_StringName_ptr; |