From 7542ff491b531a1f0d5d70009c8532c0d9ac8607 Mon Sep 17 00:00:00 2001 From: Jonathan Pobst Date: Fri, 8 Dec 2023 10:42:18 -1000 Subject: [PATCH 1/9] [JavaCallableWrappers] Begin modularizing jcw process. --- ....Interop.Tools.JavaCallableWrappers.csproj | 2 +- .../JavaCallableMethodClassifier.cs | 9 + ...aCallableWrapperGenerator.JavaFieldInfo.cs | 37 ++ .../JavaCallableWrapperGenerator.Signature.cs | 134 ++++ .../JavaCallableWrapperGenerator.cs | 625 ++---------------- .../JavaCallableWrapperWriter.cs | 475 +++++++++++++ .../JavaPeerStyle.cs | 7 + tools/jcw-gen/jcw-gen.csproj | 4 + tools/jcw-gen/jcw-gen.slnf | 13 + 9 files changed, 723 insertions(+), 583 deletions(-) create mode 100644 src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableMethodClassifier.cs create mode 100644 src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGenerator.JavaFieldInfo.cs create mode 100644 src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGenerator.Signature.cs create mode 100644 src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperWriter.cs create mode 100644 src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaPeerStyle.cs create mode 100644 tools/jcw-gen/jcw-gen.slnf diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.csproj b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.csproj index 7e4704293..fbd075c61 100644 --- a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.csproj +++ b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.csproj @@ -3,7 +3,7 @@ netstandard2.0 false - 8.0 + 10.0 enable true true diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableMethodClassifier.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableMethodClassifier.cs new file mode 100644 index 000000000..75e8061d6 --- /dev/null +++ b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableMethodClassifier.cs @@ -0,0 +1,9 @@ +using Mono.Cecil; + +namespace Java.Interop.Tools.JavaCallableWrappers +{ + public abstract class JavaCallableMethodClassifier + { + public abstract bool ShouldBeDynamicallyRegistered (TypeDefinition topType, MethodDefinition registeredMethod, MethodDefinition implementedMethod, CustomAttribute? registerAttribute); + } +} diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGenerator.JavaFieldInfo.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGenerator.JavaFieldInfo.cs new file mode 100644 index 000000000..9902148e3 --- /dev/null +++ b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGenerator.JavaFieldInfo.cs @@ -0,0 +1,37 @@ +using System; + +using Mono.Cecil; +using Java.Interop.Tools.TypeNameMappings; + +using MethodAttributes = Mono.Cecil.MethodAttributes; +using static Java.Interop.Tools.TypeNameMappings.JavaNativeTypeManager; + +namespace Java.Interop.Tools.JavaCallableWrappers +{ + public partial class JavaCallableWrapperGenerator { + internal class JavaFieldInfo { + public JavaFieldInfo (MethodDefinition method, string fieldName, IMetadataResolver resolver) + { + this.FieldName = fieldName; + InitializerName = method.Name; + TypeName = JavaNativeTypeManager.ReturnTypeFromSignature (GetJniSignature (method, resolver))?.Type + ?? throw new ArgumentException ($"Could not get JNI signature for method `{method.Name}`", nameof (method)); + IsStatic = method.IsStatic; + Access = method.Attributes & MethodAttributes.MemberAccessMask; + Annotations = GetAnnotationsString ("\t", method.CustomAttributes, resolver); + } + + public MethodAttributes Access { get; private set; } + public bool IsStatic { get; private set; } + public string TypeName { get; private set; } + public string FieldName { get; private set; } + public string InitializerName { get; private set; } + public string Annotations { get; private set; } + + public string GetJavaAccess () + { + return JavaCallableWrapperGenerator.GetJavaAccess (Access); + } + } + } +} diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGenerator.Signature.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGenerator.Signature.cs new file mode 100644 index 000000000..7e259f2eb --- /dev/null +++ b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGenerator.Signature.cs @@ -0,0 +1,134 @@ +using System; +using System.Text; + +using Mono.Cecil; + +using Android.Runtime; +using Java.Interop.Tools.Diagnostics; +using Java.Interop.Tools.TypeNameMappings; + +using MethodAttributes = Mono.Cecil.MethodAttributes; +using static Java.Interop.Tools.TypeNameMappings.JavaNativeTypeManager; + +namespace Java.Interop.Tools.JavaCallableWrappers +{ + + public partial class JavaCallableWrapperGenerator { + internal class Signature { + + public Signature (MethodDefinition method, RegisterAttribute register, IMetadataResolver cache, bool shouldBeDynamicallyRegistered = true) : this (method, register, null, null, cache, shouldBeDynamicallyRegistered) {} + + public Signature (MethodDefinition method, RegisterAttribute register, string? managedParameters, string? outerType, IMetadataResolver cache, bool shouldBeDynamicallyRegistered = true) + : this (register.Name, register.Signature, register.Connector, managedParameters, outerType, null) + { + Annotations = JavaCallableWrapperGenerator.GetAnnotationsString ("\t", method.CustomAttributes, cache); + IsDynamicallyRegistered = shouldBeDynamicallyRegistered; + } + + public Signature (MethodDefinition method, ExportAttribute export, string? managedParameters, IMetadataResolver cache) + : this (method.Name, GetJniSignature (method, cache), "__export__", null, null, export.SuperArgumentsString) + { + IsExport = true; + IsStatic = method.IsStatic; + JavaAccess = JavaCallableWrapperGenerator.GetJavaAccess (method.Attributes & MethodAttributes.MemberAccessMask); + ThrownTypeNames = export.ThrownNames; + JavaNameOverride = export.Name; + ManagedParameters = managedParameters; + Annotations = JavaCallableWrapperGenerator.GetAnnotationsString ("\t", method.CustomAttributes, cache); + } + + public Signature (MethodDefinition method, ExportFieldAttribute exportField, IMetadataResolver cache) + : this (method.Name, GetJniSignature (method, cache), "__export__", null, null, null) + { + if (method.HasParameters) + Diagnostic.Error (4205, JavaCallableWrapperGenerator.LookupSource (method), Localization.Resources.JavaCallableWrappers_XA4205); + if (method.ReturnType.MetadataType == MetadataType.Void) + Diagnostic.Error (4208, JavaCallableWrapperGenerator.LookupSource (method), Localization.Resources.JavaCallableWrappers_XA4208); + IsExport = true; + IsStatic = method.IsStatic; + JavaAccess = JavaCallableWrapperGenerator.GetJavaAccess (method.Attributes & MethodAttributes.MemberAccessMask); + + // annotations are processed within JavaFieldInfo, not the initializer method. So we don't generate them here. + } + + public Signature (string name, string? signature, string? connector, string? managedParameters, string? outerType, string? superCall) + { + ManagedParameters = managedParameters; + JniSignature = signature ?? throw new ArgumentNullException ("`connector` cannot be null.", nameof (connector)); + Method = "n_" + name + ":" + JniSignature + ":" + connector; + Name = name; + + var jnisig = JniSignature; + int closer = jnisig.IndexOf (')'); + string ret = jnisig.Substring (closer + 1); + retval = JavaNativeTypeManager.Parse (ret)?.Type; + string jniparms = jnisig.Substring (1, closer - 1); + if (string.IsNullOrEmpty (jniparms) && string.IsNullOrEmpty (superCall)) + return; + var parms = new StringBuilder (); + var scall = new StringBuilder (); + var acall = new StringBuilder (); + bool first = true; + int i = 0; + foreach (JniTypeName jti in JavaNativeTypeManager.FromSignature (jniparms)) { + if (outerType != null) { + acall.Append (outerType).Append (".this"); + outerType = null; + continue; + } + string? parmType = jti.Type; + if (!first) { + parms.Append (", "); + scall.Append (", "); + acall.Append (", "); + } + first = false; + parms.Append (parmType).Append (" p").Append (i); + scall.Append ("p").Append (i); + acall.Append ("p").Append (i); + ++i; + } + this.parms = parms.ToString (); + this.call = superCall != null ? superCall : scall.ToString (); + this.ActivateCall = acall.ToString (); + } + + string? call; + public string? SuperCall { + get { return call; } + } + + public string? ActivateCall {get; private set;} + + public readonly string Name; + public readonly string? JavaNameOverride; + public string JavaName { + get { return JavaNameOverride ?? Name; } + } + + string? parms; + public string? Params { + get { return parms; } + } + + string? retval; + public string? Retval { + get { return retval; } + } + + public string? ThrowsDeclaration { + get { return ThrownTypeNames?.Length > 0 ? " throws " + String.Join (", ", ThrownTypeNames) : null; } + } + + public readonly string? JavaAccess; + public readonly string? ManagedParameters; + public readonly string JniSignature; + public readonly string Method; + public readonly bool IsExport; + public readonly bool IsStatic; + public readonly bool IsDynamicallyRegistered = true; + public readonly string []? ThrownTypeNames; + public readonly string? Annotations; + } + } +} diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGenerator.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGenerator.cs index 699b3597e..a13ff5230 100644 --- a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGenerator.cs +++ b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGenerator.cs @@ -20,49 +20,14 @@ namespace Java.Interop.Tools.JavaCallableWrappers { - public enum JavaPeerStyle { - XAJavaInterop1, - JavaInterop1, - } - - public abstract class JavaCallableMethodClassifier - { - public abstract bool ShouldBeDynamicallyRegistered (TypeDefinition topType, MethodDefinition registeredMethod, MethodDefinition implementedMethod, CustomAttribute? registerAttribute); - } - - public class JavaCallableWrapperGenerator { - - class JavaFieldInfo { - public JavaFieldInfo (MethodDefinition method, string fieldName, IMetadataResolver resolver) - { - this.FieldName = fieldName; - InitializerName = method.Name; - TypeName = JavaNativeTypeManager.ReturnTypeFromSignature (GetJniSignature (method, resolver))?.Type - ?? throw new ArgumentException ($"Could not get JNI signature for method `{method.Name}`", nameof (method)); - IsStatic = method.IsStatic; - Access = method.Attributes & MethodAttributes.MemberAccessMask; - Annotations = GetAnnotationsString ("\t", method.CustomAttributes, resolver); - } - - public MethodAttributes Access { get; private set; } - public bool IsStatic { get; private set; } - public string TypeName { get; private set; } - public string FieldName { get; private set; } - public string InitializerName { get; private set; } - public string Annotations { get; private set; } - - public string GetJavaAccess () - { - return JavaCallableWrapperGenerator.GetJavaAccess (Access); - } - } + public partial class JavaCallableWrapperGenerator { Action log; - string name; - string package; - TypeDefinition type; + internal string name; + internal string package; + internal TypeDefinition type; List exported_fields = new List (); - List methods = new List (); + internal List methods = new List (); List ctors = new List (); List? children; @@ -575,91 +540,35 @@ string GetManagedParameters (MethodDefinition ctor, string? outerType) return sb.ToString (); } - // example of java target to generate for a type - // - // package mono.droid; - // - // import android.app.Activity; - // import android.os.Bundle; - // - // public class MonoActivity extends android.app.Activity - // { - // static final String __md_methods; - // static { - // __md_methods = - // "n_OnCreate:(Landroid/os/Bundle;)V:GetOnCreate_Landroid_os_Bundle_Handler\n" + - // ""; - // mono.android.Runtime.register ("Mono.Droid.MonoActivity, AssemblyName", MonoActivity.class, __md_methods); - // } - // - // public void onCreate(android.os.Bundle savedInstanceState) - // { - // n_onCreate (savedInstanceState); - // } - // - // private native void n_onCreate (android.os.Bundle bundle); - // } + public void Generate (string outputPath) + { + var jcw_writer = CreateWriter (); + jcw_writer.Generate (outputPath); + } public void Generate (TextWriter writer) { - if (!string.IsNullOrEmpty (package)) { - writer.WriteLine ("package " + package + ";"); - writer.WriteLine (); - } - - GenerateHeader (writer); - - bool needCtor = false; - if (HasDynamicallyRegisteredMethods) { - needCtor = true; - writer.WriteLine ("/** @hide */"); - writer.WriteLine ("\tpublic static final String __md_methods;"); - } - - if (children != null) { - for (int i = 0; i < children.Count; i++) { - if (!children[i].HasDynamicallyRegisteredMethods) { - continue; - } - needCtor = true; - writer.Write ("\tstatic final String __md_"); - writer.Write (i + 1); - writer.WriteLine ("_methods;"); - } - } - - if (needCtor) { - writer.WriteLine ("\tstatic {"); - - if (HasDynamicallyRegisteredMethods) { - GenerateRegisterType (writer, this, "__md_methods"); - } - - if (children != null) { - for (int i = 0; i < children.Count; ++i) { - GenerateRegisterType (writer, children [i], $"__md_{i + 1}_methods"); - } - } - writer.WriteLine ("\t}"); - } - - GenerateBody (writer); - - if (children != null) - foreach (JavaCallableWrapperGenerator child in children) { - child.GenerateHeader (writer); - child.GenerateBody (writer); - child.GenerateFooter (writer); - } - - GenerateFooter (writer); + var jcw_writer = CreateWriter (); + jcw_writer.Generate (writer); } - public void Generate (string outputPath) + internal JavaCallableWrapperWriter CreateWriter () { - using (StreamWriter sw = OpenStream (outputPath)) { - Generate (sw); - } + return new JavaCallableWrapperWriter ( + name: name, + package: package, + type: type, + hasDynamicallyRegisteredMethods: HasDynamicallyRegisteredMethods, + children: children, + cache: cache, + applicationJavaClass: ApplicationJavaClass, + codeGenerationTarget: CodeGenerationTarget, + generateOnCreateOverrides: GenerateOnCreateOverrides, + monoRuntimeInitialization: MonoRuntimeInitialization, + exported_fields: exported_fields, + methods: methods, + ctors: ctors, + generator: this); } static string GetAnnotationsString (string indent, IEnumerable atts, IMetadataResolver resolver) @@ -669,7 +578,7 @@ static string GetAnnotationsString (string indent, IEnumerable return sw.ToString (); } - static void WriteAnnotations (string indent, TextWriter sw, IEnumerable atts, IMetadataResolver resolver) + internal static void WriteAnnotations (string indent, TextWriter sw, IEnumerable atts, IMetadataResolver resolver) { foreach (var ca in atts) { var catype = resolver.Resolve (ca.AttributeType); @@ -698,6 +607,20 @@ static void WriteAnnotations (string indent, TextWriter sw, IEnumerable m.Name == "onCreate")) - WriteApplicationOnCreate (sw, w => { - w.Write ("\t\tmono.android.Runtime.register (\""); - w.Write (type.GetPartialAssemblyQualifiedName (cache)); - w.Write ("\", "); - w.Write (name); - w.WriteLine (".class, __md_methods);"); - w.WriteLine ("\t\tsuper.onCreate ();"); - }); - if (GenerateOnCreateOverrides && JavaNativeTypeManager.IsInstrumentation (type, cache) && !methods.Any (m => m.Name == "onCreate")) - WriteInstrumentationOnCreate (sw, w => { - w.Write ("\t\tmono.android.Runtime.register (\""); - w.Write (type.GetPartialAssemblyQualifiedName (cache)); - w.Write ("\", "); - w.Write (name); - w.WriteLine (".class, __md_methods);"); - w.WriteLine ("\t\tsuper.onCreate (arguments);"); - }); - - string addRef = "monodroidAddReference"; - string clearRefs = "monodroidClearReferences"; - if (CodeGenerationTarget == JavaPeerStyle.JavaInterop1) { - addRef = "jiAddManagedReference"; - clearRefs = "jiClearManagedReferences"; - } - - sw.WriteLine (); - sw.WriteLine ("\tprivate java.util.ArrayList refList;"); - sw.WriteLine ($"\tpublic void {addRef} (java.lang.Object obj)"); - sw.WriteLine ("\t{"); - sw.WriteLine ("\t\tif (refList == null)"); - sw.WriteLine ("\t\t\trefList = new java.util.ArrayList ();"); - sw.WriteLine ("\t\trefList.add (obj);"); - sw.WriteLine ("\t}"); - sw.WriteLine (); - sw.WriteLine ($"\tpublic void {clearRefs} ()"); - sw.WriteLine ("\t{"); - sw.WriteLine ("\t\tif (refList != null)"); - sw.WriteLine ("\t\t\trefList.clear ();"); - sw.WriteLine ("\t}"); - } - - void GenerateRegisterType (TextWriter sw, JavaCallableWrapperGenerator self, string field) - { - if (!self.HasDynamicallyRegisteredMethods) { - return; - } - - sw.Write ("\t\t"); - sw.Write (field); - sw.WriteLine (" = "); - string managedTypeName = self.type.GetPartialAssemblyQualifiedName (cache); - string javaTypeName = $"{package}.{name}"; - - foreach (Signature method in self.methods) { - if (method.IsDynamicallyRegistered) { - sw.Write ("\t\t\t\"", method.Method); - sw.Write (method.Method); - sw.WriteLine ("\\n\" +"); - } - } - sw.WriteLine ("\t\t\t\"\";"); - if (CannotRegisterInStaticConstructor (self.type)) - return; - sw.Write ("\t\t"); - switch (CodeGenerationTarget) { - case JavaPeerStyle.JavaInterop1: - sw.Write ("net.dot.jni.ManagedPeer.registerNativeMembers ("); - sw.Write (self.name); - sw.Write (".class, "); - sw.Write (field); - sw.WriteLine (");"); - break; - default: - sw.Write ("mono.android.Runtime.register (\""); - sw.Write (managedTypeName); - sw.Write ("\", "); - sw.Write (self.name); - sw.Write (".class, "); - sw.Write (field); - sw.WriteLine (");"); - break; - } - } - - void GenerateFooter (TextWriter sw) - { - sw.WriteLine ("}"); - } - - static string GetJavaAccess (MethodAttributes access) - { - switch (access) { - case MethodAttributes.Public: - return "public"; - case MethodAttributes.FamORAssem: - return "protected"; - case MethodAttributes.Family: - return "protected"; - default: - return "private"; - } - } - - static string GetJavaTypeName (TypeReference r, IMetadataResolver cache) - { - TypeDefinition d = cache.Resolve (r); - string? jniName = JavaNativeTypeManager.ToJniName (d, cache); - if (jniName == null) { - Diagnostic.Error (4201, Localization.Resources.JavaCallableWrappers_XA4201, r.FullName); - throw new InvalidOperationException ("--nrt:jniName-- Should not be reached"); - } - return jniName.Replace ('/', '.').Replace ('$', '.'); - } - - bool CannotRegisterInStaticConstructor (TypeDefinition type) - { - return JavaNativeTypeManager.IsApplication (type, cache) || JavaNativeTypeManager.IsInstrumentation (type, cache); - } - - class Signature { - - public Signature (MethodDefinition method, RegisterAttribute register, IMetadataResolver cache, bool shouldBeDynamicallyRegistered = true) : this (method, register, null, null, cache, shouldBeDynamicallyRegistered) {} - - public Signature (MethodDefinition method, RegisterAttribute register, string? managedParameters, string? outerType, IMetadataResolver cache, bool shouldBeDynamicallyRegistered = true) - : this (register.Name, register.Signature, register.Connector, managedParameters, outerType, null) - { - Annotations = JavaCallableWrapperGenerator.GetAnnotationsString ("\t", method.CustomAttributes, cache); - IsDynamicallyRegistered = shouldBeDynamicallyRegistered; - } - - public Signature (MethodDefinition method, ExportAttribute export, string? managedParameters, IMetadataResolver cache) - : this (method.Name, GetJniSignature (method, cache), "__export__", null, null, export.SuperArgumentsString) - { - IsExport = true; - IsStatic = method.IsStatic; - JavaAccess = JavaCallableWrapperGenerator.GetJavaAccess (method.Attributes & MethodAttributes.MemberAccessMask); - ThrownTypeNames = export.ThrownNames; - JavaNameOverride = export.Name; - ManagedParameters = managedParameters; - Annotations = JavaCallableWrapperGenerator.GetAnnotationsString ("\t", method.CustomAttributes, cache); - } - - public Signature (MethodDefinition method, ExportFieldAttribute exportField, IMetadataResolver cache) - : this (method.Name, GetJniSignature (method, cache), "__export__", null, null, null) - { - if (method.HasParameters) - Diagnostic.Error (4205, JavaCallableWrapperGenerator.LookupSource (method), Localization.Resources.JavaCallableWrappers_XA4205); - if (method.ReturnType.MetadataType == MetadataType.Void) - Diagnostic.Error (4208, JavaCallableWrapperGenerator.LookupSource (method), Localization.Resources.JavaCallableWrappers_XA4208); - IsExport = true; - IsStatic = method.IsStatic; - JavaAccess = JavaCallableWrapperGenerator.GetJavaAccess (method.Attributes & MethodAttributes.MemberAccessMask); - - // annotations are processed within JavaFieldInfo, not the initializer method. So we don't generate them here. - } - - public Signature (string name, string? signature, string? connector, string? managedParameters, string? outerType, string? superCall) - { - ManagedParameters = managedParameters; - JniSignature = signature ?? throw new ArgumentNullException ("`connector` cannot be null.", nameof (connector)); - Method = "n_" + name + ":" + JniSignature + ":" + connector; - Name = name; - - var jnisig = JniSignature; - int closer = jnisig.IndexOf (')'); - string ret = jnisig.Substring (closer + 1); - retval = JavaNativeTypeManager.Parse (ret)?.Type; - string jniparms = jnisig.Substring (1, closer - 1); - if (string.IsNullOrEmpty (jniparms) && string.IsNullOrEmpty (superCall)) - return; - var parms = new StringBuilder (); - var scall = new StringBuilder (); - var acall = new StringBuilder (); - bool first = true; - int i = 0; - foreach (JniTypeName jti in JavaNativeTypeManager.FromSignature (jniparms)) { - if (outerType != null) { - acall.Append (outerType).Append (".this"); - outerType = null; - continue; - } - string? parmType = jti.Type; - if (!first) { - parms.Append (", "); - scall.Append (", "); - acall.Append (", "); - } - first = false; - parms.Append (parmType).Append (" p").Append (i); - scall.Append ("p").Append (i); - acall.Append ("p").Append (i); - ++i; - } - this.parms = parms.ToString (); - this.call = superCall != null ? superCall : scall.ToString (); - this.ActivateCall = acall.ToString (); - } - - string? call; - public string? SuperCall { - get { return call; } - } - - public string? ActivateCall {get; private set;} - - public readonly string Name; - public readonly string? JavaNameOverride; - public string JavaName { - get { return JavaNameOverride ?? Name; } - } - - string? parms; - public string? Params { - get { return parms; } - } - - string? retval; - public string? Retval { - get { return retval; } - } - - public string? ThrowsDeclaration { - get { return ThrownTypeNames?.Length > 0 ? " throws " + String.Join (", ", ThrownTypeNames) : null; } - } - - public readonly string? JavaAccess; - public readonly string? ManagedParameters; - public readonly string JniSignature; - public readonly string Method; - public readonly bool IsExport; - public readonly bool IsStatic; - public readonly bool IsDynamicallyRegistered = true; - public readonly string []? ThrownTypeNames; - public readonly string? Annotations; - } - - void GenerateConstructor (Signature ctor, TextWriter sw) - { - // TODO: we only generate constructors so that Android types w/ no - // default constructor can be subclasses by our generated code. - // - // This does NOT currently allow creating managed types from Java. - sw.WriteLine (); - if (ctor.Annotations != null) - sw.WriteLine (ctor.Annotations); - sw.Write ("\tpublic "); - sw.Write (name); - sw.Write (" ("); - sw.Write (ctor.Params); - sw.Write (')'); - sw.WriteLine (ctor.ThrowsDeclaration); - sw.WriteLine ("\t{"); - sw.Write ("\t\tsuper ("); - sw.Write (ctor.SuperCall); - sw.WriteLine (");"); -#if MONODROID_TIMING - sw.WriteLine ("\t\tandroid.util.Log.i(\"MonoDroid-Timing\", \"{0}..ctor({1}): time: \"+java.lang.System.currentTimeMillis());", name, ctor.Params); -#endif - if (!CannotRegisterInStaticConstructor (type)) { - sw.Write ("\t\tif (getClass () == "); - sw.Write (name); - sw.WriteLine (".class) {"); - sw.Write ("\t\t\t"); - switch (CodeGenerationTarget) { - case JavaPeerStyle.JavaInterop1: - sw.Write ("net.dot.jni.ManagedPeer.construct (this, \""); - sw.Write (ctor.JniSignature); - sw.Write ("\", new java.lang.Object[] { "); - sw.Write (ctor.ActivateCall); - sw.WriteLine (" });"); - break; - default: - sw.Write ("mono.android.TypeManager.Activate (\""); - sw.Write (type.GetPartialAssemblyQualifiedName (cache)); - sw.Write ("\", \""); - sw.Write (ctor.ManagedParameters); - sw.Write ("\", this, new java.lang.Object[] { "); - sw.Write (ctor.ActivateCall); - sw.WriteLine (" });"); - break; - } - sw.WriteLine ("\t\t}"); - } - sw.WriteLine ("\t}"); - } - - void GenerateApplicationConstructor (TextWriter sw) - { - if (!JavaNativeTypeManager.IsApplication (type, cache)) { - return; - } - - sw.WriteLine (); - sw.Write ("\tpublic "); - sw.Write (name); - sw.WriteLine (" ()"); - sw.WriteLine ("\t{"); - sw.WriteLine ("\t\tmono.MonoPackageManager.setContext (this);"); - sw.WriteLine ("\t}"); - } - - void GenerateExportedField (JavaFieldInfo field, TextWriter sw) - { - sw.WriteLine (); - if (field.Annotations != null) - sw.WriteLine (field.Annotations); - sw.Write ("\t"); - sw.Write (field.GetJavaAccess ()); - sw.Write (' '); - if (field.IsStatic) - sw.Write ("static "); - sw.Write (field.TypeName); - sw.Write (' '); - sw.Write (field.FieldName); - sw.Write (" = "); - sw.Write (field.InitializerName); - sw.WriteLine (" ();"); - } - - void GenerateMethod (Signature method, TextWriter sw) - { - sw.WriteLine (); - if (method.Annotations != null) - sw.WriteLine (method.Annotations); - sw.Write ("\t"); - sw.Write (method.IsExport ? method.JavaAccess : "public"); - sw.Write (' '); - if (method.IsStatic) - sw.Write ("static "); - sw.Write (method.Retval); - sw.Write (' '); - sw.Write (method.JavaName); - sw.Write (" ("); - sw.Write (method.Params); - sw.Write (')'); - sw.WriteLine (method.ThrowsDeclaration); - sw.WriteLine ("\t{"); -#if MONODROID_TIMING - sw.WriteLine ("\t\tandroid.util.Log.i(\"MonoDroid-Timing\", \"{0}.{1}: time: \"+java.lang.System.currentTimeMillis());", name, method.Name); -#endif - sw.Write ("\t\t"); - sw.Write (method.Retval == "void" ? String.Empty : "return "); - sw.Write ("n_"); - sw.Write (method.Name); - sw.Write (" ("); - sw.Write (method.ActivateCall); - sw.WriteLine (");"); - - sw.WriteLine ("\t}"); - sw.WriteLine (); - sw.Write ("\tprivate "); - if (method.IsStatic) - sw.Write ("static "); - sw.Write ("native "); - sw.Write (method.Retval); - sw.Write (" n_"); - sw.Write (method.Name); - sw.Write (" ("); - sw.Write (method.Params); - sw.WriteLine (");"); - } - - void WriteApplicationOnCreate (TextWriter sw, Action extra) - { - sw.WriteLine (); - sw.WriteLine ("\tpublic void onCreate ()"); - sw.WriteLine ("\t{"); - extra (sw); - sw.WriteLine ("\t}"); - } - - void WriteInstrumentationOnCreate (TextWriter sw, Action extra) - { - sw.WriteLine (); - sw.WriteLine ("\tpublic void onCreate (android.os.Bundle arguments)"); - sw.WriteLine ("\t{"); - -#if MONODROID_TIMING - sw.WriteLine ("\t\tandroid.util.Log.i(\"MonoDroid-Timing\", \"{0}.onCreate(Bundle): time: \"+java.lang.System.currentTimeMillis());", name); - sw.WriteLine (); -#endif - - sw.WriteLine ("\t\tandroid.content.Context context = getContext ();"); - sw.WriteLine (); - - if (!string.IsNullOrEmpty (MonoRuntimeInitialization)) { - sw.WriteLine (MonoRuntimeInitialization); - sw.WriteLine (); - } - - extra (sw); - sw.WriteLine ("\t}"); - } - - StreamWriter OpenStream (string outputPath) - { - string destination = GetDestinationPath (outputPath); - Directory.CreateDirectory (Path.GetDirectoryName (destination)); - return new StreamWriter (new FileStream (destination, FileMode.Create, FileAccess.Write)); - } - - /// - /// Returns a destination file path based on the package name of this Java type - /// - public string GetDestinationPath (string outputPath) - { - var dir = package.Replace ('.', Path.DirectorySeparatorChar); - return Path.Combine (outputPath, dir, name + ".java"); - } } } diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperWriter.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperWriter.cs new file mode 100644 index 000000000..d8aa09420 --- /dev/null +++ b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperWriter.cs @@ -0,0 +1,475 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Java.Interop.Tools.Cecil; +using Java.Interop.Tools.Diagnostics; +using Java.Interop.Tools.TypeNameMappings; +using Mono.Cecil; + +using JavaFieldInfo = Java.Interop.Tools.JavaCallableWrappers.JavaCallableWrapperGenerator.JavaFieldInfo; +using Signature = Java.Interop.Tools.JavaCallableWrappers.JavaCallableWrapperGenerator.Signature; + +namespace Java.Interop.Tools.JavaCallableWrappers; + +class JavaCallableWrapperWriter +{ + readonly string name; + readonly string package; + readonly TypeDefinition type; + + readonly List? children; + readonly List ctors; + readonly List exported_fields; + readonly List methods; + + readonly IMetadataResolver cache; + readonly JavaCallableWrapperGenerator generator; + + public JavaCallableWrapperWriter (string name, string package, TypeDefinition type, bool hasDynamicallyRegisteredMethods, List? children, IMetadataResolver cache, string? applicationJavaClass, JavaPeerStyle codeGenerationTarget, bool generateOnCreateOverrides, string? monoRuntimeInitialization, List exported_fields, List methods, List ctors, JavaCallableWrapperGenerator generator) + { + this.name = name; + this.package = package; + this.type = type; + HasDynamicallyRegisteredMethods = hasDynamicallyRegisteredMethods; + this.children = children; + this.cache = cache; + ApplicationJavaClass = applicationJavaClass; + CodeGenerationTarget = codeGenerationTarget; + GenerateOnCreateOverrides = generateOnCreateOverrides; + MonoRuntimeInitialization = monoRuntimeInitialization; + this.exported_fields = exported_fields; + this.methods = methods; + this.ctors = ctors; + this.generator = generator; + } + + string? ApplicationJavaClass { get; } + + JavaPeerStyle CodeGenerationTarget { get; } + + bool GenerateOnCreateOverrides { get; } + + bool HasDynamicallyRegisteredMethods { get; } + + string? MonoRuntimeInitialization { get; } + + // example of java target to generate for a type + // + // package mono.droid; + // + // import android.app.Activity; + // import android.os.Bundle; + // + // public class MonoActivity extends android.app.Activity + // { + // static final String __md_methods; + // static { + // __md_methods = + // "n_OnCreate:(Landroid/os/Bundle;)V:GetOnCreate_Landroid_os_Bundle_Handler\n" + + // ""; + // mono.android.Runtime.register ("Mono.Droid.MonoActivity, AssemblyName", MonoActivity.class, __md_methods); + // } + // + // public void onCreate(android.os.Bundle savedInstanceState) + // { + // n_onCreate (savedInstanceState); + // } + // + // private native void n_onCreate (android.os.Bundle bundle); + // } + + public void Generate (TextWriter writer) + { + if (!string.IsNullOrEmpty (package)) { + writer.WriteLine ("package " + package + ";"); + writer.WriteLine (); + } + + GenerateHeader (writer); + + bool needCtor = false; + if (HasDynamicallyRegisteredMethods) { + needCtor = true; + writer.WriteLine ("/** @hide */"); + writer.WriteLine ("\tpublic static final String __md_methods;"); + } + + if (children != null) { + for (int i = 0; i < children.Count; i++) { + if (!children[i].HasDynamicallyRegisteredMethods) { + continue; + } + needCtor = true; + writer.Write ("\tstatic final String __md_"); + writer.Write (i + 1); + writer.WriteLine ("_methods;"); + } + } + + if (needCtor) { + writer.WriteLine ("\tstatic {"); + + if (HasDynamicallyRegisteredMethods) { + GenerateRegisterType (writer, generator, "__md_methods"); + } + + if (children != null) { + for (int i = 0; i < children.Count; ++i) { + GenerateRegisterType (writer, children [i], $"__md_{i + 1}_methods"); + } + } + writer.WriteLine ("\t}"); + } + + GenerateBody (writer); + + if (children != null) + foreach (JavaCallableWrapperGenerator child in children) { + var child_writer = child.CreateWriter (); + child_writer.GenerateHeader (writer); + child_writer.GenerateBody (writer); + child_writer.GenerateFooter (writer); + } + + GenerateFooter (writer); + } + + public void Generate (string outputPath) + { + using (StreamWriter sw = OpenStream (outputPath)) { + Generate (sw); + } + } + + void GenerateHeader (TextWriter sw) + { + sw.WriteLine (); + + // class annotations. + JavaCallableWrapperGenerator.WriteAnnotations ("", sw, type.CustomAttributes, cache); + + sw.WriteLine ("public " + (type.IsAbstract ? "abstract " : "") + "class " + name); + + string extendsType = GetJavaTypeName (type.BaseType, cache); + if (extendsType == "android.app.Application" && ApplicationJavaClass != null && !string.IsNullOrEmpty (ApplicationJavaClass)) + extendsType = ApplicationJavaClass; + sw.WriteLine ("\textends " + extendsType); + sw.WriteLine ("\timplements"); + sw.Write ("\t\t"); + switch (CodeGenerationTarget) { + case JavaPeerStyle.JavaInterop1: + sw.Write ("net.dot.jni.GCUserPeerable"); + break; + default: + sw.Write ("mono.android.IGCUserPeer"); + break; + } + foreach (var ifaceInfo in type.Interfaces) { + var iface = cache.Resolve(ifaceInfo.InterfaceType); + if (!JavaCallableWrapperGenerator.GetTypeRegistrationAttributes (iface).Any ()) + continue; + sw.WriteLine (","); + sw.Write ("\t\t"); + sw.Write (GetJavaTypeName (iface, cache)); + } + sw.WriteLine (); + sw.WriteLine ("{"); + } + + void GenerateBody (TextWriter sw) + { + foreach (Signature ctor in ctors) { + if (string.IsNullOrEmpty (ctor.Params) && JavaNativeTypeManager.IsApplication (type, cache)) + continue; + GenerateConstructor (ctor, sw); + } + + GenerateApplicationConstructor (sw); + + foreach (JavaFieldInfo field in exported_fields) + GenerateExportedField (field, sw); + + foreach (Signature method in methods) + GenerateMethod (method, sw); + + if (GenerateOnCreateOverrides && JavaNativeTypeManager.IsApplication (type, cache) && !methods.Any (m => m.Name == "onCreate")) + WriteApplicationOnCreate (sw, w => { + w.Write ("\t\tmono.android.Runtime.register (\""); + w.Write (type.GetPartialAssemblyQualifiedName (cache)); + w.Write ("\", "); + w.Write (name); + w.WriteLine (".class, __md_methods);"); + w.WriteLine ("\t\tsuper.onCreate ();"); + }); + if (GenerateOnCreateOverrides && JavaNativeTypeManager.IsInstrumentation (type, cache) && !methods.Any (m => m.Name == "onCreate")) + WriteInstrumentationOnCreate (sw, w => { + w.Write ("\t\tmono.android.Runtime.register (\""); + w.Write (type.GetPartialAssemblyQualifiedName (cache)); + w.Write ("\", "); + w.Write (name); + w.WriteLine (".class, __md_methods);"); + w.WriteLine ("\t\tsuper.onCreate (arguments);"); + }); + + string addRef = "monodroidAddReference"; + string clearRefs = "monodroidClearReferences"; + if (CodeGenerationTarget == JavaPeerStyle.JavaInterop1) { + addRef = "jiAddManagedReference"; + clearRefs = "jiClearManagedReferences"; + } + + sw.WriteLine (); + sw.WriteLine ("\tprivate java.util.ArrayList refList;"); + sw.WriteLine ($"\tpublic void {addRef} (java.lang.Object obj)"); + sw.WriteLine ("\t{"); + sw.WriteLine ("\t\tif (refList == null)"); + sw.WriteLine ("\t\t\trefList = new java.util.ArrayList ();"); + sw.WriteLine ("\t\trefList.add (obj);"); + sw.WriteLine ("\t}"); + sw.WriteLine (); + sw.WriteLine ($"\tpublic void {clearRefs} ()"); + sw.WriteLine ("\t{"); + sw.WriteLine ("\t\tif (refList != null)"); + sw.WriteLine ("\t\t\trefList.clear ();"); + sw.WriteLine ("\t}"); + } + + void GenerateRegisterType (TextWriter sw, JavaCallableWrapperGenerator self, string field) + { + if (!self.HasDynamicallyRegisteredMethods) { + return; + } + + sw.Write ("\t\t"); + sw.Write (field); + sw.WriteLine (" = "); + string managedTypeName = self.type.GetPartialAssemblyQualifiedName (cache); + string javaTypeName = $"{package}.{name}"; + + foreach (Signature method in self.methods) { + if (method.IsDynamicallyRegistered) { + sw.Write ("\t\t\t\"", method.Method); + sw.Write (method.Method); + sw.WriteLine ("\\n\" +"); + } + } + sw.WriteLine ("\t\t\t\"\";"); + if (CannotRegisterInStaticConstructor (self.type)) + return; + sw.Write ("\t\t"); + switch (CodeGenerationTarget) { + case JavaPeerStyle.JavaInterop1: + sw.Write ("net.dot.jni.ManagedPeer.registerNativeMembers ("); + sw.Write (self.name); + sw.Write (".class, "); + sw.Write (field); + sw.WriteLine (");"); + break; + default: + sw.Write ("mono.android.Runtime.register (\""); + sw.Write (managedTypeName); + sw.Write ("\", "); + sw.Write (self.name); + sw.Write (".class, "); + sw.Write (field); + sw.WriteLine (");"); + break; + } + } + + void GenerateFooter (TextWriter sw) + { + sw.WriteLine ("}"); + } + + static string GetJavaTypeName (TypeReference r, IMetadataResolver cache) + { + TypeDefinition d = cache.Resolve (r); + string? jniName = JavaNativeTypeManager.ToJniName (d, cache); + if (jniName == null) { + Diagnostic.Error (4201, Localization.Resources.JavaCallableWrappers_XA4201, r.FullName); + throw new InvalidOperationException ("--nrt:jniName-- Should not be reached"); + } + return jniName.Replace ('/', '.').Replace ('$', '.'); + } + + bool CannotRegisterInStaticConstructor (TypeDefinition type) + { + return JavaNativeTypeManager.IsApplication (type, cache) || JavaNativeTypeManager.IsInstrumentation (type, cache); + } + + void GenerateConstructor (Signature ctor, TextWriter sw) + { + // TODO: we only generate constructors so that Android types w/ no + // default constructor can be subclasses by our generated code. + // + // This does NOT currently allow creating managed types from Java. + sw.WriteLine (); + if (ctor.Annotations != null) + sw.WriteLine (ctor.Annotations); + sw.Write ("\tpublic "); + sw.Write (name); + sw.Write (" ("); + sw.Write (ctor.Params); + sw.Write (')'); + sw.WriteLine (ctor.ThrowsDeclaration); + sw.WriteLine ("\t{"); + sw.Write ("\t\tsuper ("); + sw.Write (ctor.SuperCall); + sw.WriteLine (");"); +#if MONODROID_TIMING + sw.WriteLine ("\t\tandroid.util.Log.i(\"MonoDroid-Timing\", \"{0}..ctor({1}): time: \"+java.lang.System.currentTimeMillis());", name, ctor.Params); +#endif + if (!CannotRegisterInStaticConstructor (type)) { + sw.Write ("\t\tif (getClass () == "); + sw.Write (name); + sw.WriteLine (".class) {"); + sw.Write ("\t\t\t"); + switch (CodeGenerationTarget) { + case JavaPeerStyle.JavaInterop1: + sw.Write ("net.dot.jni.ManagedPeer.construct (this, \""); + sw.Write (ctor.JniSignature); + sw.Write ("\", new java.lang.Object[] { "); + sw.Write (ctor.ActivateCall); + sw.WriteLine (" });"); + break; + default: + sw.Write ("mono.android.TypeManager.Activate (\""); + sw.Write (type.GetPartialAssemblyQualifiedName (cache)); + sw.Write ("\", \""); + sw.Write (ctor.ManagedParameters); + sw.Write ("\", this, new java.lang.Object[] { "); + sw.Write (ctor.ActivateCall); + sw.WriteLine (" });"); + break; + } + sw.WriteLine ("\t\t}"); + } + sw.WriteLine ("\t}"); + } + + void GenerateApplicationConstructor (TextWriter sw) + { + if (!JavaNativeTypeManager.IsApplication (type, cache)) { + return; + } + + sw.WriteLine (); + sw.Write ("\tpublic "); + sw.Write (name); + sw.WriteLine (" ()"); + sw.WriteLine ("\t{"); + sw.WriteLine ("\t\tmono.MonoPackageManager.setContext (this);"); + sw.WriteLine ("\t}"); + } + + void GenerateExportedField (JavaFieldInfo field, TextWriter sw) + { + sw.WriteLine (); + if (field.Annotations != null) + sw.WriteLine (field.Annotations); + sw.Write ("\t"); + sw.Write (field.GetJavaAccess ()); + sw.Write (' '); + if (field.IsStatic) + sw.Write ("static "); + sw.Write (field.TypeName); + sw.Write (' '); + sw.Write (field.FieldName); + sw.Write (" = "); + sw.Write (field.InitializerName); + sw.WriteLine (" ();"); + } + + void GenerateMethod (Signature method, TextWriter sw) + { + sw.WriteLine (); + if (method.Annotations != null) + sw.WriteLine (method.Annotations); + sw.Write ("\t"); + sw.Write (method.IsExport ? method.JavaAccess : "public"); + sw.Write (' '); + if (method.IsStatic) + sw.Write ("static "); + sw.Write (method.Retval); + sw.Write (' '); + sw.Write (method.JavaName); + sw.Write (" ("); + sw.Write (method.Params); + sw.Write (')'); + sw.WriteLine (method.ThrowsDeclaration); + sw.WriteLine ("\t{"); +#if MONODROID_TIMING + sw.WriteLine ("\t\tandroid.util.Log.i(\"MonoDroid-Timing\", \"{0}.{1}: time: \"+java.lang.System.currentTimeMillis());", name, method.Name); +#endif + sw.Write ("\t\t"); + sw.Write (method.Retval == "void" ? String.Empty : "return "); + sw.Write ("n_"); + sw.Write (method.Name); + sw.Write (" ("); + sw.Write (method.ActivateCall); + sw.WriteLine (");"); + + sw.WriteLine ("\t}"); + sw.WriteLine (); + sw.Write ("\tprivate "); + if (method.IsStatic) + sw.Write ("static "); + sw.Write ("native "); + sw.Write (method.Retval); + sw.Write (" n_"); + sw.Write (method.Name); + sw.Write (" ("); + sw.Write (method.Params); + sw.WriteLine (");"); + } + + void WriteApplicationOnCreate (TextWriter sw, Action extra) + { + sw.WriteLine (); + sw.WriteLine ("\tpublic void onCreate ()"); + sw.WriteLine ("\t{"); + extra (sw); + sw.WriteLine ("\t}"); + } + + void WriteInstrumentationOnCreate (TextWriter sw, Action extra) + { + sw.WriteLine (); + sw.WriteLine ("\tpublic void onCreate (android.os.Bundle arguments)"); + sw.WriteLine ("\t{"); + +#if MONODROID_TIMING + sw.WriteLine ("\t\tandroid.util.Log.i(\"MonoDroid-Timing\", \"{0}.onCreate(Bundle): time: \"+java.lang.System.currentTimeMillis());", name); + sw.WriteLine (); +#endif + + sw.WriteLine ("\t\tandroid.content.Context context = getContext ();"); + sw.WriteLine (); + + if (!string.IsNullOrEmpty (MonoRuntimeInitialization)) { + sw.WriteLine (MonoRuntimeInitialization); + sw.WriteLine (); + } + + extra (sw); + sw.WriteLine ("\t}"); + } + + StreamWriter OpenStream (string outputPath) + { + string destination = GetDestinationPath (outputPath); + Directory.CreateDirectory (Path.GetDirectoryName (destination)); + return new StreamWriter (new FileStream (destination, FileMode.Create, FileAccess.Write)); + } + + /// + /// Returns a destination file path based on the package name of this Java type + /// + public string GetDestinationPath (string outputPath) + { + var dir = package.Replace ('.', Path.DirectorySeparatorChar); + return Path.Combine (outputPath, dir, name + ".java"); + } +} diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaPeerStyle.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaPeerStyle.cs new file mode 100644 index 000000000..917196a16 --- /dev/null +++ b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaPeerStyle.cs @@ -0,0 +1,7 @@ +namespace Java.Interop.Tools.JavaCallableWrappers +{ + public enum JavaPeerStyle { + XAJavaInterop1, + JavaInterop1, + } +} diff --git a/tools/jcw-gen/jcw-gen.csproj b/tools/jcw-gen/jcw-gen.csproj index e798b5e19..f99367616 100644 --- a/tools/jcw-gen/jcw-gen.csproj +++ b/tools/jcw-gen/jcw-gen.csproj @@ -11,6 +11,10 @@ $(UtilityOutputFullPath) + + + + diff --git a/tools/jcw-gen/jcw-gen.slnf b/tools/jcw-gen/jcw-gen.slnf new file mode 100644 index 000000000..b8e3a83f9 --- /dev/null +++ b/tools/jcw-gen/jcw-gen.slnf @@ -0,0 +1,13 @@ +{ + "solution": { + "path": "..\\..\\Java.Interop.sln", + "projects": [ + "src\\Java.Interop.Localization\\Java.Interop.Localization.csproj", + "src\\Java.Interop.Tools.Cecil\\Java.Interop.Tools.Cecil.csproj", + "src\\Java.Interop.Tools.Diagnostics\\Java.Interop.Tools.Diagnostics.csproj", + "src\\Java.Interop.Tools.JavaCallableWrappers\\Java.Interop.Tools.JavaCallableWrappers.csproj", + "tests\\Java.Interop.Tools.JavaCallableWrappers-Tests\\Java.Interop.Tools.JavaCallableWrappers-Tests.csproj", + "tools\\jcw-gen\\jcw-gen.csproj" + ] + } +} \ No newline at end of file From 5c215b4dc48f54061377883e43a963920109c627 Mon Sep 17 00:00:00 2001 From: Jonathan Pobst Date: Mon, 11 Dec 2023 13:51:10 -1000 Subject: [PATCH 2/9] Create intermediate model checkpoint. --- .../Diagnostic.cs | 5 +- .../Adapters/CecilImporter.cs | 210 ++++++++ .../CallableWrapperApplicationConstructor.cs | 26 + .../CallableWrapperConstructor.cs | 75 +++ .../CallableWrapperField.cs | 47 ++ .../CallableWrapperMethod.cs | 93 ++++ .../CallableWrapperType.cs | 342 +++++++++++++ .../CallableWrapperWriterOptions.cs | 10 + ....Interop.Tools.JavaCallableWrappers.csproj | 2 +- .../JavaCallableWrapperGenerator.cs | 40 +- .../JavaCallableWrapperWriter.cs | 475 ------------------ 11 files changed, 823 insertions(+), 502 deletions(-) create mode 100644 src/Java.Interop.Tools.JavaCallableWrappers/Adapters/CecilImporter.cs create mode 100644 src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperApplicationConstructor.cs create mode 100644 src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperConstructor.cs create mode 100644 src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperField.cs create mode 100644 src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperMethod.cs create mode 100644 src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperType.cs create mode 100644 src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperWriterOptions.cs delete mode 100644 src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperWriter.cs diff --git a/src/Java.Interop.Tools.Diagnostics/Java.Interop.Tools.Diagnostics/Diagnostic.cs b/src/Java.Interop.Tools.Diagnostics/Java.Interop.Tools.Diagnostics/Diagnostic.cs index 4f9419799..a260ba98f 100644 --- a/src/Java.Interop.Tools.Diagnostics/Java.Interop.Tools.Diagnostics/Diagnostic.cs +++ b/src/Java.Interop.Tools.Diagnostics/Java.Interop.Tools.Diagnostics/Diagnostic.cs @@ -3,7 +3,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; - +using System.Diagnostics.CodeAnalysis; using Mono.Cecil.Cil; namespace Java.Interop.Tools.Diagnostics { @@ -148,6 +148,7 @@ namespace Java.Interop.Tools.Diagnostics { // public static class Diagnostic { + [DoesNotReturn] public static void Error (int code, SequencePoint? location, string message, params object[] args) { throw new XamarinAndroidException (code, message, args) { @@ -156,11 +157,13 @@ public static void Error (int code, SequencePoint? location, string message, par } + [DoesNotReturn] public static void Error (int code, string message, params object[] args) { throw new XamarinAndroidException (code, message, args); } + [DoesNotReturn] public static void Error (int code, Exception innerException, string message, params object[] args) { throw new XamarinAndroidException (code, innerException, message, args); diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Adapters/CecilImporter.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Adapters/CecilImporter.cs new file mode 100644 index 000000000..8a4f6e670 --- /dev/null +++ b/src/Java.Interop.Tools.JavaCallableWrappers/Adapters/CecilImporter.cs @@ -0,0 +1,210 @@ +using System; +using System.Text; +using System.Xml.Linq; +using Android.Runtime; +using Java.Interop.Tools.Diagnostics; +using Java.Interop.Tools.JavaCallableWrappers.CallableWrapperMembers; +using Java.Interop.Tools.TypeNameMappings; +using Mono.Cecil; + +namespace Java.Interop.Tools.JavaCallableWrappers.Adapters; + +class CecilImporter +{ + public static CallableWrapperField CreateField (MethodDefinition method, string fieldName, IMetadataResolver resolver) + { + var visibility = JavaCallableWrapperGenerator.GetJavaAccess (method.Attributes & MethodAttributes.MemberAccessMask); + var type_name = JavaNativeTypeManager.ReturnTypeFromSignature (JavaNativeTypeManager.GetJniSignature (method, resolver))?.Type + ?? throw new ArgumentException ($"Could not get JNI signature for method `{method.Name}`", nameof (method)); + var annotations = JavaCallableWrapperGenerator.GetAnnotationsString ("\t", method.CustomAttributes, resolver); + + return new CallableWrapperField ( + fieldName: fieldName, + typeName: type_name, + visibility: visibility, + isStatic: method.IsStatic, + initializerName: method.Name, + annotations: annotations); + } + + // Temporary conversion function + public static CallableWrapperField CreateField (JavaCallableWrapperGenerator.JavaFieldInfo field) + { + return new CallableWrapperField ( + fieldName: field.FieldName, + typeName: field.TypeName, + visibility: field.GetJavaAccess (), + isStatic: field.IsStatic, + initializerName: field.InitializerName, + annotations: field.Annotations); + } + + public static CallableWrapperMethod CreateMethod (MethodDefinition method, RegisterAttribute register, IMetadataResolver cache, bool shouldBeDynamicallyRegistered = true) + => CreateMethod (method, register, null, null, cache, shouldBeDynamicallyRegistered); + + public static CallableWrapperMethod CreateMethod (MethodDefinition methodDefinition, RegisterAttribute register, string? managedParameters, string? outerType, IMetadataResolver cache, bool shouldBeDynamicallyRegistered = true) + { + var method = CreateMethod (register.Name, register.Signature, register.Connector, managedParameters, outerType, null); + + method.Annotations = JavaCallableWrapperGenerator.GetAnnotationsString ("\t", methodDefinition.CustomAttributes, cache); + method.IsDynamicallyRegistered = shouldBeDynamicallyRegistered; + + return method; + } + + public static CallableWrapperMethod CreateMethod (MethodDefinition methodDefinition, ExportAttribute export, string? managedParameters, IMetadataResolver resolver) + { + var method = CreateMethod (methodDefinition.Name, JavaNativeTypeManager.GetJniSignature (methodDefinition, resolver), "__export__", null, null, export.SuperArgumentsString); + + method.IsExport = true; + method.IsStatic = methodDefinition.IsStatic; + method.JavaAccess = JavaCallableWrapperGenerator.GetJavaAccess (methodDefinition.Attributes & MethodAttributes.MemberAccessMask); + method.ThrownTypeNames = export.ThrownNames; + method.JavaNameOverride = export.Name; + method.ManagedParameters = managedParameters; + method.Annotations = JavaCallableWrapperGenerator.GetAnnotationsString ("\t", methodDefinition.CustomAttributes, resolver); + + return method; + } + + public static CallableWrapperMethod CreateMethod (MethodDefinition methodDefinition, IMetadataResolver resolver) + { + var method = CreateMethod (methodDefinition.Name, JavaNativeTypeManager.GetJniSignature (methodDefinition, resolver), "__export__", null, null, null); + + if (methodDefinition.HasParameters) + Diagnostic.Error (4205, JavaCallableWrapperGenerator.LookupSource (methodDefinition), Localization.Resources.JavaCallableWrappers_XA4205); + if (methodDefinition.ReturnType.MetadataType == MetadataType.Void) + Diagnostic.Error (4208, JavaCallableWrapperGenerator.LookupSource (methodDefinition), Localization.Resources.JavaCallableWrappers_XA4208); + + method.IsExport = true; + method.IsStatic = method.IsStatic; + method.JavaAccess = JavaCallableWrapperGenerator.GetJavaAccess (methodDefinition.Attributes & MethodAttributes.MemberAccessMask); + + // Annotations are processed within CallableWrapperField, not the initializer method. So we don't generate them here. + + return method; + } + + public static CallableWrapperMethod CreateMethod (string name, string? signature, string? connector, string? managedParameters, string? outerType, string? superCall) + { + signature = signature ?? throw new ArgumentNullException ("`connector` cannot be null.", nameof (connector)); + var method_name = "n_" + name + ":" + signature + ":" + connector; + + var method = new CallableWrapperMethod (name, method_name, signature) { + ManagedParameters = managedParameters + }; + + var jnisig = signature; + var closer = jnisig.IndexOf (')'); + var ret = jnisig.Substring (closer + 1); + method.Retval = JavaNativeTypeManager.Parse (ret)?.Type; + + var jniparms = jnisig.Substring (1, closer - 1); + + if (string.IsNullOrEmpty (jniparms) && string.IsNullOrEmpty (superCall)) + return method; + + var parms = new StringBuilder (); + var scall = new StringBuilder (); + var acall = new StringBuilder (); + var first = true; + var i = 0; + + foreach (var jti in JavaNativeTypeManager.FromSignature (jniparms)) { + if (outerType != null) { + acall.Append (outerType).Append (".this"); + outerType = null; + continue; + } + + var parmType = jti.Type; + + if (!first) { + parms.Append (", "); + scall.Append (", "); + acall.Append (", "); + } + + first = false; + parms.Append (parmType).Append (" p").Append (i); + scall.Append ("p").Append (i); + acall.Append ("p").Append (i); + ++i; + } + + method.Params = parms.ToString (); + method.SuperCall = superCall ?? scall.ToString (); + method.ActivateCall = acall.ToString (); + + return method; + } + + // Temporary conversion function + public static CallableWrapperMethod CreateMethod (JavaCallableWrapperGenerator.Signature signature) + { + return new CallableWrapperMethod (signature.Name, signature.Method, signature.JniSignature) { + ManagedParameters = signature.ManagedParameters, + JavaNameOverride = signature.JavaNameOverride, + Params = signature.Params, + Retval = signature.Retval, + ThrowsDeclaration = signature.ThrowsDeclaration, + JavaAccess = signature.JavaAccess, + IsExport = signature.IsExport, + IsStatic = signature.IsStatic, + IsDynamicallyRegistered = signature.IsDynamicallyRegistered, + ThrownTypeNames = signature.ThrownTypeNames, + Annotations = signature.Annotations, + SuperCall = signature.SuperCall, + ActivateCall = signature.ActivateCall, + }; + } + + // Temporary conversion function + public static CallableWrapperConstructor CreateConstructor (JavaCallableWrapperGenerator.Signature signature) + { + return new CallableWrapperConstructor (signature.Name, signature.Method, signature.JniSignature) { + ManagedParameters = signature.ManagedParameters, + JavaNameOverride = signature.JavaNameOverride, + Params = signature.Params, + Retval = signature.Retval, + ThrowsDeclaration = signature.ThrowsDeclaration, + JavaAccess = signature.JavaAccess, + IsExport = signature.IsExport, + IsStatic = signature.IsStatic, + IsDynamicallyRegistered = signature.IsDynamicallyRegistered, + ThrownTypeNames = signature.ThrownTypeNames, + Annotations = signature.Annotations, + SuperCall = signature.SuperCall, + ActivateCall = signature.ActivateCall + }; + } + + public static CallableWrapperApplicationConstructor? CreateApplicationConstructor (string name, TypeDefinition type, IMetadataResolver resolver) + { + if (!JavaNativeTypeManager.IsApplication (type, resolver)) + return null; + + return new CallableWrapperApplicationConstructor (name); + } + + // Temporary conversion function + public static CallableWrapperType CreateType (JavaCallableWrapperGenerator generator) + { + var type = new CallableWrapperType (generator.name, generator.package) { + Type = generator.type, + Cache = generator.cache, + IsAbstract = generator.type.IsAbstract, + ApplicationJavaClass = generator.ApplicationJavaClass, + Generator = generator, + HasDynamicallyRegisteredMethods = generator.HasDynamicallyRegisteredMethods, + GenerateOnCreateOverrides = generator.GenerateOnCreateOverrides, + MonoRuntimeInitialization = generator.MonoRuntimeInitialization, + }; + + if (generator.children is not null) + foreach (var nested in generator.children) + type.NestedTypes.Add (CreateType (nested)); + + return type; + } +} diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperApplicationConstructor.cs b/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperApplicationConstructor.cs new file mode 100644 index 000000000..f0616a054 --- /dev/null +++ b/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperApplicationConstructor.cs @@ -0,0 +1,26 @@ +using System.IO; + +namespace Java.Interop.Tools.JavaCallableWrappers.CallableWrapperMembers; + +class CallableWrapperApplicationConstructor +{ + public string Name { get; set; } + + public CallableWrapperApplicationConstructor (string name) + { + Name = name; + } + + public void Generate (TextWriter sw, CallableWrapperWriterOptions options) + { + sw.WriteLine (); + + sw.Write ("\tpublic "); + sw.Write (Name); + sw.WriteLine (" ()"); + + sw.WriteLine ("\t{"); + sw.WriteLine ("\t\tmono.MonoPackageManager.setContext (this);"); + sw.WriteLine ("\t}"); + } +} diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperConstructor.cs b/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperConstructor.cs new file mode 100644 index 000000000..b319d0bd5 --- /dev/null +++ b/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperConstructor.cs @@ -0,0 +1,75 @@ +using System.IO; + +namespace Java.Interop.Tools.JavaCallableWrappers.CallableWrapperMembers; + +class CallableWrapperConstructor : CallableWrapperMethod +{ + public bool CannotRegisterInStaticConstructor { get; set; } + public string? PartialAssemblyQualifiedName { get; set; } + + public CallableWrapperConstructor (string name, string method, string jniSignature) : base (name, method, jniSignature) + { + } + + public override void Generate (TextWriter sw, CallableWrapperWriterOptions options) + { + // TODO: we only generate constructors so that Android types w/ no + // default constructor can be subclasses by our generated code. + // + // This does NOT currently allow creating managed types from Java. + sw.WriteLine (); + + if (Annotations is not null) + sw.WriteLine (Annotations); + + sw.Write ("\tpublic "); + sw.Write (Name); + + sw.Write (" ("); + sw.Write (Params); + sw.Write (')'); + + sw.WriteLine (ThrowsDeclaration); + + sw.WriteLine ("\t{"); + sw.Write ("\t\tsuper ("); + sw.Write (SuperCall); + sw.WriteLine (");"); + +#if MONODROID_TIMING + sw.WriteLine ("\t\tandroid.util.Log.i(\"MonoDroid-Timing\", \"{0}..ctor({1}): time: \"+java.lang.System.currentTimeMillis());", Name, Params); +#endif + + if (!CannotRegisterInStaticConstructor) { + + sw.Write ("\t\tif (getClass () == "); + sw.Write (Name); + sw.WriteLine (".class) {"); + + sw.Write ("\t\t\t"); + + switch (options.CodeGenerationTarget) { + case JavaPeerStyle.JavaInterop1: + sw.Write ("net.dot.jni.ManagedPeer.construct (this, \""); + sw.Write (JniSignature); + sw.Write ("\", new java.lang.Object[] { "); + sw.Write (ActivateCall); + sw.WriteLine (" });"); + break; + default: + sw.Write ("mono.android.TypeManager.Activate (\""); + sw.Write (PartialAssemblyQualifiedName); + sw.Write ("\", \""); + sw.Write (ManagedParameters); + sw.Write ("\", this, new java.lang.Object[] { "); + sw.Write (ActivateCall); + sw.WriteLine (" });"); + break; + } + + sw.WriteLine ("\t\t}"); + } + + sw.WriteLine ("\t}"); + } +} diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperField.cs b/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperField.cs new file mode 100644 index 000000000..54928b095 --- /dev/null +++ b/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperField.cs @@ -0,0 +1,47 @@ +using System.IO; + +namespace Java.Interop.Tools.JavaCallableWrappers.CallableWrapperMembers; + +class CallableWrapperField +{ + public string FieldName { get; } + public string TypeName { get; } + public string Visibility { get; } + public bool IsStatic { get; } + public string InitializerName { get; } + public string? Annotations { get; } + + public CallableWrapperField (string fieldName, string typeName, string visibility, bool isStatic, string initializerName, string? annotations) + { + FieldName = fieldName; + TypeName = typeName; + Visibility = visibility; + IsStatic = isStatic; + InitializerName = initializerName; + Annotations = annotations; + } + + public void Generate (TextWriter sw) + { + sw.WriteLine (); + + if (Annotations is not null) + sw.WriteLine (Annotations); + + sw.Write ("\t"); + sw.Write (Visibility); + sw.Write (' '); + + if (IsStatic) + sw.Write ("static "); + + sw.Write (TypeName); + sw.Write (' '); + + sw.Write (FieldName); + sw.Write (" = "); + + sw.Write (InitializerName); + sw.WriteLine (" ();"); + } +} diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperMethod.cs b/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperMethod.cs new file mode 100644 index 000000000..ea14bfb15 --- /dev/null +++ b/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperMethod.cs @@ -0,0 +1,93 @@ +using System; +using System.IO; + +namespace Java.Interop.Tools.JavaCallableWrappers.CallableWrapperMembers; + +class CallableWrapperMethod +{ + public string Name { get; set; } + public string Method { get; set; } + public string JniSignature { get; set; } + public string? ManagedParameters { get; set; } + public string? JavaNameOverride { get; set; } + public string? Params { get; set; } + public string? Retval { get; set; } + public string? ThrowsDeclaration { get; set; } + public string? JavaAccess { get; set; } + public bool IsExport { get; set; } + public bool IsStatic { get; set; } + public bool IsDynamicallyRegistered { get; set; } + public string []? ThrownTypeNames { get; set; } + public string? Annotations { get; set; } + public string? SuperCall { get; set; } + public string? ActivateCall { get; set; } + public string JavaName => JavaNameOverride ?? Name; + + public CallableWrapperMethod (string name, string method, string jniSignature) + { + Name = name; + Method = method; + JniSignature = jniSignature; + } + + public virtual void Generate (TextWriter sw, CallableWrapperWriterOptions options) + { + sw.WriteLine (); + + if (Annotations is not null) + sw.WriteLine (Annotations); + + sw.Write ("\t"); + + sw.Write (IsExport ? JavaAccess : "public"); + sw.Write (' '); + + if (IsStatic) + sw.Write ("static "); + + sw.Write (Retval); + sw.Write (' '); + + sw.Write (JavaName); + + sw.Write (" ("); + sw.Write (Params); + sw.Write (')'); + + sw.WriteLine (ThrowsDeclaration); + sw.WriteLine ("\t{"); + +#if MONODROID_TIMING + sw.WriteLine ("\t\tandroid.util.Log.i(\"MonoDroid-Timing\", \"{0}.{1}: time: \"+java.lang.System.currentTimeMillis());", name, method.Name); +#endif + + sw.Write ("\t\t"); + sw.Write (Retval == "void" ? string.Empty : "return "); + + sw.Write ("n_"); + sw.Write (Name); + + sw.Write (" ("); + sw.Write (ActivateCall); + sw.WriteLine (");"); + + sw.WriteLine ("\t}"); + sw.WriteLine (); + + sw.Write ("\tprivate "); + + if (IsStatic) + sw.Write ("static "); + + sw.Write ("native "); + + sw.Write (Retval); + + sw.Write (" n_"); + sw.Write (Name); + + sw.Write (" ("); + sw.Write (Params); + sw.WriteLine (");"); + } +} diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperType.cs b/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperType.cs new file mode 100644 index 000000000..6609b2a7e --- /dev/null +++ b/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperType.cs @@ -0,0 +1,342 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Java.Interop.Tools.Cecil; +using System.Xml.Linq; +using Java.Interop.Tools.Diagnostics; +using Java.Interop.Tools.TypeNameMappings; +using Mono.Cecil; +using static Java.Interop.Tools.JavaCallableWrappers.JavaCallableWrapperGenerator; +using Java.Interop.Tools.JavaCallableWrappers.Adapters; + +namespace Java.Interop.Tools.JavaCallableWrappers.CallableWrapperMembers; + +class CallableWrapperType +{ + public string Name { get; set; } + public string Package { get; set; } + public bool IsAbstract { get; set; } + public string? ApplicationJavaClass { get; set; } + public bool HasDynamicallyRegisteredMethods { get; set; } + public bool GenerateOnCreateOverrides { get; set; } + public string? MonoRuntimeInitialization { get; set; } + + public List Constructors { get; } = new List (); + public List Fields { get; } = new List (); + public List Methods { get; } = new List (); + public List NestedTypes { get; } = new List (); + + // TODO: Remove Cecil + public TypeDefinition Type { get; set; } = null!; + public IMetadataResolver Cache { get; set; } = null!; + public JavaCallableWrapperGenerator Generator { get; set; } = null!; + + public CallableWrapperType (string name, string package) + { + Name = name; + Package = package; + } + + // example of java target to generate for a type + // + // package mono.droid; + // + // import android.app.Activity; + // import android.os.Bundle; + // + // public class MonoActivity extends android.app.Activity + // { + // static final String __md_methods; + // static { + // __md_methods = + // "n_OnCreate:(Landroid/os/Bundle;)V:GetOnCreate_Landroid_os_Bundle_Handler\n" + + // ""; + // mono.android.Runtime.register ("Mono.Droid.MonoActivity, AssemblyName", MonoActivity.class, __md_methods); + // } + // + // public void onCreate(android.os.Bundle savedInstanceState) + // { + // n_onCreate (savedInstanceState); + // } + // + // private native void n_onCreate (android.os.Bundle bundle); + // } + + public void Generate (TextWriter writer, CallableWrapperWriterOptions options, bool isNested) + { + if (!isNested && !string.IsNullOrEmpty (Package)) { + writer.WriteLine ("package " + Package + ";"); + writer.WriteLine (); + } + + GenerateHeader (writer, options); + + if (!isNested) + GenerateInfrastructure (writer, options); + + GenerateBody (writer, options); + + foreach (var nested in NestedTypes) + nested.Generate (writer, options, true); + + GenerateFooter (writer, options); + } + + public void GenerateHeader (TextWriter sw, CallableWrapperWriterOptions options) + { + sw.WriteLine (); + + // class annotations. + JavaCallableWrapperGenerator.WriteAnnotations ("", sw, Type.CustomAttributes, Cache); + + sw.WriteLine ("public " + (IsAbstract ? "abstract " : "") + "class " + Name); + + var extendsType = GetJavaTypeName (Type.BaseType, Cache); + + if (extendsType == "android.app.Application" && ApplicationJavaClass != null && !string.IsNullOrEmpty (ApplicationJavaClass)) + extendsType = ApplicationJavaClass; + + sw.WriteLine ("\textends " + extendsType); + sw.WriteLine ("\timplements"); + sw.Write ("\t\t"); + switch (options.CodeGenerationTarget) { + case JavaPeerStyle.JavaInterop1: + sw.Write ("net.dot.jni.GCUserPeerable"); + break; + default: + sw.Write ("mono.android.IGCUserPeer"); + break; + } + foreach (var ifaceInfo in Type.Interfaces) { + var iface = Cache.Resolve (ifaceInfo.InterfaceType); + if (!JavaCallableWrapperGenerator.GetTypeRegistrationAttributes (iface).Any ()) + continue; + sw.WriteLine (","); + sw.Write ("\t\t"); + sw.Write (GetJavaTypeName (iface, Cache)); + } + sw.WriteLine (); + sw.WriteLine ("{"); + } + + public void GenerateInfrastructure (TextWriter writer, CallableWrapperWriterOptions options) + { + var needCtor = false; + + if (HasDynamicallyRegisteredMethods) { + needCtor = true; + writer.WriteLine ("/** @hide */"); + writer.WriteLine ("\tpublic static final String __md_methods;"); + } + + for (int i = 0; i < NestedTypes.Count; i++) { + if (!NestedTypes [i].HasDynamicallyRegisteredMethods) { + continue; + } + needCtor = true; + writer.Write ("\tstatic final String __md_"); + writer.Write (i + 1); + writer.WriteLine ("_methods;"); + } + + if (needCtor) { + writer.WriteLine ("\tstatic {"); + + if (HasDynamicallyRegisteredMethods) { + GenerateRegisterType (writer, Generator, "__md_methods", options); + } + + for (int i = 0; i < NestedTypes.Count; ++i) { + GenerateRegisterType (writer, NestedTypes [i].Generator, $"__md_{i + 1}_methods", options); + } + + writer.WriteLine ("\t}"); + } + } + + public void GenerateBody (TextWriter sw, CallableWrapperWriterOptions options) + { + foreach (Signature ctor in Generator.ctors) { + if (string.IsNullOrEmpty (ctor.Params) && JavaNativeTypeManager.IsApplication (Type, Cache)) + continue; + + var ct = CecilImporter.CreateConstructor (ctor); + + ct.Name = Name; + ct.CannotRegisterInStaticConstructor = CannotRegisterInStaticConstructor (Type); + ct.PartialAssemblyQualifiedName = Type.GetPartialAssemblyQualifiedName (Cache); + + ct.Generate (sw, options); + } + + if (CecilImporter.CreateApplicationConstructor (Name, Type, Cache) is CallableWrapperApplicationConstructor app_ctor) + app_ctor.Generate (sw, options); + + foreach (JavaFieldInfo field in Generator.exported_fields) + CecilImporter.CreateField (field).Generate (sw); + + foreach (Signature method in Generator.methods) + CecilImporter.CreateMethod (method).Generate (sw, options); + + if (GenerateOnCreateOverrides && JavaNativeTypeManager.IsApplication (Type, Cache) && !Generator.methods.Any (m => m.Name == "onCreate")) + WriteApplicationOnCreate (sw, w => { + w.Write ("\t\tmono.android.Runtime.register (\""); + w.Write (Type.GetPartialAssemblyQualifiedName (Cache)); + w.Write ("\", "); + w.Write (Name); + w.WriteLine (".class, __md_methods);"); + w.WriteLine ("\t\tsuper.onCreate ();"); + }); + if (GenerateOnCreateOverrides && JavaNativeTypeManager.IsInstrumentation (Type, Cache) && !Generator.methods.Any (m => m.Name == "onCreate")) + WriteInstrumentationOnCreate (sw, w => { + w.Write ("\t\tmono.android.Runtime.register (\""); + w.Write (Type.GetPartialAssemblyQualifiedName (Cache)); + w.Write ("\", "); + w.Write (Name); + w.WriteLine (".class, __md_methods);"); + w.WriteLine ("\t\tsuper.onCreate (arguments);"); + }); + + string addRef = "monodroidAddReference"; + string clearRefs = "monodroidClearReferences"; + if (options.CodeGenerationTarget == JavaPeerStyle.JavaInterop1) { + addRef = "jiAddManagedReference"; + clearRefs = "jiClearManagedReferences"; + } + + sw.WriteLine (); + sw.WriteLine ("\tprivate java.util.ArrayList refList;"); + sw.WriteLine ($"\tpublic void {addRef} (java.lang.Object obj)"); + sw.WriteLine ("\t{"); + sw.WriteLine ("\t\tif (refList == null)"); + sw.WriteLine ("\t\t\trefList = new java.util.ArrayList ();"); + sw.WriteLine ("\t\trefList.add (obj);"); + sw.WriteLine ("\t}"); + sw.WriteLine (); + sw.WriteLine ($"\tpublic void {clearRefs} ()"); + sw.WriteLine ("\t{"); + sw.WriteLine ("\t\tif (refList != null)"); + sw.WriteLine ("\t\t\trefList.clear ();"); + sw.WriteLine ("\t}"); + } + + public void GenerateFooter (TextWriter sw, CallableWrapperWriterOptions options) + { + sw.WriteLine ("}"); + } + + void WriteApplicationOnCreate (TextWriter sw, Action extra) + { + sw.WriteLine (); + sw.WriteLine ("\tpublic void onCreate ()"); + sw.WriteLine ("\t{"); + extra (sw); + sw.WriteLine ("\t}"); + } + + void WriteInstrumentationOnCreate (TextWriter sw, Action extra) + { + sw.WriteLine (); + sw.WriteLine ("\tpublic void onCreate (android.os.Bundle arguments)"); + sw.WriteLine ("\t{"); + +#if MONODROID_TIMING + sw.WriteLine ("\t\tandroid.util.Log.i(\"MonoDroid-Timing\", \"{0}.onCreate(Bundle): time: \"+java.lang.System.currentTimeMillis());", name); + sw.WriteLine (); +#endif + + sw.WriteLine ("\t\tandroid.content.Context context = getContext ();"); + sw.WriteLine (); + + if (!string.IsNullOrEmpty (MonoRuntimeInitialization)) { + sw.WriteLine (MonoRuntimeInitialization); + sw.WriteLine (); + } + + extra (sw); + sw.WriteLine ("\t}"); + } + + void GenerateRegisterType (TextWriter sw, JavaCallableWrapperGenerator self, string field, CallableWrapperWriterOptions options) + { + if (!self.HasDynamicallyRegisteredMethods) { + return; + } + + sw.Write ("\t\t"); + sw.Write (field); + sw.WriteLine (" = "); + string managedTypeName = self.type.GetPartialAssemblyQualifiedName (Cache); + string javaTypeName = $"{Package}.{Name}"; + + foreach (Signature method in self.methods) { + if (method.IsDynamicallyRegistered) { + sw.Write ("\t\t\t\"", method.Method); + sw.Write (method.Method); + sw.WriteLine ("\\n\" +"); + } + } + sw.WriteLine ("\t\t\t\"\";"); + if (CannotRegisterInStaticConstructor (self.type)) + return; + sw.Write ("\t\t"); + switch (options.CodeGenerationTarget) { + case JavaPeerStyle.JavaInterop1: + sw.Write ("net.dot.jni.ManagedPeer.registerNativeMembers ("); + sw.Write (self.name); + sw.Write (".class, "); + sw.Write (field); + sw.WriteLine (");"); + break; + default: + sw.Write ("mono.android.Runtime.register (\""); + sw.Write (managedTypeName); + sw.Write ("\", "); + sw.Write (self.name); + sw.Write (".class, "); + sw.Write (field); + sw.WriteLine (");"); + break; + } + } + + bool CannotRegisterInStaticConstructor (TypeDefinition type) + { + return JavaNativeTypeManager.IsApplication (type, Cache) || JavaNativeTypeManager.IsInstrumentation (type, Cache); + } + + static string GetJavaTypeName (TypeReference r, IMetadataResolver cache) + { + TypeDefinition d = cache.Resolve (r); + string? jniName = JavaNativeTypeManager.ToJniName (d, cache); + if (jniName == null) { + Diagnostic.Error (4201, Localization.Resources.JavaCallableWrappers_XA4201, r.FullName); + throw new InvalidOperationException ("--nrt:jniName-- Should not be reached"); + } + return jniName.Replace ('/', '.').Replace ('$', '.'); + } + + /// + /// Returns a destination file path based on the package name of this Java type + /// + public string GetDestinationPath (string outputPath) + { + var dir = Package.Replace ('.', Path.DirectorySeparatorChar); + return Path.Combine (outputPath, dir, Name + ".java"); + } + + public void Generate (string outputPath, CallableWrapperWriterOptions options) + { + using (StreamWriter sw = OpenStream (outputPath)) { + Generate (sw, options, false); + } + } + + StreamWriter OpenStream (string outputPath) + { + string destination = GetDestinationPath (outputPath); + Directory.CreateDirectory (Path.GetDirectoryName (destination)); + return new StreamWriter (new FileStream (destination, FileMode.Create, FileAccess.Write)); + } +} diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperWriterOptions.cs b/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperWriterOptions.cs new file mode 100644 index 000000000..fdb09dd08 --- /dev/null +++ b/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperWriterOptions.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Java.Interop.Tools.JavaCallableWrappers; + +class CallableWrapperWriterOptions +{ + public JavaPeerStyle CodeGenerationTarget { get; set; } +} diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.csproj b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.csproj index fbd075c61..05f576b4d 100644 --- a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.csproj +++ b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.csproj @@ -3,7 +3,7 @@ netstandard2.0 false - 10.0 + 11.0 enable true true diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGenerator.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGenerator.cs index a13ff5230..7a01ed05a 100644 --- a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGenerator.cs +++ b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGenerator.cs @@ -17,6 +17,8 @@ using MethodAttributes = Mono.Cecil.MethodAttributes; using static Java.Interop.Tools.TypeNameMappings.JavaNativeTypeManager; +using Java.Interop.Tools.JavaCallableWrappers.CallableWrapperMembers; +using Java.Interop.Tools.JavaCallableWrappers.Adapters; namespace Java.Interop.Tools.JavaCallableWrappers { @@ -26,12 +28,12 @@ public partial class JavaCallableWrapperGenerator { internal string name; internal string package; internal TypeDefinition type; - List exported_fields = new List (); + internal List exported_fields = new List (); internal List methods = new List (); - List ctors = new List (); - List? children; + internal List ctors = new List (); + internal List? children; - readonly IMetadataResolver cache; + internal readonly IMetadataResolver cache; readonly JavaCallableMethodClassifier? methodClassifier; [Obsolete ("Use the TypeDefinitionCache overload for better performance.", error: true)] @@ -210,7 +212,7 @@ static void ExtractJavaNames (string jniName, out string package, out string typ } } - static SequencePoint? LookupSource (MethodDefinition method) + internal static SequencePoint? LookupSource (MethodDefinition method) { if (!method.HasBody) return null; @@ -542,36 +544,24 @@ string GetManagedParameters (MethodDefinition ctor, string? outerType) public void Generate (string outputPath) { + var options = new CallableWrapperWriterOptions { CodeGenerationTarget = CodeGenerationTarget }; var jcw_writer = CreateWriter (); - jcw_writer.Generate (outputPath); + jcw_writer.Generate (outputPath, options); } public void Generate (TextWriter writer) { + var options = new CallableWrapperWriterOptions { CodeGenerationTarget = CodeGenerationTarget }; var jcw_writer = CreateWriter (); - jcw_writer.Generate (writer); + jcw_writer.Generate (writer, options, false); } - internal JavaCallableWrapperWriter CreateWriter () + internal CallableWrapperType CreateWriter () { - return new JavaCallableWrapperWriter ( - name: name, - package: package, - type: type, - hasDynamicallyRegisteredMethods: HasDynamicallyRegisteredMethods, - children: children, - cache: cache, - applicationJavaClass: ApplicationJavaClass, - codeGenerationTarget: CodeGenerationTarget, - generateOnCreateOverrides: GenerateOnCreateOverrides, - monoRuntimeInitialization: MonoRuntimeInitialization, - exported_fields: exported_fields, - methods: methods, - ctors: ctors, - generator: this); + return CecilImporter.CreateType (this); } - static string GetAnnotationsString (string indent, IEnumerable atts, IMetadataResolver resolver) + internal static string GetAnnotationsString (string indent, IEnumerable atts, IMetadataResolver resolver) { var sw = new StringWriter (); WriteAnnotations (indent, sw, atts, resolver); @@ -607,7 +597,7 @@ internal static void WriteAnnotations (string indent, TextWriter sw, IEnumerable } } - static string GetJavaAccess (MethodAttributes access) + internal static string GetJavaAccess (MethodAttributes access) { switch (access) { case MethodAttributes.Public: diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperWriter.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperWriter.cs deleted file mode 100644 index d8aa09420..000000000 --- a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperWriter.cs +++ /dev/null @@ -1,475 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using Java.Interop.Tools.Cecil; -using Java.Interop.Tools.Diagnostics; -using Java.Interop.Tools.TypeNameMappings; -using Mono.Cecil; - -using JavaFieldInfo = Java.Interop.Tools.JavaCallableWrappers.JavaCallableWrapperGenerator.JavaFieldInfo; -using Signature = Java.Interop.Tools.JavaCallableWrappers.JavaCallableWrapperGenerator.Signature; - -namespace Java.Interop.Tools.JavaCallableWrappers; - -class JavaCallableWrapperWriter -{ - readonly string name; - readonly string package; - readonly TypeDefinition type; - - readonly List? children; - readonly List ctors; - readonly List exported_fields; - readonly List methods; - - readonly IMetadataResolver cache; - readonly JavaCallableWrapperGenerator generator; - - public JavaCallableWrapperWriter (string name, string package, TypeDefinition type, bool hasDynamicallyRegisteredMethods, List? children, IMetadataResolver cache, string? applicationJavaClass, JavaPeerStyle codeGenerationTarget, bool generateOnCreateOverrides, string? monoRuntimeInitialization, List exported_fields, List methods, List ctors, JavaCallableWrapperGenerator generator) - { - this.name = name; - this.package = package; - this.type = type; - HasDynamicallyRegisteredMethods = hasDynamicallyRegisteredMethods; - this.children = children; - this.cache = cache; - ApplicationJavaClass = applicationJavaClass; - CodeGenerationTarget = codeGenerationTarget; - GenerateOnCreateOverrides = generateOnCreateOverrides; - MonoRuntimeInitialization = monoRuntimeInitialization; - this.exported_fields = exported_fields; - this.methods = methods; - this.ctors = ctors; - this.generator = generator; - } - - string? ApplicationJavaClass { get; } - - JavaPeerStyle CodeGenerationTarget { get; } - - bool GenerateOnCreateOverrides { get; } - - bool HasDynamicallyRegisteredMethods { get; } - - string? MonoRuntimeInitialization { get; } - - // example of java target to generate for a type - // - // package mono.droid; - // - // import android.app.Activity; - // import android.os.Bundle; - // - // public class MonoActivity extends android.app.Activity - // { - // static final String __md_methods; - // static { - // __md_methods = - // "n_OnCreate:(Landroid/os/Bundle;)V:GetOnCreate_Landroid_os_Bundle_Handler\n" + - // ""; - // mono.android.Runtime.register ("Mono.Droid.MonoActivity, AssemblyName", MonoActivity.class, __md_methods); - // } - // - // public void onCreate(android.os.Bundle savedInstanceState) - // { - // n_onCreate (savedInstanceState); - // } - // - // private native void n_onCreate (android.os.Bundle bundle); - // } - - public void Generate (TextWriter writer) - { - if (!string.IsNullOrEmpty (package)) { - writer.WriteLine ("package " + package + ";"); - writer.WriteLine (); - } - - GenerateHeader (writer); - - bool needCtor = false; - if (HasDynamicallyRegisteredMethods) { - needCtor = true; - writer.WriteLine ("/** @hide */"); - writer.WriteLine ("\tpublic static final String __md_methods;"); - } - - if (children != null) { - for (int i = 0; i < children.Count; i++) { - if (!children[i].HasDynamicallyRegisteredMethods) { - continue; - } - needCtor = true; - writer.Write ("\tstatic final String __md_"); - writer.Write (i + 1); - writer.WriteLine ("_methods;"); - } - } - - if (needCtor) { - writer.WriteLine ("\tstatic {"); - - if (HasDynamicallyRegisteredMethods) { - GenerateRegisterType (writer, generator, "__md_methods"); - } - - if (children != null) { - for (int i = 0; i < children.Count; ++i) { - GenerateRegisterType (writer, children [i], $"__md_{i + 1}_methods"); - } - } - writer.WriteLine ("\t}"); - } - - GenerateBody (writer); - - if (children != null) - foreach (JavaCallableWrapperGenerator child in children) { - var child_writer = child.CreateWriter (); - child_writer.GenerateHeader (writer); - child_writer.GenerateBody (writer); - child_writer.GenerateFooter (writer); - } - - GenerateFooter (writer); - } - - public void Generate (string outputPath) - { - using (StreamWriter sw = OpenStream (outputPath)) { - Generate (sw); - } - } - - void GenerateHeader (TextWriter sw) - { - sw.WriteLine (); - - // class annotations. - JavaCallableWrapperGenerator.WriteAnnotations ("", sw, type.CustomAttributes, cache); - - sw.WriteLine ("public " + (type.IsAbstract ? "abstract " : "") + "class " + name); - - string extendsType = GetJavaTypeName (type.BaseType, cache); - if (extendsType == "android.app.Application" && ApplicationJavaClass != null && !string.IsNullOrEmpty (ApplicationJavaClass)) - extendsType = ApplicationJavaClass; - sw.WriteLine ("\textends " + extendsType); - sw.WriteLine ("\timplements"); - sw.Write ("\t\t"); - switch (CodeGenerationTarget) { - case JavaPeerStyle.JavaInterop1: - sw.Write ("net.dot.jni.GCUserPeerable"); - break; - default: - sw.Write ("mono.android.IGCUserPeer"); - break; - } - foreach (var ifaceInfo in type.Interfaces) { - var iface = cache.Resolve(ifaceInfo.InterfaceType); - if (!JavaCallableWrapperGenerator.GetTypeRegistrationAttributes (iface).Any ()) - continue; - sw.WriteLine (","); - sw.Write ("\t\t"); - sw.Write (GetJavaTypeName (iface, cache)); - } - sw.WriteLine (); - sw.WriteLine ("{"); - } - - void GenerateBody (TextWriter sw) - { - foreach (Signature ctor in ctors) { - if (string.IsNullOrEmpty (ctor.Params) && JavaNativeTypeManager.IsApplication (type, cache)) - continue; - GenerateConstructor (ctor, sw); - } - - GenerateApplicationConstructor (sw); - - foreach (JavaFieldInfo field in exported_fields) - GenerateExportedField (field, sw); - - foreach (Signature method in methods) - GenerateMethod (method, sw); - - if (GenerateOnCreateOverrides && JavaNativeTypeManager.IsApplication (type, cache) && !methods.Any (m => m.Name == "onCreate")) - WriteApplicationOnCreate (sw, w => { - w.Write ("\t\tmono.android.Runtime.register (\""); - w.Write (type.GetPartialAssemblyQualifiedName (cache)); - w.Write ("\", "); - w.Write (name); - w.WriteLine (".class, __md_methods);"); - w.WriteLine ("\t\tsuper.onCreate ();"); - }); - if (GenerateOnCreateOverrides && JavaNativeTypeManager.IsInstrumentation (type, cache) && !methods.Any (m => m.Name == "onCreate")) - WriteInstrumentationOnCreate (sw, w => { - w.Write ("\t\tmono.android.Runtime.register (\""); - w.Write (type.GetPartialAssemblyQualifiedName (cache)); - w.Write ("\", "); - w.Write (name); - w.WriteLine (".class, __md_methods);"); - w.WriteLine ("\t\tsuper.onCreate (arguments);"); - }); - - string addRef = "monodroidAddReference"; - string clearRefs = "monodroidClearReferences"; - if (CodeGenerationTarget == JavaPeerStyle.JavaInterop1) { - addRef = "jiAddManagedReference"; - clearRefs = "jiClearManagedReferences"; - } - - sw.WriteLine (); - sw.WriteLine ("\tprivate java.util.ArrayList refList;"); - sw.WriteLine ($"\tpublic void {addRef} (java.lang.Object obj)"); - sw.WriteLine ("\t{"); - sw.WriteLine ("\t\tif (refList == null)"); - sw.WriteLine ("\t\t\trefList = new java.util.ArrayList ();"); - sw.WriteLine ("\t\trefList.add (obj);"); - sw.WriteLine ("\t}"); - sw.WriteLine (); - sw.WriteLine ($"\tpublic void {clearRefs} ()"); - sw.WriteLine ("\t{"); - sw.WriteLine ("\t\tif (refList != null)"); - sw.WriteLine ("\t\t\trefList.clear ();"); - sw.WriteLine ("\t}"); - } - - void GenerateRegisterType (TextWriter sw, JavaCallableWrapperGenerator self, string field) - { - if (!self.HasDynamicallyRegisteredMethods) { - return; - } - - sw.Write ("\t\t"); - sw.Write (field); - sw.WriteLine (" = "); - string managedTypeName = self.type.GetPartialAssemblyQualifiedName (cache); - string javaTypeName = $"{package}.{name}"; - - foreach (Signature method in self.methods) { - if (method.IsDynamicallyRegistered) { - sw.Write ("\t\t\t\"", method.Method); - sw.Write (method.Method); - sw.WriteLine ("\\n\" +"); - } - } - sw.WriteLine ("\t\t\t\"\";"); - if (CannotRegisterInStaticConstructor (self.type)) - return; - sw.Write ("\t\t"); - switch (CodeGenerationTarget) { - case JavaPeerStyle.JavaInterop1: - sw.Write ("net.dot.jni.ManagedPeer.registerNativeMembers ("); - sw.Write (self.name); - sw.Write (".class, "); - sw.Write (field); - sw.WriteLine (");"); - break; - default: - sw.Write ("mono.android.Runtime.register (\""); - sw.Write (managedTypeName); - sw.Write ("\", "); - sw.Write (self.name); - sw.Write (".class, "); - sw.Write (field); - sw.WriteLine (");"); - break; - } - } - - void GenerateFooter (TextWriter sw) - { - sw.WriteLine ("}"); - } - - static string GetJavaTypeName (TypeReference r, IMetadataResolver cache) - { - TypeDefinition d = cache.Resolve (r); - string? jniName = JavaNativeTypeManager.ToJniName (d, cache); - if (jniName == null) { - Diagnostic.Error (4201, Localization.Resources.JavaCallableWrappers_XA4201, r.FullName); - throw new InvalidOperationException ("--nrt:jniName-- Should not be reached"); - } - return jniName.Replace ('/', '.').Replace ('$', '.'); - } - - bool CannotRegisterInStaticConstructor (TypeDefinition type) - { - return JavaNativeTypeManager.IsApplication (type, cache) || JavaNativeTypeManager.IsInstrumentation (type, cache); - } - - void GenerateConstructor (Signature ctor, TextWriter sw) - { - // TODO: we only generate constructors so that Android types w/ no - // default constructor can be subclasses by our generated code. - // - // This does NOT currently allow creating managed types from Java. - sw.WriteLine (); - if (ctor.Annotations != null) - sw.WriteLine (ctor.Annotations); - sw.Write ("\tpublic "); - sw.Write (name); - sw.Write (" ("); - sw.Write (ctor.Params); - sw.Write (')'); - sw.WriteLine (ctor.ThrowsDeclaration); - sw.WriteLine ("\t{"); - sw.Write ("\t\tsuper ("); - sw.Write (ctor.SuperCall); - sw.WriteLine (");"); -#if MONODROID_TIMING - sw.WriteLine ("\t\tandroid.util.Log.i(\"MonoDroid-Timing\", \"{0}..ctor({1}): time: \"+java.lang.System.currentTimeMillis());", name, ctor.Params); -#endif - if (!CannotRegisterInStaticConstructor (type)) { - sw.Write ("\t\tif (getClass () == "); - sw.Write (name); - sw.WriteLine (".class) {"); - sw.Write ("\t\t\t"); - switch (CodeGenerationTarget) { - case JavaPeerStyle.JavaInterop1: - sw.Write ("net.dot.jni.ManagedPeer.construct (this, \""); - sw.Write (ctor.JniSignature); - sw.Write ("\", new java.lang.Object[] { "); - sw.Write (ctor.ActivateCall); - sw.WriteLine (" });"); - break; - default: - sw.Write ("mono.android.TypeManager.Activate (\""); - sw.Write (type.GetPartialAssemblyQualifiedName (cache)); - sw.Write ("\", \""); - sw.Write (ctor.ManagedParameters); - sw.Write ("\", this, new java.lang.Object[] { "); - sw.Write (ctor.ActivateCall); - sw.WriteLine (" });"); - break; - } - sw.WriteLine ("\t\t}"); - } - sw.WriteLine ("\t}"); - } - - void GenerateApplicationConstructor (TextWriter sw) - { - if (!JavaNativeTypeManager.IsApplication (type, cache)) { - return; - } - - sw.WriteLine (); - sw.Write ("\tpublic "); - sw.Write (name); - sw.WriteLine (" ()"); - sw.WriteLine ("\t{"); - sw.WriteLine ("\t\tmono.MonoPackageManager.setContext (this);"); - sw.WriteLine ("\t}"); - } - - void GenerateExportedField (JavaFieldInfo field, TextWriter sw) - { - sw.WriteLine (); - if (field.Annotations != null) - sw.WriteLine (field.Annotations); - sw.Write ("\t"); - sw.Write (field.GetJavaAccess ()); - sw.Write (' '); - if (field.IsStatic) - sw.Write ("static "); - sw.Write (field.TypeName); - sw.Write (' '); - sw.Write (field.FieldName); - sw.Write (" = "); - sw.Write (field.InitializerName); - sw.WriteLine (" ();"); - } - - void GenerateMethod (Signature method, TextWriter sw) - { - sw.WriteLine (); - if (method.Annotations != null) - sw.WriteLine (method.Annotations); - sw.Write ("\t"); - sw.Write (method.IsExport ? method.JavaAccess : "public"); - sw.Write (' '); - if (method.IsStatic) - sw.Write ("static "); - sw.Write (method.Retval); - sw.Write (' '); - sw.Write (method.JavaName); - sw.Write (" ("); - sw.Write (method.Params); - sw.Write (')'); - sw.WriteLine (method.ThrowsDeclaration); - sw.WriteLine ("\t{"); -#if MONODROID_TIMING - sw.WriteLine ("\t\tandroid.util.Log.i(\"MonoDroid-Timing\", \"{0}.{1}: time: \"+java.lang.System.currentTimeMillis());", name, method.Name); -#endif - sw.Write ("\t\t"); - sw.Write (method.Retval == "void" ? String.Empty : "return "); - sw.Write ("n_"); - sw.Write (method.Name); - sw.Write (" ("); - sw.Write (method.ActivateCall); - sw.WriteLine (");"); - - sw.WriteLine ("\t}"); - sw.WriteLine (); - sw.Write ("\tprivate "); - if (method.IsStatic) - sw.Write ("static "); - sw.Write ("native "); - sw.Write (method.Retval); - sw.Write (" n_"); - sw.Write (method.Name); - sw.Write (" ("); - sw.Write (method.Params); - sw.WriteLine (");"); - } - - void WriteApplicationOnCreate (TextWriter sw, Action extra) - { - sw.WriteLine (); - sw.WriteLine ("\tpublic void onCreate ()"); - sw.WriteLine ("\t{"); - extra (sw); - sw.WriteLine ("\t}"); - } - - void WriteInstrumentationOnCreate (TextWriter sw, Action extra) - { - sw.WriteLine (); - sw.WriteLine ("\tpublic void onCreate (android.os.Bundle arguments)"); - sw.WriteLine ("\t{"); - -#if MONODROID_TIMING - sw.WriteLine ("\t\tandroid.util.Log.i(\"MonoDroid-Timing\", \"{0}.onCreate(Bundle): time: \"+java.lang.System.currentTimeMillis());", name); - sw.WriteLine (); -#endif - - sw.WriteLine ("\t\tandroid.content.Context context = getContext ();"); - sw.WriteLine (); - - if (!string.IsNullOrEmpty (MonoRuntimeInitialization)) { - sw.WriteLine (MonoRuntimeInitialization); - sw.WriteLine (); - } - - extra (sw); - sw.WriteLine ("\t}"); - } - - StreamWriter OpenStream (string outputPath) - { - string destination = GetDestinationPath (outputPath); - Directory.CreateDirectory (Path.GetDirectoryName (destination)); - return new StreamWriter (new FileStream (destination, FileMode.Create, FileAccess.Write)); - } - - /// - /// Returns a destination file path based on the package name of this Java type - /// - public string GetDestinationPath (string outputPath) - { - var dir = package.Replace ('.', Path.DirectorySeparatorChar); - return Path.Combine (outputPath, dir, name + ".java"); - } -} From 08d50ffe76eedb918aaba0630f94db63a74a7568 Mon Sep 17 00:00:00 2001 From: Jonathan Pobst Date: Tue, 12 Dec 2023 09:18:08 -1000 Subject: [PATCH 3/9] Complete generating Java source code from intermediate model. --- .../Adapters/CecilImporter.cs | 145 ++++++++++++-- .../CallableWrapperField.cs | 20 +- .../CallableWrapperType.cs | 187 +++++++----------- .../CallableWrapperTypeAnnotation.cs | 33 ++++ 4 files changed, 244 insertions(+), 141 deletions(-) create mode 100644 src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperTypeAnnotation.cs diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Adapters/CecilImporter.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Adapters/CecilImporter.cs index 8a4f6e670..2699dbb6d 100644 --- a/src/Java.Interop.Tools.JavaCallableWrappers/Adapters/CecilImporter.cs +++ b/src/Java.Interop.Tools.JavaCallableWrappers/Adapters/CecilImporter.cs @@ -1,7 +1,9 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Text; -using System.Xml.Linq; using Android.Runtime; +using Java.Interop.Tools.Cecil; using Java.Interop.Tools.Diagnostics; using Java.Interop.Tools.JavaCallableWrappers.CallableWrapperMembers; using Java.Interop.Tools.TypeNameMappings; @@ -18,25 +20,19 @@ public static CallableWrapperField CreateField (MethodDefinition method, string ?? throw new ArgumentException ($"Could not get JNI signature for method `{method.Name}`", nameof (method)); var annotations = JavaCallableWrapperGenerator.GetAnnotationsString ("\t", method.CustomAttributes, resolver); - return new CallableWrapperField ( - fieldName: fieldName, - typeName: type_name, - visibility: visibility, - isStatic: method.IsStatic, - initializerName: method.Name, - annotations: annotations); + return new CallableWrapperField (fieldName, type_name, visibility, method.Name) { + IsStatic = method.IsStatic, + Annotations = annotations + }; } // Temporary conversion function public static CallableWrapperField CreateField (JavaCallableWrapperGenerator.JavaFieldInfo field) { - return new CallableWrapperField ( - fieldName: field.FieldName, - typeName: field.TypeName, - visibility: field.GetJavaAccess (), - isStatic: field.IsStatic, - initializerName: field.InitializerName, - annotations: field.Annotations); + return new CallableWrapperField (field.FieldName, field.TypeName, field.GetJavaAccess (), field.InitializerName) { + IsStatic = field.IsStatic, + Annotations = field.Annotations + }; } public static CallableWrapperMethod CreateMethod (MethodDefinition method, RegisterAttribute register, IMetadataResolver cache, bool shouldBeDynamicallyRegistered = true) @@ -190,21 +186,132 @@ public static CallableWrapperConstructor CreateConstructor (JavaCallableWrapperG // Temporary conversion function public static CallableWrapperType CreateType (JavaCallableWrapperGenerator generator) { - var type = new CallableWrapperType (generator.name, generator.package) { - Type = generator.type, - Cache = generator.cache, + var partial_assembly_qualified_name = generator.type.GetPartialAssemblyQualifiedName (generator.cache); + + var type = new CallableWrapperType (generator.name, generator.package, partial_assembly_qualified_name) { IsAbstract = generator.type.IsAbstract, ApplicationJavaClass = generator.ApplicationJavaClass, - Generator = generator, HasDynamicallyRegisteredMethods = generator.HasDynamicallyRegisteredMethods, GenerateOnCreateOverrides = generator.GenerateOnCreateOverrides, MonoRuntimeInitialization = generator.MonoRuntimeInitialization, + IsApplication = JavaNativeTypeManager.IsApplication (generator.type, generator.cache), + IsInstrumentation = JavaNativeTypeManager.IsInstrumentation (generator.type, generator.cache), }; + type.Annotations.AddRange (CreateTypeAnnotations (generator.type, generator.cache)); + + // Extends + var extendsType = GetJavaTypeName (generator.type.BaseType, generator.cache); + + if (extendsType == "android.app.Application" && generator.ApplicationJavaClass != null && !string.IsNullOrEmpty (generator.ApplicationJavaClass)) + extendsType = generator.ApplicationJavaClass; + + type.ExtendsType = extendsType; + + // Implemented interfaces + foreach (var ifaceInfo in generator.type.Interfaces) { + var iface = generator.cache.Resolve (ifaceInfo.InterfaceType); + + if (!JavaCallableWrapperGenerator.GetTypeRegistrationAttributes (iface).Any ()) + continue; + + type.ImplementedInterfaces.Add (GetJavaTypeName (iface, generator.cache)); + } + + // Type constructors + foreach (var ctor in generator.ctors) { + if (string.IsNullOrEmpty (ctor.Params) && type.IsApplication) + continue; + + var ct = CreateConstructor (ctor); + + ct.Name = type.Name; + ct.CannotRegisterInStaticConstructor = type.CannotRegisterInStaticConstructor; + ct.PartialAssemblyQualifiedName = type.PartialAssemblyQualifiedName ; + + type.Constructors.Add (ct); + } + + // Application constructor + if (CreateApplicationConstructor (type.Name, generator.type, generator.cache) is CallableWrapperApplicationConstructor app_ctor) + type.ApplicationConstructor = app_ctor; + + // Exported fields + foreach (var field in generator.exported_fields) + type.Fields.Add (CreateField (field)); + + // Methods + foreach (var method in generator.methods) + type.Methods.Add (CreateMethod (method)); + + // Nested types if (generator.children is not null) foreach (var nested in generator.children) type.NestedTypes.Add (CreateType (nested)); return type; } + + static string GetJavaTypeName (TypeReference r, IMetadataResolver cache) + { + TypeDefinition d = cache.Resolve (r); + string? jniName = JavaNativeTypeManager.ToJniName (d, cache); + if (jniName == null) { + Diagnostic.Error (4201, Localization.Resources.JavaCallableWrappers_XA4201, r.FullName); + throw new InvalidOperationException ("--nrt:jniName-- Should not be reached"); + } + return jniName.Replace ('/', '.').Replace ('$', '.'); + } + + public static IEnumerable CreateTypeAnnotations (TypeDefinition type, IMetadataResolver resolver) + { + foreach (var ca in type.CustomAttributes) { + var annotation = CreateTypeAnnotation (ca, resolver); + + if (annotation is not null) + yield return annotation; + } + } + + public static CallableWrapperTypeAnnotation? CreateTypeAnnotation (CustomAttribute ca, IMetadataResolver resolver) + { + var catype = resolver.Resolve (ca.AttributeType); + var tca = catype.CustomAttributes.FirstOrDefault (a => a.AttributeType.FullName == "Android.Runtime.AnnotationAttribute"); + + if (tca is null) + return null; + + var name_object = tca.ConstructorArguments [0].Value; + + // Should never be hit + if (name_object is not string name) + throw new ArgumentException ($"Expected a string for the first argument of the {nameof (RegisterAttribute)} constructor.", nameof (ca)); + + var annotation = new CallableWrapperTypeAnnotation (name); + + foreach (var p in ca.Properties) { + var pd = catype.Properties.FirstOrDefault (pp => pp.Name == p.Name); + var reg = pd != null ? pd.CustomAttributes.FirstOrDefault (pdca => pdca.AttributeType.FullName == "Android.Runtime.RegisterAttribute") : null; + + var key = reg != null ? (string) reg.ConstructorArguments [0].Value : p.Name; + var value = ManagedValueToJavaSource (p.Argument.Value); + + annotation.Properties.Add (new System.Collections.Generic.KeyValuePair (key, value)); + } + + return annotation; + } + + // FIXME: this is hacky. Is there any existing code for value to source conversion? + static string ManagedValueToJavaSource (object value) + { + if (value is string) + return "\"" + value.ToString ().Replace ("\"", "\"\"") + '"'; + else if (value.GetType ().FullName == "Java.Lang.Class") + return value.ToString () + ".class"; + else if (value is bool) + return ((bool) value) ? "true" : "false"; + else + return value.ToString (); + } } diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperField.cs b/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperField.cs index 54928b095..27410946d 100644 --- a/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperField.cs +++ b/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperField.cs @@ -4,24 +4,22 @@ namespace Java.Interop.Tools.JavaCallableWrappers.CallableWrapperMembers; class CallableWrapperField { - public string FieldName { get; } - public string TypeName { get; } - public string Visibility { get; } - public bool IsStatic { get; } - public string InitializerName { get; } - public string? Annotations { get; } - - public CallableWrapperField (string fieldName, string typeName, string visibility, bool isStatic, string initializerName, string? annotations) + public string FieldName { get; set; } + public string TypeName { get; set; } + public string Visibility { get; set; } + public bool IsStatic { get; set; } + public string InitializerName { get; set; } + public string? Annotations { get; set; } + + public CallableWrapperField (string fieldName, string typeName, string visibility, string initializerName) { FieldName = fieldName; TypeName = typeName; Visibility = visibility; - IsStatic = isStatic; InitializerName = initializerName; - Annotations = annotations; } - public void Generate (TextWriter sw) + public void Generate (TextWriter sw, CallableWrapperWriterOptions options) { sw.WriteLine (); diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperType.cs b/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperType.cs index 6609b2a7e..7092c85b1 100644 --- a/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperType.cs +++ b/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperType.cs @@ -1,14 +1,6 @@ -using System; using System.Collections.Generic; using System.IO; using System.Linq; -using Java.Interop.Tools.Cecil; -using System.Xml.Linq; -using Java.Interop.Tools.Diagnostics; -using Java.Interop.Tools.TypeNameMappings; -using Mono.Cecil; -using static Java.Interop.Tools.JavaCallableWrappers.JavaCallableWrapperGenerator; -using Java.Interop.Tools.JavaCallableWrappers.Adapters; namespace Java.Interop.Tools.JavaCallableWrappers.CallableWrapperMembers; @@ -21,21 +13,26 @@ class CallableWrapperType public bool HasDynamicallyRegisteredMethods { get; set; } public bool GenerateOnCreateOverrides { get; set; } public string? MonoRuntimeInitialization { get; set; } - + public string? ExtendsType { get; set; } + public CallableWrapperApplicationConstructor? ApplicationConstructor { get; set; } + public bool IsApplication { get; set; } + public bool IsInstrumentation { get; set; } + public string PartialAssemblyQualifiedName { get; set; } + + public List Annotations { get; } = new List (); + public List ImplementedInterfaces { get; } = new List (); public List Constructors { get; } = new List (); public List Fields { get; } = new List (); public List Methods { get; } = new List (); public List NestedTypes { get; } = new List (); - // TODO: Remove Cecil - public TypeDefinition Type { get; set; } = null!; - public IMetadataResolver Cache { get; set; } = null!; - public JavaCallableWrapperGenerator Generator { get; set; } = null!; + public bool CannotRegisterInStaticConstructor => IsApplication || IsInstrumentation; - public CallableWrapperType (string name, string package) + public CallableWrapperType (string name, string package, string partialAssemblyQualifiedName) { Name = name; Package = package; + PartialAssemblyQualifiedName = partialAssemblyQualifiedName; } // example of java target to generate for a type @@ -87,19 +84,17 @@ public void GenerateHeader (TextWriter sw, CallableWrapperWriterOptions options) { sw.WriteLine (); - // class annotations. - JavaCallableWrapperGenerator.WriteAnnotations ("", sw, Type.CustomAttributes, Cache); + // Type annotations + foreach (var annotation in Annotations) + annotation.Generate (sw, "", options); sw.WriteLine ("public " + (IsAbstract ? "abstract " : "") + "class " + Name); - var extendsType = GetJavaTypeName (Type.BaseType, Cache); - - if (extendsType == "android.app.Application" && ApplicationJavaClass != null && !string.IsNullOrEmpty (ApplicationJavaClass)) - extendsType = ApplicationJavaClass; + sw.WriteLine ("\textends " + ExtendsType); - sw.WriteLine ("\textends " + extendsType); sw.WriteLine ("\timplements"); sw.Write ("\t\t"); + switch (options.CodeGenerationTarget) { case JavaPeerStyle.JavaInterop1: sw.Write ("net.dot.jni.GCUserPeerable"); @@ -108,14 +103,13 @@ public void GenerateHeader (TextWriter sw, CallableWrapperWriterOptions options) sw.Write ("mono.android.IGCUserPeer"); break; } - foreach (var ifaceInfo in Type.Interfaces) { - var iface = Cache.Resolve (ifaceInfo.InterfaceType); - if (!JavaCallableWrapperGenerator.GetTypeRegistrationAttributes (iface).Any ()) - continue; + + foreach (var iface in ImplementedInterfaces) { sw.WriteLine (","); sw.Write ("\t\t"); - sw.Write (GetJavaTypeName (iface, Cache)); + sw.Write (iface); } + sw.WriteLine (); sw.WriteLine ("{"); } @@ -130,10 +124,10 @@ public void GenerateInfrastructure (TextWriter writer, CallableWrapperWriterOpti writer.WriteLine ("\tpublic static final String __md_methods;"); } - for (int i = 0; i < NestedTypes.Count; i++) { - if (!NestedTypes [i].HasDynamicallyRegisteredMethods) { + for (var i = 0; i < NestedTypes.Count; i++) { + if (!NestedTypes [i].HasDynamicallyRegisteredMethods) continue; - } + needCtor = true; writer.Write ("\tstatic final String __md_"); writer.Write (i + 1); @@ -143,13 +137,11 @@ public void GenerateInfrastructure (TextWriter writer, CallableWrapperWriterOpti if (needCtor) { writer.WriteLine ("\tstatic {"); - if (HasDynamicallyRegisteredMethods) { - GenerateRegisterType (writer, Generator, "__md_methods", options); - } + if (HasDynamicallyRegisteredMethods) + GenerateRegisterType (writer, this, "__md_methods", options); - for (int i = 0; i < NestedTypes.Count; ++i) { - GenerateRegisterType (writer, NestedTypes [i].Generator, $"__md_{i + 1}_methods", options); - } + for (var i = 0; i < NestedTypes.Count; ++i) + GenerateRegisterType (writer, NestedTypes [i], $"__md_{i + 1}_methods", options); writer.WriteLine ("\t}"); } @@ -157,56 +149,29 @@ public void GenerateInfrastructure (TextWriter writer, CallableWrapperWriterOpti public void GenerateBody (TextWriter sw, CallableWrapperWriterOptions options) { - foreach (Signature ctor in Generator.ctors) { - if (string.IsNullOrEmpty (ctor.Params) && JavaNativeTypeManager.IsApplication (Type, Cache)) - continue; + foreach (var ctor in Constructors) + ctor.Generate (sw, options); - var ct = CecilImporter.CreateConstructor (ctor); + ApplicationConstructor?.Generate (sw, options); - ct.Name = Name; - ct.CannotRegisterInStaticConstructor = CannotRegisterInStaticConstructor (Type); - ct.PartialAssemblyQualifiedName = Type.GetPartialAssemblyQualifiedName (Cache); + foreach (var field in Fields) + field.Generate (sw, options); - ct.Generate (sw, options); - } + foreach (var method in Methods) + method.Generate (sw, options); - if (CecilImporter.CreateApplicationConstructor (Name, Type, Cache) is CallableWrapperApplicationConstructor app_ctor) - app_ctor.Generate (sw, options); - - foreach (JavaFieldInfo field in Generator.exported_fields) - CecilImporter.CreateField (field).Generate (sw); - - foreach (Signature method in Generator.methods) - CecilImporter.CreateMethod (method).Generate (sw, options); - - if (GenerateOnCreateOverrides && JavaNativeTypeManager.IsApplication (Type, Cache) && !Generator.methods.Any (m => m.Name == "onCreate")) - WriteApplicationOnCreate (sw, w => { - w.Write ("\t\tmono.android.Runtime.register (\""); - w.Write (Type.GetPartialAssemblyQualifiedName (Cache)); - w.Write ("\", "); - w.Write (Name); - w.WriteLine (".class, __md_methods);"); - w.WriteLine ("\t\tsuper.onCreate ();"); - }); - if (GenerateOnCreateOverrides && JavaNativeTypeManager.IsInstrumentation (Type, Cache) && !Generator.methods.Any (m => m.Name == "onCreate")) - WriteInstrumentationOnCreate (sw, w => { - w.Write ("\t\tmono.android.Runtime.register (\""); - w.Write (Type.GetPartialAssemblyQualifiedName (Cache)); - w.Write ("\", "); - w.Write (Name); - w.WriteLine (".class, __md_methods);"); - w.WriteLine ("\t\tsuper.onCreate (arguments);"); - }); - - string addRef = "monodroidAddReference"; - string clearRefs = "monodroidClearReferences"; - if (options.CodeGenerationTarget == JavaPeerStyle.JavaInterop1) { - addRef = "jiAddManagedReference"; - clearRefs = "jiClearManagedReferences"; - } + if (GenerateOnCreateOverrides && IsApplication && !Methods.Any (m => m.Name == "onCreate")) + WriteApplicationOnCreate (sw, options); + + if (GenerateOnCreateOverrides && IsInstrumentation && !Methods.Any (m => m.Name == "onCreate")) + WriteInstrumentationOnCreate (sw, options); + + var addRef = options.CodeGenerationTarget == JavaPeerStyle.JavaInterop1 ? "jiAddManagedReference" : "monodroidAddReference"; + var clearRefs = options.CodeGenerationTarget == JavaPeerStyle.JavaInterop1 ? "jiClearManagedReferences" : "monodroidClearReferences"; sw.WriteLine (); sw.WriteLine ("\tprivate java.util.ArrayList refList;"); + sw.WriteLine ($"\tpublic void {addRef} (java.lang.Object obj)"); sw.WriteLine ("\t{"); sw.WriteLine ("\t\tif (refList == null)"); @@ -214,6 +179,7 @@ public void GenerateBody (TextWriter sw, CallableWrapperWriterOptions options) sw.WriteLine ("\t\trefList.add (obj);"); sw.WriteLine ("\t}"); sw.WriteLine (); + sw.WriteLine ($"\tpublic void {clearRefs} ()"); sw.WriteLine ("\t{"); sw.WriteLine ("\t\tif (refList != null)"); @@ -226,16 +192,24 @@ public void GenerateFooter (TextWriter sw, CallableWrapperWriterOptions options) sw.WriteLine ("}"); } - void WriteApplicationOnCreate (TextWriter sw, Action extra) + void WriteApplicationOnCreate (TextWriter sw, CallableWrapperWriterOptions options) { sw.WriteLine (); + sw.WriteLine ("\tpublic void onCreate ()"); sw.WriteLine ("\t{"); - extra (sw); + + sw.Write ("\t\tmono.android.Runtime.register (\""); + sw.Write (PartialAssemblyQualifiedName); + sw.Write ("\", "); + sw.Write (Name); + sw.WriteLine (".class, __md_methods);"); + + sw.WriteLine ("\t\tsuper.onCreate ();"); sw.WriteLine ("\t}"); } - void WriteInstrumentationOnCreate (TextWriter sw, Action extra) + void WriteInstrumentationOnCreate (TextWriter sw, CallableWrapperWriterOptions options) { sw.WriteLine (); sw.WriteLine ("\tpublic void onCreate (android.os.Bundle arguments)"); @@ -254,46 +228,53 @@ void WriteInstrumentationOnCreate (TextWriter sw, Action extra) sw.WriteLine (); } - extra (sw); + sw.Write ("\t\tmono.android.Runtime.register (\""); + sw.Write (PartialAssemblyQualifiedName); + sw.Write ("\", "); + sw.Write (Name); + sw.WriteLine (".class, __md_methods);"); + + sw.WriteLine ("\t\tsuper.onCreate (arguments);"); sw.WriteLine ("\t}"); } - void GenerateRegisterType (TextWriter sw, JavaCallableWrapperGenerator self, string field, CallableWrapperWriterOptions options) + void GenerateRegisterType (TextWriter sw, CallableWrapperType self, string field, CallableWrapperWriterOptions options) { - if (!self.HasDynamicallyRegisteredMethods) { + if (!self.HasDynamicallyRegisteredMethods) return; - } sw.Write ("\t\t"); sw.Write (field); sw.WriteLine (" = "); - string managedTypeName = self.type.GetPartialAssemblyQualifiedName (Cache); - string javaTypeName = $"{Package}.{Name}"; - foreach (Signature method in self.methods) { + foreach (var method in self.Methods) { if (method.IsDynamicallyRegistered) { sw.Write ("\t\t\t\"", method.Method); sw.Write (method.Method); sw.WriteLine ("\\n\" +"); } } + sw.WriteLine ("\t\t\t\"\";"); - if (CannotRegisterInStaticConstructor (self.type)) + + if (CannotRegisterInStaticConstructor) return; + sw.Write ("\t\t"); + switch (options.CodeGenerationTarget) { case JavaPeerStyle.JavaInterop1: sw.Write ("net.dot.jni.ManagedPeer.registerNativeMembers ("); - sw.Write (self.name); + sw.Write (self.Name); sw.Write (".class, "); sw.Write (field); sw.WriteLine (");"); break; default: sw.Write ("mono.android.Runtime.register (\""); - sw.Write (managedTypeName); + sw.Write (self.PartialAssemblyQualifiedName); sw.Write ("\", "); - sw.Write (self.name); + sw.Write (self.Name); sw.Write (".class, "); sw.Write (field); sw.WriteLine (");"); @@ -301,22 +282,6 @@ void GenerateRegisterType (TextWriter sw, JavaCallableWrapperGenerator self, str } } - bool CannotRegisterInStaticConstructor (TypeDefinition type) - { - return JavaNativeTypeManager.IsApplication (type, Cache) || JavaNativeTypeManager.IsInstrumentation (type, Cache); - } - - static string GetJavaTypeName (TypeReference r, IMetadataResolver cache) - { - TypeDefinition d = cache.Resolve (r); - string? jniName = JavaNativeTypeManager.ToJniName (d, cache); - if (jniName == null) { - Diagnostic.Error (4201, Localization.Resources.JavaCallableWrappers_XA4201, r.FullName); - throw new InvalidOperationException ("--nrt:jniName-- Should not be reached"); - } - return jniName.Replace ('/', '.').Replace ('$', '.'); - } - /// /// Returns a destination file path based on the package name of this Java type /// @@ -328,15 +293,15 @@ public string GetDestinationPath (string outputPath) public void Generate (string outputPath, CallableWrapperWriterOptions options) { - using (StreamWriter sw = OpenStream (outputPath)) { + using (StreamWriter sw = OpenStream (outputPath)) Generate (sw, options, false); - } } StreamWriter OpenStream (string outputPath) { - string destination = GetDestinationPath (outputPath); + var destination = GetDestinationPath (outputPath); Directory.CreateDirectory (Path.GetDirectoryName (destination)); + return new StreamWriter (new FileStream (destination, FileMode.Create, FileAccess.Write)); } } diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperTypeAnnotation.cs b/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperTypeAnnotation.cs new file mode 100644 index 000000000..d30c6a8d0 --- /dev/null +++ b/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperTypeAnnotation.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace Java.Interop.Tools.JavaCallableWrappers.CallableWrapperMembers; + +class CallableWrapperTypeAnnotation +{ + public string Name { get; set; } + public List> Properties { get; } = new (); + + public CallableWrapperTypeAnnotation (string name) + { + Name = name; + } + + public void Generate (TextWriter sw, string indent, CallableWrapperWriterOptions options) + { + sw.Write (indent); + sw.Write ('@'); + sw.Write (Name); + + var properties = string.Join (", ", Properties.Select (p => $"{p.Key} = {p.Value}")); + + if (!string.IsNullOrEmpty (properties)) { + sw.Write (" ("); + sw.Write (properties); + sw.Write (")"); + } + + sw.WriteLine (); + } +} From f43c462794cf584acd5adbb45290f07fee9e0ff8 Mon Sep 17 00:00:00 2001 From: Jonathan Pobst Date: Thu, 14 Dec 2023 13:19:52 -1000 Subject: [PATCH 4/9] =?UTF-8?q?=EF=BB=BFFinish=20refactoring.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Adapters/CecilImporter.cs | 502 ++++++++++---- .../CallableWrapperApplicationConstructor.cs | 2 +- .../CallableWrapperConstructor.cs | 6 +- .../CallableWrapperField.cs | 9 +- .../CallableWrapperMethod.cs | 16 +- .../CallableWrapperType.cs | 35 +- .../CallableWrapperTypeAnnotation.cs | 2 +- .../CallableWrapperWriterOptions.cs | 2 +- ...aCallableWrapperGenerator.JavaFieldInfo.cs | 37 -- .../JavaCallableWrapperGenerator.Signature.cs | 134 ---- .../JavaCallableWrapperGenerator.cs | 627 ------------------ .../Utilities/CecilExtensions.cs | 237 +++++++ .../Crc64.Table.cs | 0 .../Crc64.cs | 0 .../Crc64Helper.cs | 0 .../IdentifierValidator.cs | 0 .../JavaCallableMethodClassifier.cs | 0 .../JavaPeerStyle.cs | 0 .../JavaTypeScanner.cs | 3 +- .../TypeNameMapGenerator.cs | 0 .../JavaCallableWrapperGeneratorTests.cs | 42 +- tools/jcw-gen/App.cs | 10 +- 22 files changed, 680 insertions(+), 984 deletions(-) delete mode 100644 src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGenerator.JavaFieldInfo.cs delete mode 100644 src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGenerator.Signature.cs delete mode 100644 src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGenerator.cs create mode 100644 src/Java.Interop.Tools.JavaCallableWrappers/Utilities/CecilExtensions.cs rename src/Java.Interop.Tools.JavaCallableWrappers/{Java.Interop.Tools.JavaCallableWrappers => Utilities}/Crc64.Table.cs (100%) rename src/Java.Interop.Tools.JavaCallableWrappers/{Java.Interop.Tools.JavaCallableWrappers => Utilities}/Crc64.cs (100%) rename src/Java.Interop.Tools.JavaCallableWrappers/{Java.Interop.Tools.JavaCallableWrappers => Utilities}/Crc64Helper.cs (100%) rename src/Java.Interop.Tools.JavaCallableWrappers/{Java.Interop.Tools.JavaCallableWrappers => Utilities}/IdentifierValidator.cs (100%) rename src/Java.Interop.Tools.JavaCallableWrappers/{Java.Interop.Tools.JavaCallableWrappers => Utilities}/JavaCallableMethodClassifier.cs (100%) rename src/Java.Interop.Tools.JavaCallableWrappers/{Java.Interop.Tools.JavaCallableWrappers => Utilities}/JavaPeerStyle.cs (100%) rename src/Java.Interop.Tools.JavaCallableWrappers/{Java.Interop.Tools.JavaCallableWrappers => Utilities}/JavaTypeScanner.cs (96%) rename src/Java.Interop.Tools.JavaCallableWrappers/{Java.Interop.Tools.JavaCallableWrappers => Utilities}/TypeNameMapGenerator.cs (100%) diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Adapters/CecilImporter.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Adapters/CecilImporter.cs index 2699dbb6d..8ae057585 100644 --- a/src/Java.Interop.Tools.JavaCallableWrappers/Adapters/CecilImporter.cs +++ b/src/Java.Interop.Tools.JavaCallableWrappers/Adapters/CecilImporter.cs @@ -6,99 +6,286 @@ using Java.Interop.Tools.Cecil; using Java.Interop.Tools.Diagnostics; using Java.Interop.Tools.JavaCallableWrappers.CallableWrapperMembers; +using Java.Interop.Tools.JavaCallableWrappers.Utilities; using Java.Interop.Tools.TypeNameMappings; using Mono.Cecil; namespace Java.Interop.Tools.JavaCallableWrappers.Adapters; -class CecilImporter +public class CecilImporter { - public static CallableWrapperField CreateField (MethodDefinition method, string fieldName, IMetadataResolver resolver) + public static CallableWrapperType CreateType (TypeDefinition type, IMetadataResolver resolver, string? outerType = null, JavaCallableMethodClassifier? methodClassifier = null) { - var visibility = JavaCallableWrapperGenerator.GetJavaAccess (method.Attributes & MethodAttributes.MemberAccessMask); + if (type.IsEnum || type.IsInterface || type.IsValueType) + Diagnostic.Error (4200, CecilExtensions.LookupSource (type), Localization.Resources.JavaCallableWrappers_XA4200, type.FullName); + + var jniName = JavaNativeTypeManager.ToJniName (type, resolver); + + if (jniName is null) + Diagnostic.Error (4201, CecilExtensions.LookupSource (type), Localization.Resources.JavaCallableWrappers_XA4201, type.FullName); + + if (outerType != null && !string.IsNullOrEmpty (outerType)) { + jniName = jniName.Substring (outerType.Length + 1); + ExtractJavaNames (outerType, out var p, out outerType); + } + + ExtractJavaNames (jniName, out var package, out var name); + + if (string.IsNullOrEmpty (package) && + (type.IsSubclassOf ("Android.App.Activity", resolver) || + type.IsSubclassOf ("Android.App.Application", resolver) || + type.IsSubclassOf ("Android.App.Service", resolver) || + type.IsSubclassOf ("Android.Content.BroadcastReceiver", resolver) || + type.IsSubclassOf ("Android.Content.ContentProvider", resolver))) + Diagnostic.Error (4203, CecilExtensions.LookupSource (type), Localization.Resources.JavaCallableWrappers_XA4203, jniName); + + var cwt = new CallableWrapperType (name, package, type.GetPartialAssemblyQualifiedName (resolver)) { + IsApplication = JavaNativeTypeManager.IsApplication (type, resolver), + IsInstrumentation = JavaNativeTypeManager.IsInstrumentation (type, resolver), + IsAbstract = type.IsAbstract, + }; + + // Type annotations + cwt.Annotations.AddRange (CreateAnnotations (type, resolver)); + + // Extends + cwt.ExtendsType = GetJavaTypeName (type.BaseType, resolver); + + // Implemented interfaces + foreach (var ifaceInfo in type.Interfaces) { + var iface = resolver.Resolve (ifaceInfo.InterfaceType); + + if (!CecilExtensions.GetTypeRegistrationAttributes (iface).Any ()) + continue; + + cwt.ImplementedInterfaces.Add (GetJavaTypeName (iface, resolver)); + } + + // Application constructor + if (CreateApplicationConstructor (cwt.Name, type, resolver) is CallableWrapperApplicationConstructor app_ctor) + cwt.ApplicationConstructor = app_ctor; + + // Methods + foreach (var minfo in type.Methods.Where (m => !m.IsConstructor)) { + var baseRegisteredMethod = CecilExtensions.GetBaseRegisteredMethod (minfo, resolver); + + if (baseRegisteredMethod is not null) + AddMethod (cwt, type, baseRegisteredMethod, minfo, methodClassifier, resolver); + else if (minfo.AnyCustomAttributes ("Java.Interop.JavaCallableAttribute")) { + AddMethod (cwt, type, null, minfo, methodClassifier, resolver); + cwt.HasExport = true; + } else if (minfo.AnyCustomAttributes ("Java.Interop.JavaCallableConstructorAttribute")) { + AddMethod (cwt, type, null, minfo, methodClassifier, resolver); + cwt.HasExport = true; + } else if (minfo.AnyCustomAttributes (typeof (ExportFieldAttribute))) { + AddMethod (cwt, type, null, minfo, methodClassifier, resolver); + cwt.HasExport = true; + } else if (minfo.AnyCustomAttributes (typeof (ExportAttribute))) { + AddMethod (cwt, type, null, minfo, methodClassifier, resolver); + cwt.HasExport = true; + } + } + + // Methods from interfaces + foreach (InterfaceImplementation ifaceInfo in type.Interfaces) { + var typeReference = ifaceInfo.InterfaceType; + var typeDefinition = resolver.Resolve (typeReference); + + if (typeDefinition is null) { + Diagnostic.Error (4204, + CecilExtensions.LookupSource (type), + Localization.Resources.JavaCallableWrappers_XA4204, + typeReference.FullName); + continue; + } + + if (!CecilExtensions.GetTypeRegistrationAttributes (typeDefinition).Any ()) + continue; + + foreach (MethodDefinition imethod in typeDefinition.Methods) { + if (imethod.IsStatic) + continue; + + AddMethod (cwt, type, imethod, imethod, methodClassifier, resolver); + } + } + + // Constructors + var ctorTypes = new List () { + type, + }; + + foreach (var bt in type.GetBaseTypes (resolver)) { + ctorTypes.Add (bt); + var rattr = CecilExtensions.GetMethodRegistrationAttributes (bt).FirstOrDefault (); + + if (rattr != null && rattr.DoNotGenerateAcw) + break; + } + + ctorTypes.Reverse (); + + var curCtors = new List (); + + foreach (var minfo in type.Methods) { + if (minfo.IsConstructor && minfo.AnyCustomAttributes (typeof (ExportAttribute))) { + if (minfo.IsStatic) { + // Diagnostic.Warning (log, "ExportAttribute does not work on static constructor"); + } else { + if (CreateConstructor (cwt, minfo, ctorTypes [0], type, outerType, null, curCtors, false, true, resolver) is CallableWrapperConstructor c) + cwt.Constructors.Add (c); + + cwt.HasExport = true; + } + } + } + + AddConstructors (cwt, ctorTypes [0], type, outerType, null, curCtors, true, resolver); + + for (var i = 1; i < ctorTypes.Count; ++i) { + var baseCtors = curCtors; + curCtors = new List (); + AddConstructors (cwt, ctorTypes [i], type, outerType, baseCtors, curCtors, false, resolver); + } + + AddNestedTypes (cwt, type, resolver, methodClassifier); + + return cwt; + } + + static CallableWrapperField CreateField (MethodDefinition method, string fieldName, IMetadataResolver resolver) + { + var visibility = GetJavaAccess (method.Attributes & MethodAttributes.MemberAccessMask); var type_name = JavaNativeTypeManager.ReturnTypeFromSignature (JavaNativeTypeManager.GetJniSignature (method, resolver))?.Type ?? throw new ArgumentException ($"Could not get JNI signature for method `{method.Name}`", nameof (method)); - var annotations = JavaCallableWrapperGenerator.GetAnnotationsString ("\t", method.CustomAttributes, resolver); - return new CallableWrapperField (fieldName, type_name, visibility, method.Name) { + var field = new CallableWrapperField (fieldName, type_name, visibility, method.Name) { IsStatic = method.IsStatic, - Annotations = annotations }; + + field.Annotations.AddRange (CreateAnnotations (method, resolver)); + + return field; } - // Temporary conversion function - public static CallableWrapperField CreateField (JavaCallableWrapperGenerator.JavaFieldInfo field) + // Constructor with [Register] attribute + static CallableWrapperConstructor CreateConstructor (MethodDefinition methodDefinition, CallableWrapperType type, RegisterAttribute register, string? managedParameters, string? outerType, IMetadataResolver cache, bool shouldBeDynamicallyRegistered = true) { - return new CallableWrapperField (field.FieldName, field.TypeName, field.GetJavaAccess (), field.InitializerName) { - IsStatic = field.IsStatic, - Annotations = field.Annotations - }; + var method = CreateConstructor (methodDefinition.Name, type, register.Signature, register.Connector, managedParameters, outerType, null); + + method.Annotations.AddRange (CreateAnnotations (methodDefinition, cache)); + method.IsDynamicallyRegistered = shouldBeDynamicallyRegistered; + + return method; } - public static CallableWrapperMethod CreateMethod (MethodDefinition method, RegisterAttribute register, IMetadataResolver cache, bool shouldBeDynamicallyRegistered = true) - => CreateMethod (method, register, null, null, cache, shouldBeDynamicallyRegistered); + // Constructor with [Export] attribute + static CallableWrapperConstructor CreateConstructor (MethodDefinition methodDefinition, CallableWrapperType type, ExportAttribute export, string? managedParameters, IMetadataResolver resolver) + { + var method = CreateConstructor (methodDefinition.Name, type, JavaNativeTypeManager.GetJniSignature (methodDefinition, resolver), "__export__", null, null, export.SuperArgumentsString); + + method.IsExport = true; + method.IsStatic = methodDefinition.IsStatic; + method.JavaAccess = GetJavaAccess (methodDefinition.Attributes & MethodAttributes.MemberAccessMask); + method.ThrownTypeNames = export.ThrownNames; + method.JavaNameOverride = export.Name; + method.ManagedParameters = managedParameters; + method.Annotations.AddRange (CreateAnnotations (methodDefinition, resolver)); - public static CallableWrapperMethod CreateMethod (MethodDefinition methodDefinition, RegisterAttribute register, string? managedParameters, string? outerType, IMetadataResolver cache, bool shouldBeDynamicallyRegistered = true) + return method; + } + + // Common constructor creation code + static CallableWrapperConstructor CreateConstructor (string name, CallableWrapperType type, string? signature, string? connector, string? managedParameters, string? outerType, string? superCall) + { + signature = signature ?? throw new ArgumentNullException ("`connector` cannot be null.", nameof (connector)); + var method_name = "n_" + name + ":" + signature + ":" + connector; + + var method = new CallableWrapperConstructor (name, method_name, signature); + + PopulateMethod (method, signature, managedParameters, outerType, superCall); + + method.Name = type.Name; + method.CannotRegisterInStaticConstructor = type.CannotRegisterInStaticConstructor; + method.PartialAssemblyQualifiedName = type.PartialAssemblyQualifiedName; + + return method; + } + + // Method with a [Register] attribute + static CallableWrapperMethod CreateMethod (MethodDefinition methodDefinition, RegisterAttribute register, string? managedParameters, string? outerType, IMetadataResolver resolver, bool shouldBeDynamicallyRegistered = true) { var method = CreateMethod (register.Name, register.Signature, register.Connector, managedParameters, outerType, null); - method.Annotations = JavaCallableWrapperGenerator.GetAnnotationsString ("\t", methodDefinition.CustomAttributes, cache); + method.Annotations.AddRange (CreateAnnotations (methodDefinition, resolver)); method.IsDynamicallyRegistered = shouldBeDynamicallyRegistered; return method; } - public static CallableWrapperMethod CreateMethod (MethodDefinition methodDefinition, ExportAttribute export, string? managedParameters, IMetadataResolver resolver) + // Method with an [Export] attribute + static CallableWrapperMethod CreateMethod (MethodDefinition methodDefinition, ExportAttribute export, string? managedParameters, IMetadataResolver resolver) { var method = CreateMethod (methodDefinition.Name, JavaNativeTypeManager.GetJniSignature (methodDefinition, resolver), "__export__", null, null, export.SuperArgumentsString); method.IsExport = true; method.IsStatic = methodDefinition.IsStatic; - method.JavaAccess = JavaCallableWrapperGenerator.GetJavaAccess (methodDefinition.Attributes & MethodAttributes.MemberAccessMask); + method.JavaAccess = GetJavaAccess (methodDefinition.Attributes & MethodAttributes.MemberAccessMask); method.ThrownTypeNames = export.ThrownNames; method.JavaNameOverride = export.Name; method.ManagedParameters = managedParameters; - method.Annotations = JavaCallableWrapperGenerator.GetAnnotationsString ("\t", methodDefinition.CustomAttributes, resolver); + method.Annotations.AddRange (CreateAnnotations (methodDefinition, resolver)); return method; } - public static CallableWrapperMethod CreateMethod (MethodDefinition methodDefinition, IMetadataResolver resolver) + // Method with an [ExportField] attribute + static CallableWrapperMethod CreateMethod (MethodDefinition methodDefinition, IMetadataResolver resolver) { var method = CreateMethod (methodDefinition.Name, JavaNativeTypeManager.GetJniSignature (methodDefinition, resolver), "__export__", null, null, null); if (methodDefinition.HasParameters) - Diagnostic.Error (4205, JavaCallableWrapperGenerator.LookupSource (methodDefinition), Localization.Resources.JavaCallableWrappers_XA4205); + Diagnostic.Error (4205, CecilExtensions.LookupSource (methodDefinition), Localization.Resources.JavaCallableWrappers_XA4205); if (methodDefinition.ReturnType.MetadataType == MetadataType.Void) - Diagnostic.Error (4208, JavaCallableWrapperGenerator.LookupSource (methodDefinition), Localization.Resources.JavaCallableWrappers_XA4208); + Diagnostic.Error (4208, CecilExtensions.LookupSource (methodDefinition), Localization.Resources.JavaCallableWrappers_XA4208); method.IsExport = true; - method.IsStatic = method.IsStatic; - method.JavaAccess = JavaCallableWrapperGenerator.GetJavaAccess (methodDefinition.Attributes & MethodAttributes.MemberAccessMask); + method.IsStatic = methodDefinition.IsStatic; + method.JavaAccess = GetJavaAccess (methodDefinition.Attributes & MethodAttributes.MemberAccessMask); // Annotations are processed within CallableWrapperField, not the initializer method. So we don't generate them here. return method; } - public static CallableWrapperMethod CreateMethod (string name, string? signature, string? connector, string? managedParameters, string? outerType, string? superCall) + // Common method creation code + static CallableWrapperMethod CreateMethod (string name, string? signature, string? connector, string? managedParameters, string? outerType, string? superCall) { signature = signature ?? throw new ArgumentNullException ("`connector` cannot be null.", nameof (connector)); var method_name = "n_" + name + ":" + signature + ":" + connector; - var method = new CallableWrapperMethod (name, method_name, signature) { - ManagedParameters = managedParameters - }; + var method = new CallableWrapperMethod (name, method_name, signature); + + PopulateMethod (method, signature, managedParameters, outerType, superCall); + + return method; + } + + // This is done this way to allow sharing between CallableWrapperMethod and CallableWrapperConstructor + static void PopulateMethod (CallableWrapperMethod method, string signature, string? managedParameters, string? outerType, string? superCall) + { + method.ManagedParameters = managedParameters; var jnisig = signature; var closer = jnisig.IndexOf (')'); var ret = jnisig.Substring (closer + 1); + method.Retval = JavaNativeTypeManager.Parse (ret)?.Type; var jniparms = jnisig.Substring (1, closer - 1); if (string.IsNullOrEmpty (jniparms) && string.IsNullOrEmpty (superCall)) - return method; + return; var parms = new StringBuilder (); var scall = new StringBuilder (); @@ -131,51 +318,95 @@ public static CallableWrapperMethod CreateMethod (string name, string? signature method.Params = parms.ToString (); method.SuperCall = superCall ?? scall.ToString (); method.ActivateCall = acall.ToString (); + } - return method; + static void AddConstructors (CallableWrapperType cwt, TypeDefinition type, TypeDefinition rootType, string? outerType, List? baseCtors, List curCtors, bool onlyRegisteredOrExportedCtors, IMetadataResolver cache) + { + foreach (var ctor in type.Methods) + if (ctor.IsConstructor && !ctor.IsStatic && !ctor.AnyCustomAttributes (typeof (ExportAttribute))) + if (CreateConstructor (cwt, ctor, type, rootType, outerType, baseCtors, curCtors, onlyRegisteredOrExportedCtors, false, cache) is CallableWrapperConstructor c) + cwt.Constructors.Add (c); } - // Temporary conversion function - public static CallableWrapperMethod CreateMethod (JavaCallableWrapperGenerator.Signature signature) + static CallableWrapperConstructor? CreateConstructor (CallableWrapperType cwt, MethodDefinition ctor, TypeDefinition type, TypeDefinition rootType, string? outerType, List? baseCtors, List curCtors, bool onlyRegisteredOrExportedCtors, bool skipParameterCheck, IMetadataResolver cache) { - return new CallableWrapperMethod (signature.Name, signature.Method, signature.JniSignature) { - ManagedParameters = signature.ManagedParameters, - JavaNameOverride = signature.JavaNameOverride, - Params = signature.Params, - Retval = signature.Retval, - ThrowsDeclaration = signature.ThrowsDeclaration, - JavaAccess = signature.JavaAccess, - IsExport = signature.IsExport, - IsStatic = signature.IsStatic, - IsDynamicallyRegistered = signature.IsDynamicallyRegistered, - ThrownTypeNames = signature.ThrownTypeNames, - Annotations = signature.Annotations, - SuperCall = signature.SuperCall, - ActivateCall = signature.ActivateCall, - }; + // We create a parameter-less constructor for the application class, so don't use the imported one + if (!ctor.HasParameters && JavaNativeTypeManager.IsApplication (rootType, cache)) + return null; + + var managedParameters = GetManagedParameters (ctor, outerType, type, cache); + + if (!skipParameterCheck && (managedParameters == null || cwt.Constructors.Any (c => c.ManagedParameters == managedParameters))) + return null; + + // Constructor with [Export] attribute + var eattr = CecilExtensions.GetExportAttributes (ctor, cache).FirstOrDefault (); + + if (eattr != null) { + if (!string.IsNullOrEmpty (eattr.Name)) { + // Diagnostic.Warning (log, "Use of ExportAttribute.Name property is invalid on constructors"); + } + + curCtors.Add (ctor); + return CreateConstructor (ctor, cwt, eattr, managedParameters, cache); + } + + // Constructor with [Register] attribute + var rattr = CecilExtensions.GetMethodRegistrationAttributes (ctor).FirstOrDefault (); + + if (rattr != null) { + if (cwt.Constructors.Any (c => c.JniSignature == rattr.Signature)) + return null; + + curCtors.Add (ctor); + return CreateConstructor (ctor, cwt, rattr, managedParameters, outerType, cache); + } + + if (onlyRegisteredOrExportedCtors) + return null; + + // Constructors without [Export] or [Register] attributes + var jniSignature = JavaNativeTypeManager.GetJniSignature (ctor, cache); + + if (jniSignature is null) + return null; + + if (cwt.Constructors.Any (c => c.JniSignature == jniSignature)) + return null; + + if (baseCtors is null) + throw new InvalidOperationException ("`baseCtors` should not be null!"); + + if (baseCtors.Any (m => m.Parameters.AreParametersCompatibleWith (ctor.Parameters, cache))) { + curCtors.Add (ctor); + return CreateConstructor (".ctor", cwt, jniSignature, "", managedParameters, outerType, null); + } + + if (baseCtors.Any (m => !m.HasParameters)) { + curCtors.Add (ctor); + return CreateConstructor (".ctor", cwt, jniSignature, "", managedParameters, outerType, ""); + } + + return null; } - // Temporary conversion function - public static CallableWrapperConstructor CreateConstructor (JavaCallableWrapperGenerator.Signature signature) + static string GetManagedParameters (MethodDefinition ctor, string? outerType, TypeDefinition type, IMetadataResolver cache) { - return new CallableWrapperConstructor (signature.Name, signature.Method, signature.JniSignature) { - ManagedParameters = signature.ManagedParameters, - JavaNameOverride = signature.JavaNameOverride, - Params = signature.Params, - Retval = signature.Retval, - ThrowsDeclaration = signature.ThrowsDeclaration, - JavaAccess = signature.JavaAccess, - IsExport = signature.IsExport, - IsStatic = signature.IsStatic, - IsDynamicallyRegistered = signature.IsDynamicallyRegistered, - ThrownTypeNames = signature.ThrownTypeNames, - Annotations = signature.Annotations, - SuperCall = signature.SuperCall, - ActivateCall = signature.ActivateCall - }; + var sb = new StringBuilder (); + + foreach (var pdef in ctor.Parameters) { + if (sb.Length > 0) + sb.Append (':'); + if (outerType != null && sb.Length == 0) + sb.Append (type.DeclaringType.GetPartialAssemblyQualifiedName (cache)); + else + sb.Append (pdef.ParameterType.GetPartialAssemblyQualifiedName (cache)); + } + + return sb.ToString (); } - public static CallableWrapperApplicationConstructor? CreateApplicationConstructor (string name, TypeDefinition type, IMetadataResolver resolver) + static CallableWrapperApplicationConstructor? CreateApplicationConstructor (string name, TypeDefinition type, IMetadataResolver resolver) { if (!JavaNativeTypeManager.IsApplication (type, resolver)) return null; @@ -183,97 +414,101 @@ public static CallableWrapperConstructor CreateConstructor (JavaCallableWrapperG return new CallableWrapperApplicationConstructor (name); } - // Temporary conversion function - public static CallableWrapperType CreateType (JavaCallableWrapperGenerator generator) + static void AddNestedTypes (CallableWrapperType cwt, TypeDefinition type, IMetadataResolver cache,JavaCallableMethodClassifier? methodClassifier) { - var partial_assembly_qualified_name = generator.type.GetPartialAssemblyQualifiedName (generator.cache); - - var type = new CallableWrapperType (generator.name, generator.package, partial_assembly_qualified_name) { - IsAbstract = generator.type.IsAbstract, - ApplicationJavaClass = generator.ApplicationJavaClass, - HasDynamicallyRegisteredMethods = generator.HasDynamicallyRegisteredMethods, - GenerateOnCreateOverrides = generator.GenerateOnCreateOverrides, - MonoRuntimeInitialization = generator.MonoRuntimeInitialization, - IsApplication = JavaNativeTypeManager.IsApplication (generator.type, generator.cache), - IsInstrumentation = JavaNativeTypeManager.IsInstrumentation (generator.type, generator.cache), - }; + if (!type.HasNestedTypes) + return; - type.Annotations.AddRange (CreateTypeAnnotations (generator.type, generator.cache)); - - // Extends - var extendsType = GetJavaTypeName (generator.type.BaseType, generator.cache); - - if (extendsType == "android.app.Application" && generator.ApplicationJavaClass != null && !string.IsNullOrEmpty (generator.ApplicationJavaClass)) - extendsType = generator.ApplicationJavaClass; - - type.ExtendsType = extendsType; - - // Implemented interfaces - foreach (var ifaceInfo in generator.type.Interfaces) { - var iface = generator.cache.Resolve (ifaceInfo.InterfaceType); - - if (!JavaCallableWrapperGenerator.GetTypeRegistrationAttributes (iface).Any ()) + foreach (var nt in type.NestedTypes) { + if (!nt.HasJavaPeer (cache)) + continue; + if (!JavaNativeTypeManager.IsNonStaticInnerClass (nt, cache)) continue; - type.ImplementedInterfaces.Add (GetJavaTypeName (iface, generator.cache)); + cwt.NestedTypes.Add (CreateType (nt, cache, JavaNativeTypeManager.ToJniName (type, cache), methodClassifier)); + AddNestedTypes (cwt, nt, cache, methodClassifier); } - // Type constructors - foreach (var ctor in generator.ctors) { - if (string.IsNullOrEmpty (ctor.Params) && type.IsApplication) - continue; + cwt.HasExport |= cwt.NestedTypes.Any (t => t.HasExport); + } + + static void AddMethod (CallableWrapperType cwt, TypeDefinition type, MethodDefinition? registeredMethod, MethodDefinition implementedMethod, JavaCallableMethodClassifier? methodClassifier, IMetadataResolver cache) + { + if (registeredMethod != null) + foreach (RegisterAttribute attr in CecilExtensions.GetMethodRegistrationAttributes (registeredMethod)) { + // Check for Kotlin-mangled methods that cannot be overridden + if (attr.Name.Contains ("-impl") || (attr.Name.Length > 7 && attr.Name [attr.Name.Length - 8] == '-')) + Diagnostic.Error (4217, CecilExtensions.LookupSource (implementedMethod), Localization.Resources.JavaCallableWrappers_XA4217, attr.Name); - var ct = CreateConstructor (ctor); + var shouldBeDynamicallyRegistered = methodClassifier?.ShouldBeDynamicallyRegistered (type, registeredMethod, implementedMethod, attr.OriginAttribute) ?? true; + var method = CreateMethod (implementedMethod, attr, null, null, cache, shouldBeDynamicallyRegistered); - ct.Name = type.Name; - ct.CannotRegisterInStaticConstructor = type.CannotRegisterInStaticConstructor; - ct.PartialAssemblyQualifiedName = type.PartialAssemblyQualifiedName ; + if (!registeredMethod.IsConstructor && !cwt.Methods.Any (m => m.Name == method.Name && m.Params == method.Params)) + cwt.Methods.Add (method); + } + foreach (ExportAttribute attr in CecilExtensions.GetExportAttributes (implementedMethod, cache)) { + if (type.HasGenericParameters) + Diagnostic.Error (4206, CecilExtensions.LookupSource (implementedMethod), Localization.Resources.JavaCallableWrappers_XA4206); - type.Constructors.Add (ct); - } + var method = CreateMethod (implementedMethod, attr, null, cache); - // Application constructor - if (CreateApplicationConstructor (type.Name, generator.type, generator.cache) is CallableWrapperApplicationConstructor app_ctor) - type.ApplicationConstructor = app_ctor; + if (!string.IsNullOrEmpty (attr.SuperArgumentsString)) { + // Diagnostic.Warning (log, "Use of ExportAttribute.SuperArgumentsString property is invalid on methods"); + } - // Exported fields - foreach (var field in generator.exported_fields) - type.Fields.Add (CreateField (field)); + if (!implementedMethod.IsConstructor && !cwt.Methods.Any (m => m.Name == method.Name && m.Params == method.Params)) + cwt.Methods.Add (method); + } + foreach (ExportFieldAttribute attr in CecilExtensions.GetExportFieldAttributes (implementedMethod)) { + if (type.HasGenericParameters) + Diagnostic.Error (4207, CecilExtensions.LookupSource (implementedMethod), Localization.Resources.JavaCallableWrappers_XA4207); - // Methods - foreach (var method in generator.methods) - type.Methods.Add (CreateMethod (method)); + var method = CreateMethod (implementedMethod, cache); - // Nested types - if (generator.children is not null) - foreach (var nested in generator.children) - type.NestedTypes.Add (CreateType (nested)); + if (!implementedMethod.IsConstructor && !cwt.Methods.Any (m => m.Name == method.Name && m.Params == method.Params)) { + cwt.Methods.Add (method); + cwt.Fields.Add (CreateField (implementedMethod, attr.Name, cache)); + } + } + } - return type; + static void ExtractJavaNames (string jniName, out string package, out string type) + { + var i = jniName.LastIndexOf ('/'); + + if (i < 0) { + type = jniName; + package = string.Empty; + } else { + type = jniName.Substring (i + 1); + package = jniName.Substring (0, i).Replace ('/', '.'); + } } static string GetJavaTypeName (TypeReference r, IMetadataResolver cache) { - TypeDefinition d = cache.Resolve (r); - string? jniName = JavaNativeTypeManager.ToJniName (d, cache); - if (jniName == null) { + var d = cache.Resolve (r); + var jniName = JavaNativeTypeManager.ToJniName (d, cache); + + if (jniName is null) { Diagnostic.Error (4201, Localization.Resources.JavaCallableWrappers_XA4201, r.FullName); throw new InvalidOperationException ("--nrt:jniName-- Should not be reached"); } + return jniName.Replace ('/', '.').Replace ('$', '.'); } - public static IEnumerable CreateTypeAnnotations (TypeDefinition type, IMetadataResolver resolver) + static IEnumerable CreateAnnotations (ICustomAttributeProvider type, IMetadataResolver resolver) { foreach (var ca in type.CustomAttributes) { - var annotation = CreateTypeAnnotation (ca, resolver); + var annotation = CreateAnnotation (ca, resolver); if (annotation is not null) yield return annotation; } } - public static CallableWrapperTypeAnnotation? CreateTypeAnnotation (CustomAttribute ca, IMetadataResolver resolver) + static CallableWrapperTypeAnnotation? CreateAnnotation (CustomAttribute ca, IMetadataResolver resolver) { var catype = resolver.Resolve (ca.AttributeType); var tca = catype.CustomAttributes.FirstOrDefault (a => a.AttributeType.FullName == "Android.Runtime.AnnotationAttribute"); @@ -291,12 +526,11 @@ public static IEnumerable CreateTypeAnnotations ( foreach (var p in ca.Properties) { var pd = catype.Properties.FirstOrDefault (pp => pp.Name == p.Name); - var reg = pd != null ? pd.CustomAttributes.FirstOrDefault (pdca => pdca.AttributeType.FullName == "Android.Runtime.RegisterAttribute") : null; - + var reg = pd?.CustomAttributes.FirstOrDefault (pdca => pdca.AttributeType.FullName == "Android.Runtime.RegisterAttribute"); var key = reg != null ? (string) reg.ConstructorArguments [0].Value : p.Name; var value = ManagedValueToJavaSource (p.Argument.Value); - annotation.Properties.Add (new System.Collections.Generic.KeyValuePair (key, value)); + annotation.Properties.Add (new KeyValuePair (key, value)); } return annotation; @@ -309,9 +543,19 @@ static string ManagedValueToJavaSource (object value) return "\"" + value.ToString ().Replace ("\"", "\"\"") + '"'; else if (value.GetType ().FullName == "Java.Lang.Class") return value.ToString () + ".class"; - else if (value is bool) - return ((bool) value) ? "true" : "false"; + else if (value is bool v) + return v ? "true" : "false"; else return value.ToString (); } + + static string GetJavaAccess (MethodAttributes access) + { + return access switch { + MethodAttributes.Public => "public", + MethodAttributes.FamORAssem => "protected", + MethodAttributes.Family => "protected", + _ => "private", + }; + } } diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperApplicationConstructor.cs b/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperApplicationConstructor.cs index f0616a054..2d8c40938 100644 --- a/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperApplicationConstructor.cs +++ b/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperApplicationConstructor.cs @@ -2,7 +2,7 @@ namespace Java.Interop.Tools.JavaCallableWrappers.CallableWrapperMembers; -class CallableWrapperApplicationConstructor +public class CallableWrapperApplicationConstructor { public string Name { get; set; } diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperConstructor.cs b/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperConstructor.cs index b319d0bd5..d07c39eff 100644 --- a/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperConstructor.cs +++ b/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperConstructor.cs @@ -2,7 +2,7 @@ namespace Java.Interop.Tools.JavaCallableWrappers.CallableWrapperMembers; -class CallableWrapperConstructor : CallableWrapperMethod +public class CallableWrapperConstructor : CallableWrapperMethod { public bool CannotRegisterInStaticConstructor { get; set; } public string? PartialAssemblyQualifiedName { get; set; } @@ -19,8 +19,8 @@ public override void Generate (TextWriter sw, CallableWrapperWriterOptions optio // This does NOT currently allow creating managed types from Java. sw.WriteLine (); - if (Annotations is not null) - sw.WriteLine (Annotations); + foreach (var annotation in Annotations) + annotation.Generate (sw, "", options); sw.Write ("\tpublic "); sw.Write (Name); diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperField.cs b/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperField.cs index 27410946d..043dedc9e 100644 --- a/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperField.cs +++ b/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperField.cs @@ -1,15 +1,16 @@ +using System.Collections.Generic; using System.IO; namespace Java.Interop.Tools.JavaCallableWrappers.CallableWrapperMembers; -class CallableWrapperField +public class CallableWrapperField { public string FieldName { get; set; } public string TypeName { get; set; } public string Visibility { get; set; } public bool IsStatic { get; set; } public string InitializerName { get; set; } - public string? Annotations { get; set; } + public List Annotations { get; } = new List (); public CallableWrapperField (string fieldName, string typeName, string visibility, string initializerName) { @@ -23,8 +24,8 @@ public void Generate (TextWriter sw, CallableWrapperWriterOptions options) { sw.WriteLine (); - if (Annotations is not null) - sw.WriteLine (Annotations); + foreach (var annotation in Annotations) + annotation.Generate (sw, "", options); sw.Write ("\t"); sw.Write (Visibility); diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperMethod.cs b/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperMethod.cs index ea14bfb15..412bb42e3 100644 --- a/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperMethod.cs +++ b/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperMethod.cs @@ -1,9 +1,10 @@ using System; +using System.Collections.Generic; using System.IO; namespace Java.Interop.Tools.JavaCallableWrappers.CallableWrapperMembers; -class CallableWrapperMethod +public class CallableWrapperMethod { public string Name { get; set; } public string Method { get; set; } @@ -12,16 +13,17 @@ class CallableWrapperMethod public string? JavaNameOverride { get; set; } public string? Params { get; set; } public string? Retval { get; set; } - public string? ThrowsDeclaration { get; set; } public string? JavaAccess { get; set; } public bool IsExport { get; set; } public bool IsStatic { get; set; } - public bool IsDynamicallyRegistered { get; set; } + public bool IsDynamicallyRegistered { get; set; } = true; public string []? ThrownTypeNames { get; set; } - public string? Annotations { get; set; } public string? SuperCall { get; set; } public string? ActivateCall { get; set; } public string JavaName => JavaNameOverride ?? Name; + public List Annotations { get; } = new List (); + + public string? ThrowsDeclaration => ThrownTypeNames?.Length > 0 ? $" throws {string.Join (", ", ThrownTypeNames)}" : null; public CallableWrapperMethod (string name, string method, string jniSignature) { @@ -34,8 +36,8 @@ public virtual void Generate (TextWriter sw, CallableWrapperWriterOptions option { sw.WriteLine (); - if (Annotations is not null) - sw.WriteLine (Annotations); + foreach (var annotation in Annotations) + annotation.Generate (sw, "", options); sw.Write ("\t"); @@ -58,7 +60,7 @@ public virtual void Generate (TextWriter sw, CallableWrapperWriterOptions option sw.WriteLine ("\t{"); #if MONODROID_TIMING - sw.WriteLine ("\t\tandroid.util.Log.i(\"MonoDroid-Timing\", \"{0}.{1}: time: \"+java.lang.System.currentTimeMillis());", name, method.Name); + sw.WriteLine ("\t\tandroid.util.Log.i(\"MonoDroid-Timing\", \"{0}.{1}: time: \"+java.lang.System.currentTimeMillis());", Name, Method); #endif sw.Write ("\t\t"); diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperType.cs b/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperType.cs index 7092c85b1..2fd5d082e 100644 --- a/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperType.cs +++ b/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperType.cs @@ -4,20 +4,27 @@ namespace Java.Interop.Tools.JavaCallableWrappers.CallableWrapperMembers; -class CallableWrapperType +public class CallableWrapperType { public string Name { get; set; } public string Package { get; set; } public bool IsAbstract { get; set; } public string? ApplicationJavaClass { get; set; } - public bool HasDynamicallyRegisteredMethods { get; set; } public bool GenerateOnCreateOverrides { get; set; } + /// + /// The Java source code to be included in Instrumentation.onCreate + /// + /// Originally came from MonoRuntimeProvider.java delimited by: + /// // Mono Runtime Initialization {{{ + /// // }}} + /// public string? MonoRuntimeInitialization { get; set; } public string? ExtendsType { get; set; } public CallableWrapperApplicationConstructor? ApplicationConstructor { get; set; } public bool IsApplication { get; set; } public bool IsInstrumentation { get; set; } public string PartialAssemblyQualifiedName { get; set; } + public bool HasExport { get; set; } public List Annotations { get; } = new List (); public List ImplementedInterfaces { get; } = new List (); @@ -59,8 +66,7 @@ public CallableWrapperType (string name, string package, string partialAssemblyQ // // private native void n_onCreate (android.os.Bundle bundle); // } - - public void Generate (TextWriter writer, CallableWrapperWriterOptions options, bool isNested) + public void Generate (TextWriter writer, CallableWrapperWriterOptions options, bool isNested = false) { if (!isNested && !string.IsNullOrEmpty (Package)) { writer.WriteLine ("package " + Package + ";"); @@ -80,7 +86,7 @@ public void Generate (TextWriter writer, CallableWrapperWriterOptions options, b GenerateFooter (writer, options); } - public void GenerateHeader (TextWriter sw, CallableWrapperWriterOptions options) + void GenerateHeader (TextWriter sw, CallableWrapperWriterOptions options) { sw.WriteLine (); @@ -90,7 +96,13 @@ public void GenerateHeader (TextWriter sw, CallableWrapperWriterOptions options) sw.WriteLine ("public " + (IsAbstract ? "abstract " : "") + "class " + Name); - sw.WriteLine ("\textends " + ExtendsType); + var extends = ExtendsType; + + // Do this check here rather than the constructor because it can be set after the constructor is called + if (extends == "android.app.Application" && ApplicationJavaClass != null && !string.IsNullOrEmpty (ApplicationJavaClass)) + extends = ApplicationJavaClass; + + sw.WriteLine ("\textends " + extends); sw.WriteLine ("\timplements"); sw.Write ("\t\t"); @@ -114,7 +126,7 @@ public void GenerateHeader (TextWriter sw, CallableWrapperWriterOptions options) sw.WriteLine ("{"); } - public void GenerateInfrastructure (TextWriter writer, CallableWrapperWriterOptions options) + void GenerateInfrastructure (TextWriter writer, CallableWrapperWriterOptions options) { var needCtor = false; @@ -147,7 +159,7 @@ public void GenerateInfrastructure (TextWriter writer, CallableWrapperWriterOpti } } - public void GenerateBody (TextWriter sw, CallableWrapperWriterOptions options) + void GenerateBody (TextWriter sw, CallableWrapperWriterOptions options) { foreach (var ctor in Constructors) ctor.Generate (sw, options); @@ -187,7 +199,7 @@ public void GenerateBody (TextWriter sw, CallableWrapperWriterOptions options) sw.WriteLine ("\t}"); } - public void GenerateFooter (TextWriter sw, CallableWrapperWriterOptions options) + void GenerateFooter (TextWriter sw, CallableWrapperWriterOptions options) { sw.WriteLine ("}"); } @@ -216,7 +228,7 @@ void WriteInstrumentationOnCreate (TextWriter sw, CallableWrapperWriterOptions o sw.WriteLine ("\t{"); #if MONODROID_TIMING - sw.WriteLine ("\t\tandroid.util.Log.i(\"MonoDroid-Timing\", \"{0}.onCreate(Bundle): time: \"+java.lang.System.currentTimeMillis());", name); + sw.WriteLine ("\t\tandroid.util.Log.i(\"MonoDroid-Timing\", \"{0}.onCreate(Bundle): time: \"+java.lang.System.currentTimeMillis());", Name); sw.WriteLine (); #endif @@ -282,6 +294,9 @@ void GenerateRegisterType (TextWriter sw, CallableWrapperType self, string field } } + // If there are no methods, we need to generate "empty" registration because of backward compatibility + public bool HasDynamicallyRegisteredMethods => Methods.Count == 0 || Methods.Any (sig => sig.IsDynamicallyRegistered); + /// /// Returns a destination file path based on the package name of this Java type /// diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperTypeAnnotation.cs b/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperTypeAnnotation.cs index d30c6a8d0..e605eee62 100644 --- a/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperTypeAnnotation.cs +++ b/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperTypeAnnotation.cs @@ -4,7 +4,7 @@ namespace Java.Interop.Tools.JavaCallableWrappers.CallableWrapperMembers; -class CallableWrapperTypeAnnotation +public class CallableWrapperTypeAnnotation { public string Name { get; set; } public List> Properties { get; } = new (); diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperWriterOptions.cs b/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperWriterOptions.cs index fdb09dd08..e0bd76c08 100644 --- a/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperWriterOptions.cs +++ b/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperWriterOptions.cs @@ -4,7 +4,7 @@ namespace Java.Interop.Tools.JavaCallableWrappers; -class CallableWrapperWriterOptions +public class CallableWrapperWriterOptions { public JavaPeerStyle CodeGenerationTarget { get; set; } } diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGenerator.JavaFieldInfo.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGenerator.JavaFieldInfo.cs deleted file mode 100644 index 9902148e3..000000000 --- a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGenerator.JavaFieldInfo.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; - -using Mono.Cecil; -using Java.Interop.Tools.TypeNameMappings; - -using MethodAttributes = Mono.Cecil.MethodAttributes; -using static Java.Interop.Tools.TypeNameMappings.JavaNativeTypeManager; - -namespace Java.Interop.Tools.JavaCallableWrappers -{ - public partial class JavaCallableWrapperGenerator { - internal class JavaFieldInfo { - public JavaFieldInfo (MethodDefinition method, string fieldName, IMetadataResolver resolver) - { - this.FieldName = fieldName; - InitializerName = method.Name; - TypeName = JavaNativeTypeManager.ReturnTypeFromSignature (GetJniSignature (method, resolver))?.Type - ?? throw new ArgumentException ($"Could not get JNI signature for method `{method.Name}`", nameof (method)); - IsStatic = method.IsStatic; - Access = method.Attributes & MethodAttributes.MemberAccessMask; - Annotations = GetAnnotationsString ("\t", method.CustomAttributes, resolver); - } - - public MethodAttributes Access { get; private set; } - public bool IsStatic { get; private set; } - public string TypeName { get; private set; } - public string FieldName { get; private set; } - public string InitializerName { get; private set; } - public string Annotations { get; private set; } - - public string GetJavaAccess () - { - return JavaCallableWrapperGenerator.GetJavaAccess (Access); - } - } - } -} diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGenerator.Signature.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGenerator.Signature.cs deleted file mode 100644 index 7e259f2eb..000000000 --- a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGenerator.Signature.cs +++ /dev/null @@ -1,134 +0,0 @@ -using System; -using System.Text; - -using Mono.Cecil; - -using Android.Runtime; -using Java.Interop.Tools.Diagnostics; -using Java.Interop.Tools.TypeNameMappings; - -using MethodAttributes = Mono.Cecil.MethodAttributes; -using static Java.Interop.Tools.TypeNameMappings.JavaNativeTypeManager; - -namespace Java.Interop.Tools.JavaCallableWrappers -{ - - public partial class JavaCallableWrapperGenerator { - internal class Signature { - - public Signature (MethodDefinition method, RegisterAttribute register, IMetadataResolver cache, bool shouldBeDynamicallyRegistered = true) : this (method, register, null, null, cache, shouldBeDynamicallyRegistered) {} - - public Signature (MethodDefinition method, RegisterAttribute register, string? managedParameters, string? outerType, IMetadataResolver cache, bool shouldBeDynamicallyRegistered = true) - : this (register.Name, register.Signature, register.Connector, managedParameters, outerType, null) - { - Annotations = JavaCallableWrapperGenerator.GetAnnotationsString ("\t", method.CustomAttributes, cache); - IsDynamicallyRegistered = shouldBeDynamicallyRegistered; - } - - public Signature (MethodDefinition method, ExportAttribute export, string? managedParameters, IMetadataResolver cache) - : this (method.Name, GetJniSignature (method, cache), "__export__", null, null, export.SuperArgumentsString) - { - IsExport = true; - IsStatic = method.IsStatic; - JavaAccess = JavaCallableWrapperGenerator.GetJavaAccess (method.Attributes & MethodAttributes.MemberAccessMask); - ThrownTypeNames = export.ThrownNames; - JavaNameOverride = export.Name; - ManagedParameters = managedParameters; - Annotations = JavaCallableWrapperGenerator.GetAnnotationsString ("\t", method.CustomAttributes, cache); - } - - public Signature (MethodDefinition method, ExportFieldAttribute exportField, IMetadataResolver cache) - : this (method.Name, GetJniSignature (method, cache), "__export__", null, null, null) - { - if (method.HasParameters) - Diagnostic.Error (4205, JavaCallableWrapperGenerator.LookupSource (method), Localization.Resources.JavaCallableWrappers_XA4205); - if (method.ReturnType.MetadataType == MetadataType.Void) - Diagnostic.Error (4208, JavaCallableWrapperGenerator.LookupSource (method), Localization.Resources.JavaCallableWrappers_XA4208); - IsExport = true; - IsStatic = method.IsStatic; - JavaAccess = JavaCallableWrapperGenerator.GetJavaAccess (method.Attributes & MethodAttributes.MemberAccessMask); - - // annotations are processed within JavaFieldInfo, not the initializer method. So we don't generate them here. - } - - public Signature (string name, string? signature, string? connector, string? managedParameters, string? outerType, string? superCall) - { - ManagedParameters = managedParameters; - JniSignature = signature ?? throw new ArgumentNullException ("`connector` cannot be null.", nameof (connector)); - Method = "n_" + name + ":" + JniSignature + ":" + connector; - Name = name; - - var jnisig = JniSignature; - int closer = jnisig.IndexOf (')'); - string ret = jnisig.Substring (closer + 1); - retval = JavaNativeTypeManager.Parse (ret)?.Type; - string jniparms = jnisig.Substring (1, closer - 1); - if (string.IsNullOrEmpty (jniparms) && string.IsNullOrEmpty (superCall)) - return; - var parms = new StringBuilder (); - var scall = new StringBuilder (); - var acall = new StringBuilder (); - bool first = true; - int i = 0; - foreach (JniTypeName jti in JavaNativeTypeManager.FromSignature (jniparms)) { - if (outerType != null) { - acall.Append (outerType).Append (".this"); - outerType = null; - continue; - } - string? parmType = jti.Type; - if (!first) { - parms.Append (", "); - scall.Append (", "); - acall.Append (", "); - } - first = false; - parms.Append (parmType).Append (" p").Append (i); - scall.Append ("p").Append (i); - acall.Append ("p").Append (i); - ++i; - } - this.parms = parms.ToString (); - this.call = superCall != null ? superCall : scall.ToString (); - this.ActivateCall = acall.ToString (); - } - - string? call; - public string? SuperCall { - get { return call; } - } - - public string? ActivateCall {get; private set;} - - public readonly string Name; - public readonly string? JavaNameOverride; - public string JavaName { - get { return JavaNameOverride ?? Name; } - } - - string? parms; - public string? Params { - get { return parms; } - } - - string? retval; - public string? Retval { - get { return retval; } - } - - public string? ThrowsDeclaration { - get { return ThrownTypeNames?.Length > 0 ? " throws " + String.Join (", ", ThrownTypeNames) : null; } - } - - public readonly string? JavaAccess; - public readonly string? ManagedParameters; - public readonly string JniSignature; - public readonly string Method; - public readonly bool IsExport; - public readonly bool IsStatic; - public readonly bool IsDynamicallyRegistered = true; - public readonly string []? ThrownTypeNames; - public readonly string? Annotations; - } - } -} diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGenerator.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGenerator.cs deleted file mode 100644 index 7a01ed05a..000000000 --- a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGenerator.cs +++ /dev/null @@ -1,627 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Text.RegularExpressions; - -using Mono.Cecil; -using Mono.Cecil.Cil; - -using Android.Runtime; - -using Java.Interop.Tools.Cecil; -using Java.Interop.Tools.Diagnostics; -using Java.Interop.Tools.TypeNameMappings; - -using MethodAttributes = Mono.Cecil.MethodAttributes; -using static Java.Interop.Tools.TypeNameMappings.JavaNativeTypeManager; -using Java.Interop.Tools.JavaCallableWrappers.CallableWrapperMembers; -using Java.Interop.Tools.JavaCallableWrappers.Adapters; - -namespace Java.Interop.Tools.JavaCallableWrappers { - - public partial class JavaCallableWrapperGenerator { - - Action log; - internal string name; - internal string package; - internal TypeDefinition type; - internal List exported_fields = new List (); - internal List methods = new List (); - internal List ctors = new List (); - internal List? children; - - internal readonly IMetadataResolver cache; - readonly JavaCallableMethodClassifier? methodClassifier; - - [Obsolete ("Use the TypeDefinitionCache overload for better performance.", error: true)] - public JavaCallableWrapperGenerator (TypeDefinition type, Action log) => throw new NotSupportedException (); - - public JavaCallableWrapperGenerator (TypeDefinition type, Action log, TypeDefinitionCache cache) - : this (type, log, (IMetadataResolver) cache, methodClassifier: null) - { } - - public JavaCallableWrapperGenerator (TypeDefinition type, Action log, TypeDefinitionCache cache, JavaCallableMethodClassifier? methodClassifier) - : this (type, log, (IMetadataResolver) cache, methodClassifier) - { - } - - public JavaCallableWrapperGenerator (TypeDefinition type, Action log, IMetadataResolver resolver) - : this (type, log, resolver, methodClassifier: null) - { } - - public JavaCallableWrapperGenerator (TypeDefinition type, Action log, IMetadataResolver resolver, JavaCallableMethodClassifier? methodClassifier) - : this (type, null, log, resolver, methodClassifier) - { - AddNestedTypes (type); - } - - public string? ApplicationJavaClass { get; set; } - public JavaPeerStyle CodeGenerationTarget { get; set; } - - public bool GenerateOnCreateOverrides { get; set; } - - public bool HasExport { get; private set; } - - // If there are no methods, we need to generate "empty" registration because of backward compatibility - public bool HasDynamicallyRegisteredMethods => methods.Count == 0 || methods.Any ((Signature sig) => sig.IsDynamicallyRegistered); - - /// - /// The Java source code to be included in Instrumentation.onCreate - /// - /// Originally came from MonoRuntimeProvider.java delimited by: - /// // Mono Runtime Initialization {{{ - /// // }}} - /// - public string? MonoRuntimeInitialization { get; set; } - - public string Name { - get { return name; } - } - - void AddNestedTypes (TypeDefinition type) - { - if (!type.HasNestedTypes) { - return; - } - children = children ?? new List (); - foreach (TypeDefinition nt in type.NestedTypes) { - if (!nt.HasJavaPeer (cache)) - continue; - if (!JavaNativeTypeManager.IsNonStaticInnerClass (nt, cache)) - continue; - children.Add (new JavaCallableWrapperGenerator (nt, JavaNativeTypeManager.ToJniName (type, cache), log, cache)); - AddNestedTypes (nt); - } - HasExport |= children.Any (t => t.HasExport); - } - - JavaCallableWrapperGenerator (TypeDefinition type, string? outerType, Action log, IMetadataResolver resolver, JavaCallableMethodClassifier? methodClassifier = null) - { - this.methodClassifier = methodClassifier; - this.type = type; - this.log = log; - this.cache = resolver ?? new TypeDefinitionCache (); - - if (type.IsEnum || type.IsInterface || type.IsValueType) - Diagnostic.Error (4200, LookupSource (type), Localization.Resources.JavaCallableWrappers_XA4200, type.FullName); - - string jniName = JavaNativeTypeManager.ToJniName (type, this.cache); - if (jniName == null) { - Diagnostic.Error (4201, LookupSource (type), Localization.Resources.JavaCallableWrappers_XA4201, type.FullName); - throw new InvalidOperationException ("--nrt:jniName-- Should not be reached"); - } - if (outerType != null && !string.IsNullOrEmpty (outerType)) { - string p; - jniName = jniName.Substring (outerType.Length + 1); - ExtractJavaNames (outerType, out p, out outerType); - } - ExtractJavaNames (jniName, out package, out name); - if (string.IsNullOrEmpty (package) && - (type.IsSubclassOf ("Android.App.Activity", cache) || - type.IsSubclassOf ("Android.App.Application", cache) || - type.IsSubclassOf ("Android.App.Service", cache) || - type.IsSubclassOf ("Android.Content.BroadcastReceiver", cache) || - type.IsSubclassOf ("Android.Content.ContentProvider", cache))) - Diagnostic.Error (4203, LookupSource (type), Localization.Resources.JavaCallableWrappers_XA4203, jniName); - - foreach (MethodDefinition minfo in type.Methods.Where (m => !m.IsConstructor)) { - var baseRegisteredMethod = GetBaseRegisteredMethod (minfo); - if (baseRegisteredMethod != null) - AddMethod (baseRegisteredMethod, minfo); - else if (minfo.AnyCustomAttributes ("Java.Interop.JavaCallableAttribute")) { - AddMethod (null, minfo); - HasExport = true; - } else if (minfo.AnyCustomAttributes ("Java.Interop.JavaCallableConstructorAttribute")) { - AddMethod (null, minfo); - HasExport = true; - } else if (minfo.AnyCustomAttributes (typeof(ExportFieldAttribute))) { - AddMethod (null, minfo); - HasExport = true; - } else if (minfo.AnyCustomAttributes (typeof (ExportAttribute))) { - AddMethod (null, minfo); - HasExport = true; - } - } - - foreach (InterfaceImplementation ifaceInfo in type.Interfaces) { - var typeReference = ifaceInfo.InterfaceType; - var typeDefinition = cache.Resolve (typeReference); - if (typeDefinition == null) { - Diagnostic.Error (4204, - LookupSource (type), - Localization.Resources.JavaCallableWrappers_XA4204, - typeReference.FullName); - continue; - } - if (!GetTypeRegistrationAttributes (typeDefinition).Any ()) - continue; - foreach (MethodDefinition imethod in typeDefinition.Methods) { - if (imethod.IsStatic) - continue; - AddMethod (imethod, imethod); - } - } - - var ctorTypes = new List () { - type, - }; - foreach (var bt in type.GetBaseTypes (cache)) { - ctorTypes.Add (bt); - RegisterAttribute rattr = GetMethodRegistrationAttributes (bt).FirstOrDefault (); - if (rattr != null && rattr.DoNotGenerateAcw) - break; - } - ctorTypes.Reverse (); - - var curCtors = new List (); - - foreach (MethodDefinition minfo in type.Methods) { - if (minfo.IsConstructor && minfo.AnyCustomAttributes (typeof (ExportAttribute))) { - if (minfo.IsStatic) { - // Diagnostic.Warning (log, "ExportAttribute does not work on static constructor"); - } - else { - AddConstructor (minfo, ctorTypes [0], outerType, null, curCtors, false, true); - HasExport = true; - } - } - } - - AddConstructors (ctorTypes [0], outerType, null, curCtors, true); - - for (int i = 1; i < ctorTypes.Count; ++i) { - var baseCtors = curCtors; - curCtors = new List (); - AddConstructors (ctorTypes [i], outerType, baseCtors, curCtors, false); - } - } - - static void ExtractJavaNames (string jniName, out string package, out string type) - { - int i = jniName.LastIndexOf ('/'); - if (i < 0) { - type = jniName; - package = string.Empty; - } - else { - type = jniName.Substring (i+1); - package = jniName.Substring (0, i).Replace ('/', '.'); - } - } - - internal static SequencePoint? LookupSource (MethodDefinition method) - { - if (!method.HasBody) - return null; - - foreach (var ins in method.Body.Instructions) { - var seqPoint = method.DebugInformation.GetSequencePoint (ins); - if (seqPoint != null) - return seqPoint; - } - - return null; - } - - static SequencePoint? LookupSource (TypeDefinition type) - { - SequencePoint? candidate = null; - foreach (var method in type.Methods) { - if (!method.HasBody) - continue; - - foreach (var ins in method.Body.Instructions) { - var seq = method.DebugInformation.GetSequencePoint (ins); - if (seq == null) - continue; - - if (Regex.IsMatch (seq.Document.Url, ".+\\.(g|designer)\\..+")) - break; - if (candidate == null || seq.StartLine < candidate.StartLine) - candidate = seq; - break; - } - } - - return candidate; - } - - void AddConstructors (TypeDefinition type, string? outerType, List? baseCtors, List curCtors, bool onlyRegisteredOrExportedCtors) - { - foreach (MethodDefinition ctor in type.Methods) - if (ctor.IsConstructor && !ctor.IsStatic && !ctor.AnyCustomAttributes (typeof (ExportAttribute))) - AddConstructor (ctor, type, outerType, baseCtors, curCtors, onlyRegisteredOrExportedCtors, false); - } - - void AddConstructor (MethodDefinition ctor, TypeDefinition type, string? outerType, List? baseCtors, List curCtors, bool onlyRegisteredOrExportedCtors, bool skipParameterCheck) - { - string managedParameters = GetManagedParameters (ctor, outerType); - if (!skipParameterCheck && (managedParameters == null || ctors.Any (c => c.ManagedParameters == managedParameters))) { - return; - } - - ExportAttribute eattr = GetExportAttributes (ctor).FirstOrDefault (); - if (eattr != null) { - if (!string.IsNullOrEmpty (eattr.Name)) { - // Diagnostic.Warning (log, "Use of ExportAttribute.Name property is invalid on constructors"); - } - ctors.Add (new Signature (ctor, eattr, managedParameters, cache)); - curCtors.Add (ctor); - return; - } - - RegisterAttribute rattr = GetMethodRegistrationAttributes (ctor).FirstOrDefault (); - if (rattr != null) { - if (ctors.Any (c => c.JniSignature == rattr.Signature)) - return; - ctors.Add (new Signature (ctor, rattr, managedParameters, outerType, cache)); - curCtors.Add (ctor); - return; - } - - if (onlyRegisteredOrExportedCtors) - return; - - string? jniSignature = GetJniSignature (ctor, cache); - - if (jniSignature == null) - return; - - if (ctors.Any (c => c.JniSignature == jniSignature)) - return; - - if (baseCtors == null) { - throw new InvalidOperationException ("`baseCtors` should not be null!"); - } - - if (baseCtors.Any (m => m.Parameters.AreParametersCompatibleWith (ctor.Parameters, cache))) { - ctors.Add (new Signature (".ctor", jniSignature, "", managedParameters, outerType, null)); - curCtors.Add (ctor); - return; - } - if (baseCtors.Any (m => !m.HasParameters)) { - ctors.Add (new Signature (".ctor", jniSignature, "", managedParameters, outerType, "")); - curCtors.Add (ctor); - return; - } - } - - MethodDefinition? GetBaseRegisteredMethod (MethodDefinition method) - { - MethodDefinition bmethod; - while ((bmethod = method.GetBaseDefinition (cache)) != method) { - method = bmethod; - - if (method.AnyCustomAttributes (typeof (RegisterAttribute))) { - return method; - } - } - return null; - } - - internal static RegisterAttribute? ToRegisterAttribute (CustomAttribute attr) - { - // attr.Resolve (); - RegisterAttribute? r = null; - if (attr.ConstructorArguments.Count == 1) - r = new RegisterAttribute ((string) attr.ConstructorArguments [0].Value, attr); - else if (attr.ConstructorArguments.Count == 3) - r = new RegisterAttribute ( - (string) attr.ConstructorArguments [0].Value, - (string) attr.ConstructorArguments [1].Value, - (string) attr.ConstructorArguments [2].Value, - attr); - if (r != null) { - var v = attr.Properties.FirstOrDefault (p => p.Name == "DoNotGenerateAcw"); - r.DoNotGenerateAcw = v.Name == null ? false : (bool) v.Argument.Value; - } - return r; - } - - internal static RegisterAttribute? RegisterFromJniTypeSignatureAttribute (CustomAttribute attr) - { - // attr.Resolve (); - RegisterAttribute? r = null; - if (attr.ConstructorArguments.Count == 1) - r = new RegisterAttribute ((string) attr.ConstructorArguments [0].Value, attr); - if (r != null) { - var v = attr.Properties.FirstOrDefault (p => p.Name == "GenerateJavaPeer"); - if (v.Name == null) { - r.DoNotGenerateAcw = false; - } else if (v.Name == "GenerateJavaPeer") { - r.DoNotGenerateAcw = ! (bool) v.Argument.Value; - } - var isKeyProp = attr.Properties.FirstOrDefault (p => p.Name == "IsKeyword"); - var isKeyword = isKeyProp.Name != null && ((bool) isKeyProp.Argument.Value) == true; - var arrRankProp = attr.Properties.FirstOrDefault (p => p.Name == "ArrayRank"); - if (arrRankProp.Name != null && arrRankProp.Argument.Value is int rank) { - r.Name = new string ('[', rank) + (isKeyword ? r.Name : "L" + r.Name + ";"); - } - } - return r; - } - - internal static RegisterAttribute? RegisterFromJniConstructorSignatureAttribute (CustomAttribute attr) - { - // attr.Resolve (); - RegisterAttribute? r = null; - if (attr.ConstructorArguments.Count == 1) - r = new RegisterAttribute ( - name: ".ctor", - signature: (string) attr.ConstructorArguments [0].Value, - connector: "", - originAttribute: attr); - return r; - } - - internal static RegisterAttribute? RegisterFromJniMethodSignatureAttribute (CustomAttribute attr) - { - // attr.Resolve (); - RegisterAttribute? r = null; - if (attr.ConstructorArguments.Count == 2) - r = new RegisterAttribute ((string) attr.ConstructorArguments [0].Value, - (string) attr.ConstructorArguments [1].Value, - "", - attr); - return r; - } - - ExportAttribute ToExportAttribute (CustomAttribute attr, IMemberDefinition declaringMember) - { - var name = attr.ConstructorArguments.Count > 0 ? (string) attr.ConstructorArguments [0].Value : declaringMember.Name; - if (attr.Properties.Count == 0) - return new ExportAttribute (name); - var typeArgs = (CustomAttributeArgument []) attr.Properties.FirstOrDefault (p => p.Name == "Throws").Argument.Value; - var thrown = typeArgs != null && typeArgs.Any () - ? (from caa in typeArgs select JavaNativeTypeManager.Parse (GetJniTypeName ((TypeReference)caa.Value, cache))?.Type) - .Where (v => v != null) - .ToArray () - : null; - var superArgs = (string) attr.Properties.FirstOrDefault (p => p.Name == "SuperArgumentsString").Argument.Value; - return new ExportAttribute (name) {ThrownNames = thrown, SuperArgumentsString = superArgs}; - } - - ExportAttribute ToExportAttributeFromJavaCallableAttribute (CustomAttribute attr, IMemberDefinition declaringMember) - { - var name = attr.ConstructorArguments.Count > 0 - ? (string) attr.ConstructorArguments [0].Value - : declaringMember.Name; - return new ExportAttribute (name); - } - - ExportAttribute ToExportAttributeFromJavaCallableConstructorAttribute (CustomAttribute attr, IMemberDefinition declaringMember) - { - var superArgs = (string) attr.Properties - .FirstOrDefault (p => p.Name == "SuperConstructorExpression") - .Argument - .Value; - return new ExportAttribute (".ctor") { - SuperArgumentsString = superArgs, - }; - } - - internal static ExportFieldAttribute ToExportFieldAttribute (CustomAttribute attr) - { - return new ExportFieldAttribute ((string) attr.ConstructorArguments [0].Value); - } - - internal static IEnumerable GetTypeRegistrationAttributes (Mono.Cecil.ICustomAttributeProvider p) - { - foreach (var a in GetAttributes (p, a => ToRegisterAttribute (a))) { - yield return a; - } - foreach (var c in p.GetCustomAttributes ("Java.Interop.JniTypeSignatureAttribute")) { - var r = RegisterFromJniTypeSignatureAttribute (c); - if (r == null) { - continue; - } - yield return r; - } - } - - static IEnumerable GetMethodRegistrationAttributes (Mono.Cecil.ICustomAttributeProvider p) - { - foreach (var a in GetAttributes (p, a => ToRegisterAttribute (a))) { - yield return a; - } - foreach (var c in p.GetCustomAttributes ("Java.Interop.JniConstructorSignatureAttribute")) { - var r = RegisterFromJniConstructorSignatureAttribute (c); - if (r == null) { - continue; - } - yield return r; - } - foreach (var c in p.GetCustomAttributes ("Java.Interop.JniMethodSignatureAttribute")) { - var r = RegisterFromJniMethodSignatureAttribute (c); - if (r == null) { - continue; - } - yield return r; - } - } - - IEnumerable GetExportAttributes (IMemberDefinition p) - { - return GetAttributes (p, a => ToExportAttribute (a, p)) - .Concat (GetAttributes (p, "Java.Interop.JavaCallableAttribute", - a => ToExportAttributeFromJavaCallableAttribute (a, p))) - .Concat (GetAttributes (p, "Java.Interop.JavaCallableConstructorAttribute", - a => ToExportAttributeFromJavaCallableConstructorAttribute (a, p))); - } - - static IEnumerable GetExportFieldAttributes (Mono.Cecil.ICustomAttributeProvider p) - { - return GetAttributes (p, a => ToExportFieldAttribute (a)); - } - - static IEnumerable GetAttributes (Mono.Cecil.ICustomAttributeProvider p, Func selector) - where TAttribute : class - { - return GetAttributes (p, typeof (TAttribute).FullName, selector); - } - - static IEnumerable GetAttributes (Mono.Cecil.ICustomAttributeProvider p, string attributeName, Func selector) - where TAttribute : class - { - return p.GetCustomAttributes (attributeName) - .Select (selector) - .Where (v => v != null) - .Select (v => v!); - } - - void AddMethod (MethodDefinition? registeredMethod, MethodDefinition implementedMethod) - { - if (registeredMethod != null) - foreach (RegisterAttribute attr in GetMethodRegistrationAttributes (registeredMethod)) { - // Check for Kotlin-mangled methods that cannot be overridden - if (attr.Name.Contains ("-impl") || (attr.Name.Length > 7 && attr.Name[attr.Name.Length - 8] == '-')) - Diagnostic.Error (4217, LookupSource (implementedMethod), Localization.Resources.JavaCallableWrappers_XA4217, attr.Name); - - bool shouldBeDynamicallyRegistered = methodClassifier?.ShouldBeDynamicallyRegistered (type, registeredMethod, implementedMethod, attr.OriginAttribute) ?? true; - var msig = new Signature (implementedMethod, attr, cache, shouldBeDynamicallyRegistered); - if (!registeredMethod.IsConstructor && !methods.Any (m => m.Name == msig.Name && m.Params == msig.Params)) - methods.Add (msig); - } - foreach (ExportAttribute attr in GetExportAttributes (implementedMethod)) { - if (type.HasGenericParameters) - Diagnostic.Error (4206, LookupSource (implementedMethod), Localization.Resources.JavaCallableWrappers_XA4206); - - var msig = new Signature (implementedMethod, attr, managedParameters: null, cache: cache); - if (!string.IsNullOrEmpty (attr.SuperArgumentsString)) { - // Diagnostic.Warning (log, "Use of ExportAttribute.SuperArgumentsString property is invalid on methods"); - } - if (!implementedMethod.IsConstructor && !methods.Any (m => m.Name == msig.Name && m.Params == msig.Params)) - methods.Add (msig); - } - foreach (ExportFieldAttribute attr in GetExportFieldAttributes (implementedMethod)) { - if (type.HasGenericParameters) - Diagnostic.Error (4207, LookupSource (implementedMethod), Localization.Resources.JavaCallableWrappers_XA4207); - - var msig = new Signature (implementedMethod, attr, cache); - if (!implementedMethod.IsConstructor && !methods.Any (m => m.Name == msig.Name && m.Params == msig.Params)) { - methods.Add (msig); - exported_fields.Add (new JavaFieldInfo (implementedMethod, attr.Name, cache)); - } - } - } - - string GetManagedParameters (MethodDefinition ctor, string? outerType) - { - StringBuilder sb = new StringBuilder (); - foreach (ParameterDefinition pdef in ctor.Parameters) { - if (sb.Length > 0) - sb.Append (':'); - if (outerType != null && sb.Length == 0) - sb.Append (type.DeclaringType.GetPartialAssemblyQualifiedName (cache)); - else - sb.Append (pdef.ParameterType.GetPartialAssemblyQualifiedName (cache)); - } - return sb.ToString (); - } - - public void Generate (string outputPath) - { - var options = new CallableWrapperWriterOptions { CodeGenerationTarget = CodeGenerationTarget }; - var jcw_writer = CreateWriter (); - jcw_writer.Generate (outputPath, options); - } - - public void Generate (TextWriter writer) - { - var options = new CallableWrapperWriterOptions { CodeGenerationTarget = CodeGenerationTarget }; - var jcw_writer = CreateWriter (); - jcw_writer.Generate (writer, options, false); - } - - internal CallableWrapperType CreateWriter () - { - return CecilImporter.CreateType (this); - } - - internal static string GetAnnotationsString (string indent, IEnumerable atts, IMetadataResolver resolver) - { - var sw = new StringWriter (); - WriteAnnotations (indent, sw, atts, resolver); - return sw.ToString (); - } - - internal static void WriteAnnotations (string indent, TextWriter sw, IEnumerable atts, IMetadataResolver resolver) - { - foreach (var ca in atts) { - var catype = resolver.Resolve (ca.AttributeType); - var tca = catype.CustomAttributes.FirstOrDefault (a => a.AttributeType.FullName == "Android.Runtime.AnnotationAttribute"); - if (tca != null) { - sw.Write (indent); - sw.Write ('@'); - sw.Write (tca.ConstructorArguments [0].Value); - if (ca.Properties.Count > 0) { - sw.WriteLine ("("); - bool wrote = false; - foreach (var p in ca.Properties) { - if (wrote) - sw.WriteLine (','); - var pd = catype.Properties.FirstOrDefault (pp => pp.Name == p.Name); - var reg = pd != null ? pd.CustomAttributes.FirstOrDefault (pdca => pdca.AttributeType.FullName == "Android.Runtime.RegisterAttribute") : null; - sw.Write (reg != null ? reg.ConstructorArguments [0].Value : p.Name); - sw.Write (" = "); - sw.Write (ManagedValueToJavaSource (p.Argument.Value)); - wrote = true; - } - sw.Write (")"); - } - sw.WriteLine (); - } - } - } - - internal static string GetJavaAccess (MethodAttributes access) - { - switch (access) { - case MethodAttributes.Public: - return "public"; - case MethodAttributes.FamORAssem: - return "protected"; - case MethodAttributes.Family: - return "protected"; - default: - return "private"; - } - } - - // FIXME: this is hacky. Is there any existing code for value to source conversion? - static string ManagedValueToJavaSource (object value) - { - if (value is string) - return "\"" + value.ToString ().Replace ("\"", "\"\"") + '"'; - else if (value.GetType ().FullName == "Java.Lang.Class") - return value.ToString () + ".class"; - else if (value is bool) - return ((bool) value) ? "true" : "false"; - else - return value.ToString (); - } - } -} diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Utilities/CecilExtensions.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Utilities/CecilExtensions.cs new file mode 100644 index 000000000..c73b829b2 --- /dev/null +++ b/src/Java.Interop.Tools.JavaCallableWrappers/Utilities/CecilExtensions.cs @@ -0,0 +1,237 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using Android.Runtime; +using Java.Interop.Tools.Cecil; +using Java.Interop.Tools.TypeNameMappings; +using Mono.Cecil; +using Mono.Cecil.Cil; +using static Java.Interop.Tools.TypeNameMappings.JavaNativeTypeManager; + +namespace Java.Interop.Tools.JavaCallableWrappers.Utilities; + +static class CecilExtensions +{ + public static IEnumerable GetExportFieldAttributes (Mono.Cecil.ICustomAttributeProvider p) + { + return GetAttributes (p, a => ToExportFieldAttribute (a)); + } + + public static IEnumerable GetAttributes (Mono.Cecil.ICustomAttributeProvider p, Func selector) + where TAttribute : class + { + return GetAttributes (p, typeof (TAttribute).FullName, selector); + } + + public static IEnumerable GetAttributes (Mono.Cecil.ICustomAttributeProvider p, string attributeName, Func selector) + where TAttribute : class + { + return p.GetCustomAttributes (attributeName) + .Select (selector) + .Where (v => v != null) + .Select (v => v!); + } + + internal static ExportFieldAttribute ToExportFieldAttribute (CustomAttribute attr) + { + return new ExportFieldAttribute ((string) attr.ConstructorArguments [0].Value); + } + + public static MethodDefinition? GetBaseRegisteredMethod (MethodDefinition method, IMetadataResolver cache) + { + MethodDefinition bmethod; + while ((bmethod = method.GetBaseDefinition (cache)) != method) { + method = bmethod; + + if (method.AnyCustomAttributes (typeof (RegisterAttribute))) { + return method; + } + } + return null; + } + + internal static RegisterAttribute? ToRegisterAttribute (CustomAttribute attr) + { + // attr.Resolve (); + RegisterAttribute? r = null; + if (attr.ConstructorArguments.Count == 1) + r = new RegisterAttribute ((string) attr.ConstructorArguments [0].Value, attr); + else if (attr.ConstructorArguments.Count == 3) + r = new RegisterAttribute ( + (string) attr.ConstructorArguments [0].Value, + (string) attr.ConstructorArguments [1].Value, + (string) attr.ConstructorArguments [2].Value, + attr); + if (r != null) { + var v = attr.Properties.FirstOrDefault (p => p.Name == "DoNotGenerateAcw"); + r.DoNotGenerateAcw = v.Name == null ? false : (bool) v.Argument.Value; + } + return r; + } + + + internal static RegisterAttribute? RegisterFromJniTypeSignatureAttribute (CustomAttribute attr) + { + // attr.Resolve (); + RegisterAttribute? r = null; + if (attr.ConstructorArguments.Count == 1) + r = new RegisterAttribute ((string) attr.ConstructorArguments [0].Value, attr); + if (r != null) { + var v = attr.Properties.FirstOrDefault (p => p.Name == "GenerateJavaPeer"); + if (v.Name == null) { + r.DoNotGenerateAcw = false; + } else if (v.Name == "GenerateJavaPeer") { + r.DoNotGenerateAcw = !(bool) v.Argument.Value; + } + var isKeyProp = attr.Properties.FirstOrDefault (p => p.Name == "IsKeyword"); + var isKeyword = isKeyProp.Name != null && ((bool) isKeyProp.Argument.Value) == true; + var arrRankProp = attr.Properties.FirstOrDefault (p => p.Name == "ArrayRank"); + if (arrRankProp.Name != null && arrRankProp.Argument.Value is int rank) { + r.Name = new string ('[', rank) + (isKeyword ? r.Name : "L" + r.Name + ";"); + } + } + return r; + } + + internal static RegisterAttribute? RegisterFromJniConstructorSignatureAttribute (CustomAttribute attr) + { + // attr.Resolve (); + RegisterAttribute? r = null; + if (attr.ConstructorArguments.Count == 1) + r = new RegisterAttribute ( + name: ".ctor", + signature: (string) attr.ConstructorArguments [0].Value, + connector: "", + originAttribute: attr); + return r; + } + + internal static RegisterAttribute? RegisterFromJniMethodSignatureAttribute (CustomAttribute attr) + { + // attr.Resolve (); + RegisterAttribute? r = null; + if (attr.ConstructorArguments.Count == 2) + r = new RegisterAttribute ((string) attr.ConstructorArguments [0].Value, + (string) attr.ConstructorArguments [1].Value, + "", + attr); + return r; + } + + static ExportAttribute ToExportAttribute (CustomAttribute attr, IMemberDefinition declaringMember, IMetadataResolver cache) + { + var name = attr.ConstructorArguments.Count > 0 ? (string) attr.ConstructorArguments [0].Value : declaringMember.Name; + if (attr.Properties.Count == 0) + return new ExportAttribute (name); + var typeArgs = (CustomAttributeArgument []) attr.Properties.FirstOrDefault (p => p.Name == "Throws").Argument.Value; + var thrown = typeArgs != null && typeArgs.Any () + ? (from caa in typeArgs select JavaNativeTypeManager.Parse (GetJniTypeName ((TypeReference) caa.Value, cache))?.Type) + .Where (v => v != null) + .ToArray () + : null; + var superArgs = (string) attr.Properties.FirstOrDefault (p => p.Name == "SuperArgumentsString").Argument.Value; + return new ExportAttribute (name) { ThrownNames = thrown, SuperArgumentsString = superArgs }; + } + + static ExportAttribute ToExportAttributeFromJavaCallableAttribute (CustomAttribute attr, IMemberDefinition declaringMember) + { + var name = attr.ConstructorArguments.Count > 0 + ? (string) attr.ConstructorArguments [0].Value + : declaringMember.Name; + return new ExportAttribute (name); + } + + static ExportAttribute ToExportAttributeFromJavaCallableConstructorAttribute (CustomAttribute attr, IMemberDefinition declaringMember) + { + var superArgs = (string) attr.Properties + .FirstOrDefault (p => p.Name == "SuperConstructorExpression") + .Argument + .Value; + return new ExportAttribute (".ctor") { + SuperArgumentsString = superArgs, + }; + } + + internal static IEnumerable GetTypeRegistrationAttributes (Mono.Cecil.ICustomAttributeProvider p) + { + foreach (var a in CecilExtensions.GetAttributes (p, a => CecilExtensions.ToRegisterAttribute (a))) { + yield return a; + } + foreach (var c in p.GetCustomAttributes ("Java.Interop.JniTypeSignatureAttribute")) { + var r = RegisterFromJniTypeSignatureAttribute (c); + if (r == null) { + continue; + } + yield return r; + } + } + + public static IEnumerable GetMethodRegistrationAttributes (Mono.Cecil.ICustomAttributeProvider p) + { + foreach (var a in CecilExtensions.GetAttributes (p, a => CecilExtensions.ToRegisterAttribute (a))) { + yield return a; + } + foreach (var c in p.GetCustomAttributes ("Java.Interop.JniConstructorSignatureAttribute")) { + var r = RegisterFromJniConstructorSignatureAttribute (c); + if (r == null) { + continue; + } + yield return r; + } + foreach (var c in p.GetCustomAttributes ("Java.Interop.JniMethodSignatureAttribute")) { + var r = RegisterFromJniMethodSignatureAttribute (c); + if (r == null) { + continue; + } + yield return r; + } + } + + public static IEnumerable GetExportAttributes (IMemberDefinition p, IMetadataResolver cache) + { + return CecilExtensions.GetAttributes (p, a => CecilExtensions.ToExportAttribute (a, p, cache)) + .Concat (CecilExtensions.GetAttributes (p, "Java.Interop.JavaCallableAttribute", + a => CecilExtensions.ToExportAttributeFromJavaCallableAttribute (a, p))) + .Concat (CecilExtensions.GetAttributes (p, "Java.Interop.JavaCallableConstructorAttribute", + a => CecilExtensions.ToExportAttributeFromJavaCallableConstructorAttribute (a, p))); + } + + public static SequencePoint? LookupSource (MethodDefinition method) + { + if (!method.HasBody) + return null; + + foreach (var ins in method.Body.Instructions) { + var seqPoint = method.DebugInformation.GetSequencePoint (ins); + if (seqPoint != null) + return seqPoint; + } + + return null; + } + + public static SequencePoint? LookupSource (TypeDefinition type) + { + SequencePoint? candidate = null; + foreach (var method in type.Methods) { + if (!method.HasBody) + continue; + + foreach (var ins in method.Body.Instructions) { + var seq = method.DebugInformation.GetSequencePoint (ins); + if (seq == null) + continue; + + if (Regex.IsMatch (seq.Document.Url, ".+\\.(g|designer)\\..+")) + break; + if (candidate == null || seq.StartLine < candidate.StartLine) + candidate = seq; + break; + } + } + + return candidate; + } +} diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/Crc64.Table.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Utilities/Crc64.Table.cs similarity index 100% rename from src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/Crc64.Table.cs rename to src/Java.Interop.Tools.JavaCallableWrappers/Utilities/Crc64.Table.cs diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/Crc64.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Utilities/Crc64.cs similarity index 100% rename from src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/Crc64.cs rename to src/Java.Interop.Tools.JavaCallableWrappers/Utilities/Crc64.cs diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/Crc64Helper.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Utilities/Crc64Helper.cs similarity index 100% rename from src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/Crc64Helper.cs rename to src/Java.Interop.Tools.JavaCallableWrappers/Utilities/Crc64Helper.cs diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/IdentifierValidator.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Utilities/IdentifierValidator.cs similarity index 100% rename from src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/IdentifierValidator.cs rename to src/Java.Interop.Tools.JavaCallableWrappers/Utilities/IdentifierValidator.cs diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableMethodClassifier.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Utilities/JavaCallableMethodClassifier.cs similarity index 100% rename from src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableMethodClassifier.cs rename to src/Java.Interop.Tools.JavaCallableWrappers/Utilities/JavaCallableMethodClassifier.cs diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaPeerStyle.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Utilities/JavaPeerStyle.cs similarity index 100% rename from src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaPeerStyle.cs rename to src/Java.Interop.Tools.JavaCallableWrappers/Utilities/JavaPeerStyle.cs diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaTypeScanner.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Utilities/JavaTypeScanner.cs similarity index 96% rename from src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaTypeScanner.cs rename to src/Java.Interop.Tools.JavaCallableWrappers/Utilities/JavaTypeScanner.cs index 9d74487a9..638464335 100644 --- a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaTypeScanner.cs +++ b/src/Java.Interop.Tools.JavaCallableWrappers/Utilities/JavaTypeScanner.cs @@ -8,6 +8,7 @@ using Java.Interop.Tools.Cecil; using Java.Interop.Tools.Diagnostics; using Java.Interop.Tools.TypeNameMappings; +using Java.Interop.Tools.JavaCallableWrappers.Utilities; namespace Java.Interop.Tools.JavaCallableWrappers { @@ -95,7 +96,7 @@ public static bool ShouldSkipJavaCallableWrapperGeneration (TypeDefinition type, if (JavaNativeTypeManager.IsNonStaticInnerClass (type, resolver)) return true; - foreach (var c in JavaCallableWrapperGenerator.GetTypeRegistrationAttributes (type)) { + foreach (var c in CecilExtensions.GetTypeRegistrationAttributes (type)) { if (c.DoNotGenerateAcw) { return true; } diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/TypeNameMapGenerator.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Utilities/TypeNameMapGenerator.cs similarity index 100% rename from src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/TypeNameMapGenerator.cs rename to src/Java.Interop.Tools.JavaCallableWrappers/Utilities/TypeNameMapGenerator.cs diff --git a/tests/Java.Interop.Tools.JavaCallableWrappers-Tests/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGeneratorTests.cs b/tests/Java.Interop.Tools.JavaCallableWrappers-Tests/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGeneratorTests.cs index e64033543..16b5dac02 100644 --- a/tests/Java.Interop.Tools.JavaCallableWrappers-Tests/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGeneratorTests.cs +++ b/tests/Java.Interop.Tools.JavaCallableWrappers-Tests/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGeneratorTests.cs @@ -13,6 +13,8 @@ using Java.Interop.Tools.TypeNameMappings; using Xamarin.Android.ToolsTests; +using Java.Interop.Tools.JavaCallableWrappers.Adapters; +using Java.Interop.Tools.Cecil; namespace Java.Interop.Tools.JavaCallableWrappersTests { @@ -26,7 +28,7 @@ public void ConstructorExceptions () // structs aren't supported var td = SupportDeclarations.GetTypeDefinition (typeof (int)); - var e = Assert.Throws (() => new JavaCallableWrapperGenerator (td, logger, cache: null)); + var e = Assert.Throws (() => CecilImporter.CreateType (td, new TypeDefinitionCache ())); Assert.AreEqual (4200, e.Code); } @@ -37,7 +39,7 @@ public void KotlinInvalidImplRegisterName () // Contains invalid [Register] name of "foo-impl" var td = SupportDeclarations.GetTypeDefinition (typeof (KotlinInvalidImplRegisterName)); - var e = Assert.Throws (() => new JavaCallableWrapperGenerator (td, logger, cache: null)); + var e = Assert.Throws (() => CecilImporter.CreateType (td, new TypeDefinitionCache ())); Assert.AreEqual (4217, e.Code); } @@ -48,7 +50,7 @@ public void KotlinInvalidHashRegisterName () // Contains invalid [Register] name of "foo-f8k2a13" var td = SupportDeclarations.GetTypeDefinition (typeof (KotlinInvalidHashRegisterName)); - var e = Assert.Throws (() => new JavaCallableWrapperGenerator (td, logger, cache: null)); + var e = Assert.Throws (() => CecilImporter.CreateType (td, new TypeDefinitionCache ())); Assert.AreEqual (4217, e.Code); } @@ -105,16 +107,19 @@ public void monodroidClearReferences () static string Generate (Type type, string applicationJavaClass = null, string monoRuntimeInit = null, JavaPeerStyle style = JavaPeerStyle.XAJavaInterop1) { var td = SupportDeclarations.GetTypeDefinition (type); - var g = new JavaCallableWrapperGenerator (td, log: null, cache: null) { - ApplicationJavaClass = applicationJavaClass, - GenerateOnCreateOverrides = true, - MonoRuntimeInitialization = monoRuntimeInit, - CodeGenerationTarget = style, - }; + var g = CecilImporter.CreateType (td, new TypeDefinitionCache (), null); + + g.ApplicationJavaClass = applicationJavaClass; + g.GenerateOnCreateOverrides = true; + g.MonoRuntimeInitialization = monoRuntimeInit; + var o = new StringWriter (); var dir = Path.GetDirectoryName (typeof (JavaCallableWrapperGeneratorTests).Assembly.Location); - g.Generate (Path.Combine (dir, "__o")); - g.Generate (o); + var options = new CallableWrapperWriterOptions { CodeGenerationTarget = style }; + + g.Generate (Path.Combine (dir, "__o"), options); + g.Generate (o, options); + return o.ToString (); } @@ -146,7 +151,6 @@ public IndirectApplication () mono.MonoPackageManager.setContext (this); } - public void onCreate () { n_onCreate (); @@ -199,10 +203,8 @@ extends java.lang.Object mono.android.Runtime.register (""Xamarin.Android.ToolsTests.ExportsMembers, Java.Interop.Tools.JavaCallableWrappers-Tests"", ExportsMembers.class, __md_methods); } - public static crc64197ae30a36756915.ExportsMembers STATIC_INSTANCE = GetInstance (); - public java.lang.String VALUE = GetValue (); public static crc64197ae30a36756915.ExportsMembers GetInstance () @@ -219,7 +221,6 @@ public java.lang.String GetValue () private native java.lang.String n_GetValue (); - public static void staticMethodNotMangled () { n_staticMethodNotMangled (); @@ -227,7 +228,6 @@ public static void staticMethodNotMangled () private static native void n_staticMethodNotMangled (); - public void methodNamesNotMangled () { n_methodNamesNotMangled (); @@ -235,7 +235,6 @@ public void methodNamesNotMangled () private native void n_methodNamesNotMangled (); - public java.lang.String attributeOverridesNames (java.lang.String p0, int p1) { return n_CompletelyDifferentName (p0, p1); @@ -243,7 +242,6 @@ public java.lang.String attributeOverridesNames (java.lang.String p0, int p1) private native java.lang.String n_CompletelyDifferentName (java.lang.String p0, int p1); - public void methodThatThrows () throws java.lang.Throwable { n_methodThatThrows (); @@ -251,7 +249,6 @@ public void methodThatThrows () throws java.lang.Throwable private native void n_methodThatThrows (); - public void methodThatThrowsEmptyArray () { n_methodThatThrowsEmptyArray (); @@ -436,7 +433,6 @@ extends java.lang.Object mono.android.Runtime.register (""Xamarin.Android.ToolsTests.ExportsConstructors, Java.Interop.Tools.JavaCallableWrappers-Tests"", ExportsConstructors.class, __md_methods); } - public ExportsConstructors () { super (); @@ -445,7 +441,6 @@ public ExportsConstructors () } } - public ExportsConstructors (int p0) { super (p0); @@ -492,7 +487,6 @@ extends java.lang.Object mono.android.Runtime.register (""Xamarin.Android.ToolsTests.ExportsThrowsConstructors, Java.Interop.Tools.JavaCallableWrappers-Tests"", ExportsThrowsConstructors.class, __md_methods); } - public ExportsThrowsConstructors () throws java.lang.Throwable { super (); @@ -501,7 +495,6 @@ public ExportsThrowsConstructors () throws java.lang.Throwable } } - public ExportsThrowsConstructors (int p0) throws java.lang.Throwable { super (p0); @@ -510,7 +503,6 @@ public ExportsThrowsConstructors (int p0) throws java.lang.Throwable } } - public ExportsThrowsConstructors (java.lang.String p0) { super (p0); @@ -644,7 +636,6 @@ extends java.lang.Object net.dot.jni.ManagedPeer.registerNativeMembers (JavaInteropExample.class, __md_methods); } - public JavaInteropExample (int p0, int p1) { super (); @@ -653,7 +644,6 @@ public JavaInteropExample (int p0, int p1) } } - public void example () { n_Example (); diff --git a/tools/jcw-gen/App.cs b/tools/jcw-gen/App.cs index 02788d55d..0eb606902 100644 --- a/tools/jcw-gen/App.cs +++ b/tools/jcw-gen/App.cs @@ -9,6 +9,7 @@ using Java.Interop.Tools.JavaCallableWrappers; using Mono.Cecil; using Mono.Options; +using Java.Interop.Tools.JavaCallableWrappers.Adapters; namespace Java.Interop.Tools { @@ -91,10 +92,13 @@ static void GenerateJavaCallableWrapper (TypeDefinition type, string outputPath, return; } - var generator = new JavaCallableWrapperGenerator (type, log: Console.WriteLine, cache) { - CodeGenerationTarget = style, + var t = CecilImporter.CreateType (type, cache); + + var options = new CallableWrapperWriterOptions { + CodeGenerationTarget = style, }; - generator.Generate (outputPath); + + t.Generate (outputPath, options); } } } From a30efadd451c0781c72c1d8c1a28b32e90215f74 Mon Sep 17 00:00:00 2001 From: Jonathan Pryor Date: Tue, 13 Feb 2024 14:56:20 -0500 Subject: [PATCH 5/9] Move files into "conventional" locations. --- .../CecilImporter.cs | 0 .../CallableWrapperApplicationConstructor.cs | 0 .../CallableWrapperConstructor.cs | 0 .../CallableWrapperField.cs | 0 .../CallableWrapperMethod.cs | 0 .../CallableWrapperType.cs | 0 .../CallableWrapperTypeAnnotation.cs | 0 .../CecilExtensions.cs | 0 .../Crc64.Table.cs | 0 .../Crc64.cs | 0 .../Crc64Helper.cs | 0 .../IdentifierValidator.cs | 0 .../JavaCallableMethodClassifier.cs | 0 .../JavaPeerStyle.cs | 0 .../JavaTypeScanner.cs | 0 .../TypeNameMapGenerator.cs | 0 .../CallableWrapperWriterOptions.cs | 0 17 files changed, 0 insertions(+), 0 deletions(-) rename src/Java.Interop.Tools.JavaCallableWrappers/{Adapters => Java.Interop.Tools.JavaCallableWrappers.Adapters}/CecilImporter.cs (100%) rename src/Java.Interop.Tools.JavaCallableWrappers/{CallableWrapperMembers => Java.Interop.Tools.JavaCallableWrappers.CallableWrapperMembers}/CallableWrapperApplicationConstructor.cs (100%) rename src/Java.Interop.Tools.JavaCallableWrappers/{CallableWrapperMembers => Java.Interop.Tools.JavaCallableWrappers.CallableWrapperMembers}/CallableWrapperConstructor.cs (100%) rename src/Java.Interop.Tools.JavaCallableWrappers/{CallableWrapperMembers => Java.Interop.Tools.JavaCallableWrappers.CallableWrapperMembers}/CallableWrapperField.cs (100%) rename src/Java.Interop.Tools.JavaCallableWrappers/{CallableWrapperMembers => Java.Interop.Tools.JavaCallableWrappers.CallableWrapperMembers}/CallableWrapperMethod.cs (100%) rename src/Java.Interop.Tools.JavaCallableWrappers/{CallableWrapperMembers => Java.Interop.Tools.JavaCallableWrappers.CallableWrapperMembers}/CallableWrapperType.cs (100%) rename src/Java.Interop.Tools.JavaCallableWrappers/{CallableWrapperMembers => Java.Interop.Tools.JavaCallableWrappers.CallableWrapperMembers}/CallableWrapperTypeAnnotation.cs (100%) rename src/Java.Interop.Tools.JavaCallableWrappers/{Utilities => Java.Interop.Tools.JavaCallableWrappers.Utilities}/CecilExtensions.cs (100%) rename src/Java.Interop.Tools.JavaCallableWrappers/{Utilities => Java.Interop.Tools.JavaCallableWrappers.Utilities}/Crc64.Table.cs (100%) rename src/Java.Interop.Tools.JavaCallableWrappers/{Utilities => Java.Interop.Tools.JavaCallableWrappers.Utilities}/Crc64.cs (100%) rename src/Java.Interop.Tools.JavaCallableWrappers/{Utilities => Java.Interop.Tools.JavaCallableWrappers.Utilities}/Crc64Helper.cs (100%) rename src/Java.Interop.Tools.JavaCallableWrappers/{Utilities => Java.Interop.Tools.JavaCallableWrappers.Utilities}/IdentifierValidator.cs (100%) rename src/Java.Interop.Tools.JavaCallableWrappers/{Utilities => Java.Interop.Tools.JavaCallableWrappers.Utilities}/JavaCallableMethodClassifier.cs (100%) rename src/Java.Interop.Tools.JavaCallableWrappers/{Utilities => Java.Interop.Tools.JavaCallableWrappers.Utilities}/JavaPeerStyle.cs (100%) rename src/Java.Interop.Tools.JavaCallableWrappers/{Utilities => Java.Interop.Tools.JavaCallableWrappers.Utilities}/JavaTypeScanner.cs (100%) rename src/Java.Interop.Tools.JavaCallableWrappers/{Utilities => Java.Interop.Tools.JavaCallableWrappers.Utilities}/TypeNameMapGenerator.cs (100%) rename src/Java.Interop.Tools.JavaCallableWrappers/{ => Java.Interop.Tools.JavaCallableWrappers}/CallableWrapperWriterOptions.cs (100%) diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Adapters/CecilImporter.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.Adapters/CecilImporter.cs similarity index 100% rename from src/Java.Interop.Tools.JavaCallableWrappers/Adapters/CecilImporter.cs rename to src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.Adapters/CecilImporter.cs diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperApplicationConstructor.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.CallableWrapperMembers/CallableWrapperApplicationConstructor.cs similarity index 100% rename from src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperApplicationConstructor.cs rename to src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.CallableWrapperMembers/CallableWrapperApplicationConstructor.cs diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperConstructor.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.CallableWrapperMembers/CallableWrapperConstructor.cs similarity index 100% rename from src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperConstructor.cs rename to src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.CallableWrapperMembers/CallableWrapperConstructor.cs diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperField.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.CallableWrapperMembers/CallableWrapperField.cs similarity index 100% rename from src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperField.cs rename to src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.CallableWrapperMembers/CallableWrapperField.cs diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperMethod.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.CallableWrapperMembers/CallableWrapperMethod.cs similarity index 100% rename from src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperMethod.cs rename to src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.CallableWrapperMembers/CallableWrapperMethod.cs diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperType.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.CallableWrapperMembers/CallableWrapperType.cs similarity index 100% rename from src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperType.cs rename to src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.CallableWrapperMembers/CallableWrapperType.cs diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperTypeAnnotation.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.CallableWrapperMembers/CallableWrapperTypeAnnotation.cs similarity index 100% rename from src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperMembers/CallableWrapperTypeAnnotation.cs rename to src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.CallableWrapperMembers/CallableWrapperTypeAnnotation.cs diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Utilities/CecilExtensions.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.Utilities/CecilExtensions.cs similarity index 100% rename from src/Java.Interop.Tools.JavaCallableWrappers/Utilities/CecilExtensions.cs rename to src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.Utilities/CecilExtensions.cs diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Utilities/Crc64.Table.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.Utilities/Crc64.Table.cs similarity index 100% rename from src/Java.Interop.Tools.JavaCallableWrappers/Utilities/Crc64.Table.cs rename to src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.Utilities/Crc64.Table.cs diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Utilities/Crc64.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.Utilities/Crc64.cs similarity index 100% rename from src/Java.Interop.Tools.JavaCallableWrappers/Utilities/Crc64.cs rename to src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.Utilities/Crc64.cs diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Utilities/Crc64Helper.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.Utilities/Crc64Helper.cs similarity index 100% rename from src/Java.Interop.Tools.JavaCallableWrappers/Utilities/Crc64Helper.cs rename to src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.Utilities/Crc64Helper.cs diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Utilities/IdentifierValidator.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.Utilities/IdentifierValidator.cs similarity index 100% rename from src/Java.Interop.Tools.JavaCallableWrappers/Utilities/IdentifierValidator.cs rename to src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.Utilities/IdentifierValidator.cs diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Utilities/JavaCallableMethodClassifier.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.Utilities/JavaCallableMethodClassifier.cs similarity index 100% rename from src/Java.Interop.Tools.JavaCallableWrappers/Utilities/JavaCallableMethodClassifier.cs rename to src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.Utilities/JavaCallableMethodClassifier.cs diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Utilities/JavaPeerStyle.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.Utilities/JavaPeerStyle.cs similarity index 100% rename from src/Java.Interop.Tools.JavaCallableWrappers/Utilities/JavaPeerStyle.cs rename to src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.Utilities/JavaPeerStyle.cs diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Utilities/JavaTypeScanner.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.Utilities/JavaTypeScanner.cs similarity index 100% rename from src/Java.Interop.Tools.JavaCallableWrappers/Utilities/JavaTypeScanner.cs rename to src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.Utilities/JavaTypeScanner.cs diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Utilities/TypeNameMapGenerator.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.Utilities/TypeNameMapGenerator.cs similarity index 100% rename from src/Java.Interop.Tools.JavaCallableWrappers/Utilities/TypeNameMapGenerator.cs rename to src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.Utilities/TypeNameMapGenerator.cs diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperWriterOptions.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperWriterOptions.cs similarity index 100% rename from src/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperWriterOptions.cs rename to src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperWriterOptions.cs From f6ce322e7abf5d27776fb732eb2fc0d47b2837be Mon Sep 17 00:00:00 2001 From: Jonathan Pryor Date: Thu, 15 Feb 2024 17:32:06 -0500 Subject: [PATCH 6/9] Remove unnecessarily provided default parameter. --- .../JavaCallableWrapperGeneratorTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Java.Interop.Tools.JavaCallableWrappers-Tests/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGeneratorTests.cs b/tests/Java.Interop.Tools.JavaCallableWrappers-Tests/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGeneratorTests.cs index 16b5dac02..d67f25024 100644 --- a/tests/Java.Interop.Tools.JavaCallableWrappers-Tests/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGeneratorTests.cs +++ b/tests/Java.Interop.Tools.JavaCallableWrappers-Tests/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGeneratorTests.cs @@ -107,7 +107,7 @@ public void monodroidClearReferences () static string Generate (Type type, string applicationJavaClass = null, string monoRuntimeInit = null, JavaPeerStyle style = JavaPeerStyle.XAJavaInterop1) { var td = SupportDeclarations.GetTypeDefinition (type); - var g = CecilImporter.CreateType (td, new TypeDefinitionCache (), null); + var g = CecilImporter.CreateType (td, new TypeDefinitionCache ()); g.ApplicationJavaClass = applicationJavaClass; g.GenerateOnCreateOverrides = true; From f4d1a5b0074031b5e577449bbfbb97481cecdfa8 Mon Sep 17 00:00:00 2001 From: Jonathan Pryor Date: Thu, 15 Feb 2024 18:04:41 -0500 Subject: [PATCH 7/9] Prefer multi-line initializers + trailing comma --- .../JavaCallableWrapperGeneratorTests.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/Java.Interop.Tools.JavaCallableWrappers-Tests/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGeneratorTests.cs b/tests/Java.Interop.Tools.JavaCallableWrappers-Tests/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGeneratorTests.cs index d67f25024..033525c52 100644 --- a/tests/Java.Interop.Tools.JavaCallableWrappers-Tests/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGeneratorTests.cs +++ b/tests/Java.Interop.Tools.JavaCallableWrappers-Tests/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGeneratorTests.cs @@ -115,7 +115,9 @@ static string Generate (Type type, string applicationJavaClass = null, string mo var o = new StringWriter (); var dir = Path.GetDirectoryName (typeof (JavaCallableWrapperGeneratorTests).Assembly.Location); - var options = new CallableWrapperWriterOptions { CodeGenerationTarget = style }; + var options = new CallableWrapperWriterOptions { + CodeGenerationTarget = style, + }; g.Generate (Path.Combine (dir, "__o"), options); g.Generate (o, options); From 72c7b509c00426c883184bd1569a6a22cfd93a9a Mon Sep 17 00:00:00 2001 From: Jonathan Pryor Date: Thu, 15 Feb 2024 18:05:04 -0500 Subject: [PATCH 8/9] Convention: filename in directory matching namespace. This also results in a less "noisy" patch, as these files are otherwise unchanged. --- .../CecilExtensions.cs | 0 .../Crc64.Table.cs | 0 .../Crc64.cs | 0 .../Crc64Helper.cs | 0 .../IdentifierValidator.cs | 0 .../JavaCallableMethodClassifier.cs | 0 .../JavaPeerStyle.cs | 0 .../JavaTypeScanner.cs | 0 .../TypeNameMapGenerator.cs | 0 9 files changed, 0 insertions(+), 0 deletions(-) rename src/Java.Interop.Tools.JavaCallableWrappers/{Java.Interop.Tools.JavaCallableWrappers.Utilities => Java.Interop.Tools.JavaCallableWrappers}/CecilExtensions.cs (100%) rename src/Java.Interop.Tools.JavaCallableWrappers/{Java.Interop.Tools.JavaCallableWrappers.Utilities => Java.Interop.Tools.JavaCallableWrappers}/Crc64.Table.cs (100%) rename src/Java.Interop.Tools.JavaCallableWrappers/{Java.Interop.Tools.JavaCallableWrappers.Utilities => Java.Interop.Tools.JavaCallableWrappers}/Crc64.cs (100%) rename src/Java.Interop.Tools.JavaCallableWrappers/{Java.Interop.Tools.JavaCallableWrappers.Utilities => Java.Interop.Tools.JavaCallableWrappers}/Crc64Helper.cs (100%) rename src/Java.Interop.Tools.JavaCallableWrappers/{Java.Interop.Tools.JavaCallableWrappers.Utilities => Java.Interop.Tools.JavaCallableWrappers}/IdentifierValidator.cs (100%) rename src/Java.Interop.Tools.JavaCallableWrappers/{Java.Interop.Tools.JavaCallableWrappers.Utilities => Java.Interop.Tools.JavaCallableWrappers}/JavaCallableMethodClassifier.cs (100%) rename src/Java.Interop.Tools.JavaCallableWrappers/{Java.Interop.Tools.JavaCallableWrappers.Utilities => Java.Interop.Tools.JavaCallableWrappers}/JavaPeerStyle.cs (100%) rename src/Java.Interop.Tools.JavaCallableWrappers/{Java.Interop.Tools.JavaCallableWrappers.Utilities => Java.Interop.Tools.JavaCallableWrappers}/JavaTypeScanner.cs (100%) rename src/Java.Interop.Tools.JavaCallableWrappers/{Java.Interop.Tools.JavaCallableWrappers.Utilities => Java.Interop.Tools.JavaCallableWrappers}/TypeNameMapGenerator.cs (100%) diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.Utilities/CecilExtensions.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/CecilExtensions.cs similarity index 100% rename from src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.Utilities/CecilExtensions.cs rename to src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/CecilExtensions.cs diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.Utilities/Crc64.Table.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/Crc64.Table.cs similarity index 100% rename from src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.Utilities/Crc64.Table.cs rename to src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/Crc64.Table.cs diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.Utilities/Crc64.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/Crc64.cs similarity index 100% rename from src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.Utilities/Crc64.cs rename to src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/Crc64.cs diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.Utilities/Crc64Helper.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/Crc64Helper.cs similarity index 100% rename from src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.Utilities/Crc64Helper.cs rename to src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/Crc64Helper.cs diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.Utilities/IdentifierValidator.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/IdentifierValidator.cs similarity index 100% rename from src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.Utilities/IdentifierValidator.cs rename to src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/IdentifierValidator.cs diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.Utilities/JavaCallableMethodClassifier.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableMethodClassifier.cs similarity index 100% rename from src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.Utilities/JavaCallableMethodClassifier.cs rename to src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableMethodClassifier.cs diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.Utilities/JavaPeerStyle.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaPeerStyle.cs similarity index 100% rename from src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.Utilities/JavaPeerStyle.cs rename to src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaPeerStyle.cs diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.Utilities/JavaTypeScanner.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaTypeScanner.cs similarity index 100% rename from src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.Utilities/JavaTypeScanner.cs rename to src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaTypeScanner.cs diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.Utilities/TypeNameMapGenerator.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/TypeNameMapGenerator.cs similarity index 100% rename from src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.Utilities/TypeNameMapGenerator.cs rename to src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/TypeNameMapGenerator.cs From 16e9711965ae30c57b566121d4ce62d985b853b0 Mon Sep 17 00:00:00 2001 From: Jonathan Pobst Date: Fri, 16 Feb 2024 09:08:12 -1000 Subject: [PATCH 9/9] Add CallableWrapperReaderOptions. --- .../CecilImporter.cs | 97 ++++++++++--------- .../CallableWrapperConstructor.cs | 9 +- .../CallableWrapperMethod.cs | 4 +- .../CallableWrapperReaderOptions.cs | 9 ++ .../JavaCallableWrapperGeneratorTests.cs | 12 ++- 5 files changed, 74 insertions(+), 57 deletions(-) create mode 100644 src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperReaderOptions.cs diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.Adapters/CecilImporter.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.Adapters/CecilImporter.cs index 8ae057585..51cb90707 100644 --- a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.Adapters/CecilImporter.cs +++ b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.Adapters/CecilImporter.cs @@ -14,7 +14,11 @@ namespace Java.Interop.Tools.JavaCallableWrappers.Adapters; public class CecilImporter { - public static CallableWrapperType CreateType (TypeDefinition type, IMetadataResolver resolver, string? outerType = null, JavaCallableMethodClassifier? methodClassifier = null) + // Don't expose internal "outerType" parameter to the public API + public static CallableWrapperType CreateType (TypeDefinition type, IMetadataResolver resolver, CallableWrapperReaderOptions? options = null) + => CreateType (type, resolver, options, null); + + static CallableWrapperType CreateType (TypeDefinition type, IMetadataResolver resolver, CallableWrapperReaderOptions? options = null, string? outerType = null) { if (type.IsEnum || type.IsInterface || type.IsValueType) Diagnostic.Error (4200, CecilExtensions.LookupSource (type), Localization.Resources.JavaCallableWrappers_XA4200, type.FullName); @@ -31,6 +35,8 @@ public static CallableWrapperType CreateType (TypeDefinition type, IMetadataReso ExtractJavaNames (jniName, out var package, out var name); + options ??= new CallableWrapperReaderOptions (); + if (string.IsNullOrEmpty (package) && (type.IsSubclassOf ("Android.App.Activity", resolver) || type.IsSubclassOf ("Android.App.Application", resolver) || @@ -43,6 +49,9 @@ public static CallableWrapperType CreateType (TypeDefinition type, IMetadataReso IsApplication = JavaNativeTypeManager.IsApplication (type, resolver), IsInstrumentation = JavaNativeTypeManager.IsInstrumentation (type, resolver), IsAbstract = type.IsAbstract, + ApplicationJavaClass = options.DefaultApplicationJavaClass, + GenerateOnCreateOverrides = options.DefaultGenerateOnCreateOverrides, + MonoRuntimeInitialization = options.DefaultMonoRuntimeInitialization, }; // Type annotations @@ -70,18 +79,18 @@ public static CallableWrapperType CreateType (TypeDefinition type, IMetadataReso var baseRegisteredMethod = CecilExtensions.GetBaseRegisteredMethod (minfo, resolver); if (baseRegisteredMethod is not null) - AddMethod (cwt, type, baseRegisteredMethod, minfo, methodClassifier, resolver); + AddMethod (cwt, type, baseRegisteredMethod, minfo, options.MethodClassifier, resolver); else if (minfo.AnyCustomAttributes ("Java.Interop.JavaCallableAttribute")) { - AddMethod (cwt, type, null, minfo, methodClassifier, resolver); + AddMethod (cwt, type, null, minfo, options.MethodClassifier, resolver); cwt.HasExport = true; } else if (minfo.AnyCustomAttributes ("Java.Interop.JavaCallableConstructorAttribute")) { - AddMethod (cwt, type, null, minfo, methodClassifier, resolver); + AddMethod (cwt, type, null, minfo, options.MethodClassifier, resolver); cwt.HasExport = true; } else if (minfo.AnyCustomAttributes (typeof (ExportFieldAttribute))) { - AddMethod (cwt, type, null, minfo, methodClassifier, resolver); + AddMethod (cwt, type, null, minfo, options.MethodClassifier, resolver); cwt.HasExport = true; } else if (minfo.AnyCustomAttributes (typeof (ExportAttribute))) { - AddMethod (cwt, type, null, minfo, methodClassifier, resolver); + AddMethod (cwt, type, null, minfo, options.MethodClassifier, resolver); cwt.HasExport = true; } } @@ -106,7 +115,7 @@ public static CallableWrapperType CreateType (TypeDefinition type, IMetadataReso if (imethod.IsStatic) continue; - AddMethod (cwt, type, imethod, imethod, methodClassifier, resolver); + AddMethod (cwt, type, imethod, imethod, options.MethodClassifier, resolver); } } @@ -148,7 +157,7 @@ public static CallableWrapperType CreateType (TypeDefinition type, IMetadataReso AddConstructors (cwt, ctorTypes [i], type, outerType, baseCtors, curCtors, false, resolver); } - AddNestedTypes (cwt, type, resolver, methodClassifier); + AddNestedTypes (cwt, type, resolver, options); return cwt; } @@ -201,21 +210,19 @@ static CallableWrapperConstructor CreateConstructor (string name, CallableWrappe signature = signature ?? throw new ArgumentNullException ("`connector` cannot be null.", nameof (connector)); var method_name = "n_" + name + ":" + signature + ":" + connector; - var method = new CallableWrapperConstructor (name, method_name, signature); + var method = new CallableWrapperConstructor (type, name, method_name, signature); PopulateMethod (method, signature, managedParameters, outerType, superCall); method.Name = type.Name; - method.CannotRegisterInStaticConstructor = type.CannotRegisterInStaticConstructor; - method.PartialAssemblyQualifiedName = type.PartialAssemblyQualifiedName; return method; } // Method with a [Register] attribute - static CallableWrapperMethod CreateMethod (MethodDefinition methodDefinition, RegisterAttribute register, string? managedParameters, string? outerType, IMetadataResolver resolver, bool shouldBeDynamicallyRegistered = true) + static CallableWrapperMethod CreateMethod (MethodDefinition methodDefinition, CallableWrapperType declaringType, RegisterAttribute register, string? managedParameters, string? outerType, IMetadataResolver resolver, bool shouldBeDynamicallyRegistered = true) { - var method = CreateMethod (register.Name, register.Signature, register.Connector, managedParameters, outerType, null); + var method = CreateMethod (register.Name, declaringType, register.Signature, register.Connector, managedParameters, outerType, null); method.Annotations.AddRange (CreateAnnotations (methodDefinition, resolver)); method.IsDynamicallyRegistered = shouldBeDynamicallyRegistered; @@ -224,9 +231,9 @@ static CallableWrapperMethod CreateMethod (MethodDefinition methodDefinition, Re } // Method with an [Export] attribute - static CallableWrapperMethod CreateMethod (MethodDefinition methodDefinition, ExportAttribute export, string? managedParameters, IMetadataResolver resolver) + static CallableWrapperMethod CreateMethod (MethodDefinition methodDefinition, CallableWrapperType declaringType, ExportAttribute export, string? managedParameters, IMetadataResolver resolver) { - var method = CreateMethod (methodDefinition.Name, JavaNativeTypeManager.GetJniSignature (methodDefinition, resolver), "__export__", null, null, export.SuperArgumentsString); + var method = CreateMethod (methodDefinition.Name, declaringType, JavaNativeTypeManager.GetJniSignature (methodDefinition, resolver), "__export__", null, null, export.SuperArgumentsString); method.IsExport = true; method.IsStatic = methodDefinition.IsStatic; @@ -240,9 +247,9 @@ static CallableWrapperMethod CreateMethod (MethodDefinition methodDefinition, Ex } // Method with an [ExportField] attribute - static CallableWrapperMethod CreateMethod (MethodDefinition methodDefinition, IMetadataResolver resolver) + static CallableWrapperMethod CreateMethod (MethodDefinition methodDefinition, CallableWrapperType declaringType, IMetadataResolver resolver) { - var method = CreateMethod (methodDefinition.Name, JavaNativeTypeManager.GetJniSignature (methodDefinition, resolver), "__export__", null, null, null); + var method = CreateMethod (methodDefinition.Name, declaringType, JavaNativeTypeManager.GetJniSignature (methodDefinition, resolver), "__export__", null, null, null); if (methodDefinition.HasParameters) Diagnostic.Error (4205, CecilExtensions.LookupSource (methodDefinition), Localization.Resources.JavaCallableWrappers_XA4205); @@ -259,12 +266,12 @@ static CallableWrapperMethod CreateMethod (MethodDefinition methodDefinition, IM } // Common method creation code - static CallableWrapperMethod CreateMethod (string name, string? signature, string? connector, string? managedParameters, string? outerType, string? superCall) + static CallableWrapperMethod CreateMethod (string name, CallableWrapperType declaringType, string? signature, string? connector, string? managedParameters, string? outerType, string? superCall) { signature = signature ?? throw new ArgumentNullException ("`connector` cannot be null.", nameof (connector)); var method_name = "n_" + name + ":" + signature + ":" + connector; - var method = new CallableWrapperMethod (name, method_name, signature); + var method = new CallableWrapperMethod (declaringType, name, method_name, signature); PopulateMethod (method, signature, managedParameters, outerType, superCall); @@ -320,15 +327,15 @@ static void PopulateMethod (CallableWrapperMethod method, string signature, stri method.ActivateCall = acall.ToString (); } - static void AddConstructors (CallableWrapperType cwt, TypeDefinition type, TypeDefinition rootType, string? outerType, List? baseCtors, List curCtors, bool onlyRegisteredOrExportedCtors, IMetadataResolver cache) + static void AddConstructors (CallableWrapperType declaringType, TypeDefinition type, TypeDefinition rootType, string? outerType, List? baseCtors, List curCtors, bool onlyRegisteredOrExportedCtors, IMetadataResolver cache) { foreach (var ctor in type.Methods) if (ctor.IsConstructor && !ctor.IsStatic && !ctor.AnyCustomAttributes (typeof (ExportAttribute))) - if (CreateConstructor (cwt, ctor, type, rootType, outerType, baseCtors, curCtors, onlyRegisteredOrExportedCtors, false, cache) is CallableWrapperConstructor c) - cwt.Constructors.Add (c); + if (CreateConstructor (declaringType, ctor, type, rootType, outerType, baseCtors, curCtors, onlyRegisteredOrExportedCtors, false, cache) is CallableWrapperConstructor c) + declaringType.Constructors.Add (c); } - static CallableWrapperConstructor? CreateConstructor (CallableWrapperType cwt, MethodDefinition ctor, TypeDefinition type, TypeDefinition rootType, string? outerType, List? baseCtors, List curCtors, bool onlyRegisteredOrExportedCtors, bool skipParameterCheck, IMetadataResolver cache) + static CallableWrapperConstructor? CreateConstructor (CallableWrapperType declaringType, MethodDefinition ctor, TypeDefinition type, TypeDefinition rootType, string? outerType, List? baseCtors, List curCtors, bool onlyRegisteredOrExportedCtors, bool skipParameterCheck, IMetadataResolver cache) { // We create a parameter-less constructor for the application class, so don't use the imported one if (!ctor.HasParameters && JavaNativeTypeManager.IsApplication (rootType, cache)) @@ -336,7 +343,7 @@ static void AddConstructors (CallableWrapperType cwt, TypeDefinition type, TypeD var managedParameters = GetManagedParameters (ctor, outerType, type, cache); - if (!skipParameterCheck && (managedParameters == null || cwt.Constructors.Any (c => c.ManagedParameters == managedParameters))) + if (!skipParameterCheck && (managedParameters == null || declaringType.Constructors.Any (c => c.ManagedParameters == managedParameters))) return null; // Constructor with [Export] attribute @@ -348,18 +355,18 @@ static void AddConstructors (CallableWrapperType cwt, TypeDefinition type, TypeD } curCtors.Add (ctor); - return CreateConstructor (ctor, cwt, eattr, managedParameters, cache); + return CreateConstructor (ctor, declaringType, eattr, managedParameters, cache); } // Constructor with [Register] attribute var rattr = CecilExtensions.GetMethodRegistrationAttributes (ctor).FirstOrDefault (); if (rattr != null) { - if (cwt.Constructors.Any (c => c.JniSignature == rattr.Signature)) + if (declaringType.Constructors.Any (c => c.JniSignature == rattr.Signature)) return null; curCtors.Add (ctor); - return CreateConstructor (ctor, cwt, rattr, managedParameters, outerType, cache); + return CreateConstructor (ctor, declaringType, rattr, managedParameters, outerType, cache); } if (onlyRegisteredOrExportedCtors) @@ -371,7 +378,7 @@ static void AddConstructors (CallableWrapperType cwt, TypeDefinition type, TypeD if (jniSignature is null) return null; - if (cwt.Constructors.Any (c => c.JniSignature == jniSignature)) + if (declaringType.Constructors.Any (c => c.JniSignature == jniSignature)) return null; if (baseCtors is null) @@ -379,12 +386,12 @@ static void AddConstructors (CallableWrapperType cwt, TypeDefinition type, TypeD if (baseCtors.Any (m => m.Parameters.AreParametersCompatibleWith (ctor.Parameters, cache))) { curCtors.Add (ctor); - return CreateConstructor (".ctor", cwt, jniSignature, "", managedParameters, outerType, null); + return CreateConstructor (".ctor", declaringType, jniSignature, "", managedParameters, outerType, null); } if (baseCtors.Any (m => !m.HasParameters)) { curCtors.Add (ctor); - return CreateConstructor (".ctor", cwt, jniSignature, "", managedParameters, outerType, ""); + return CreateConstructor (".ctor", declaringType, jniSignature, "", managedParameters, outerType, ""); } return null; @@ -414,7 +421,7 @@ static string GetManagedParameters (MethodDefinition ctor, string? outerType, Ty return new CallableWrapperApplicationConstructor (name); } - static void AddNestedTypes (CallableWrapperType cwt, TypeDefinition type, IMetadataResolver cache,JavaCallableMethodClassifier? methodClassifier) + static void AddNestedTypes (CallableWrapperType declaringType, TypeDefinition type, IMetadataResolver cache, CallableWrapperReaderOptions? options) { if (!type.HasNestedTypes) return; @@ -425,14 +432,14 @@ static void AddNestedTypes (CallableWrapperType cwt, TypeDefinition type, IMetad if (!JavaNativeTypeManager.IsNonStaticInnerClass (nt, cache)) continue; - cwt.NestedTypes.Add (CreateType (nt, cache, JavaNativeTypeManager.ToJniName (type, cache), methodClassifier)); - AddNestedTypes (cwt, nt, cache, methodClassifier); + declaringType.NestedTypes.Add (CreateType (nt, cache, options, JavaNativeTypeManager.ToJniName (type, cache))); + AddNestedTypes (declaringType, nt, cache, options); } - cwt.HasExport |= cwt.NestedTypes.Any (t => t.HasExport); + declaringType.HasExport |= declaringType.NestedTypes.Any (t => t.HasExport); } - static void AddMethod (CallableWrapperType cwt, TypeDefinition type, MethodDefinition? registeredMethod, MethodDefinition implementedMethod, JavaCallableMethodClassifier? methodClassifier, IMetadataResolver cache) + static void AddMethod (CallableWrapperType declaringType, TypeDefinition type, MethodDefinition? registeredMethod, MethodDefinition implementedMethod, JavaCallableMethodClassifier? methodClassifier, IMetadataResolver cache) { if (registeredMethod != null) foreach (RegisterAttribute attr in CecilExtensions.GetMethodRegistrationAttributes (registeredMethod)) { @@ -441,33 +448,33 @@ static void AddMethod (CallableWrapperType cwt, TypeDefinition type, MethodDefin Diagnostic.Error (4217, CecilExtensions.LookupSource (implementedMethod), Localization.Resources.JavaCallableWrappers_XA4217, attr.Name); var shouldBeDynamicallyRegistered = methodClassifier?.ShouldBeDynamicallyRegistered (type, registeredMethod, implementedMethod, attr.OriginAttribute) ?? true; - var method = CreateMethod (implementedMethod, attr, null, null, cache, shouldBeDynamicallyRegistered); + var method = CreateMethod (implementedMethod, declaringType, attr, null, null, cache, shouldBeDynamicallyRegistered); - if (!registeredMethod.IsConstructor && !cwt.Methods.Any (m => m.Name == method.Name && m.Params == method.Params)) - cwt.Methods.Add (method); + if (!registeredMethod.IsConstructor && !declaringType.Methods.Any (m => m.Name == method.Name && m.Params == method.Params)) + declaringType.Methods.Add (method); } foreach (ExportAttribute attr in CecilExtensions.GetExportAttributes (implementedMethod, cache)) { if (type.HasGenericParameters) Diagnostic.Error (4206, CecilExtensions.LookupSource (implementedMethod), Localization.Resources.JavaCallableWrappers_XA4206); - var method = CreateMethod (implementedMethod, attr, null, cache); + var method = CreateMethod (implementedMethod, declaringType, attr, null, cache); if (!string.IsNullOrEmpty (attr.SuperArgumentsString)) { // Diagnostic.Warning (log, "Use of ExportAttribute.SuperArgumentsString property is invalid on methods"); } - if (!implementedMethod.IsConstructor && !cwt.Methods.Any (m => m.Name == method.Name && m.Params == method.Params)) - cwt.Methods.Add (method); + if (!implementedMethod.IsConstructor && !declaringType.Methods.Any (m => m.Name == method.Name && m.Params == method.Params)) + declaringType.Methods.Add (method); } foreach (ExportFieldAttribute attr in CecilExtensions.GetExportFieldAttributes (implementedMethod)) { if (type.HasGenericParameters) Diagnostic.Error (4207, CecilExtensions.LookupSource (implementedMethod), Localization.Resources.JavaCallableWrappers_XA4207); - var method = CreateMethod (implementedMethod, cache); + var method = CreateMethod (implementedMethod, declaringType, cache); - if (!implementedMethod.IsConstructor && !cwt.Methods.Any (m => m.Name == method.Name && m.Params == method.Params)) { - cwt.Methods.Add (method); - cwt.Fields.Add (CreateField (implementedMethod, attr.Name, cache)); + if (!implementedMethod.IsConstructor && !declaringType.Methods.Any (m => m.Name == method.Name && m.Params == method.Params)) { + declaringType.Methods.Add (method); + declaringType.Fields.Add (CreateField (implementedMethod, attr.Name, cache)); } } } diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.CallableWrapperMembers/CallableWrapperConstructor.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.CallableWrapperMembers/CallableWrapperConstructor.cs index d07c39eff..59ef048ae 100644 --- a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.CallableWrapperMembers/CallableWrapperConstructor.cs +++ b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.CallableWrapperMembers/CallableWrapperConstructor.cs @@ -4,10 +4,7 @@ namespace Java.Interop.Tools.JavaCallableWrappers.CallableWrapperMembers; public class CallableWrapperConstructor : CallableWrapperMethod { - public bool CannotRegisterInStaticConstructor { get; set; } - public string? PartialAssemblyQualifiedName { get; set; } - - public CallableWrapperConstructor (string name, string method, string jniSignature) : base (name, method, jniSignature) + public CallableWrapperConstructor (CallableWrapperType declaringType, string name, string method, string jniSignature) : base (declaringType, name, method, jniSignature) { } @@ -40,7 +37,7 @@ public override void Generate (TextWriter sw, CallableWrapperWriterOptions optio sw.WriteLine ("\t\tandroid.util.Log.i(\"MonoDroid-Timing\", \"{0}..ctor({1}): time: \"+java.lang.System.currentTimeMillis());", Name, Params); #endif - if (!CannotRegisterInStaticConstructor) { + if (!DeclaringType.CannotRegisterInStaticConstructor) { sw.Write ("\t\tif (getClass () == "); sw.Write (Name); @@ -58,7 +55,7 @@ public override void Generate (TextWriter sw, CallableWrapperWriterOptions optio break; default: sw.Write ("mono.android.TypeManager.Activate (\""); - sw.Write (PartialAssemblyQualifiedName); + sw.Write (DeclaringType.PartialAssemblyQualifiedName); sw.Write ("\", \""); sw.Write (ManagedParameters); sw.Write ("\", this, new java.lang.Object[] { "); diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.CallableWrapperMembers/CallableWrapperMethod.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.CallableWrapperMembers/CallableWrapperMethod.cs index 412bb42e3..9b9e692d5 100644 --- a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.CallableWrapperMembers/CallableWrapperMethod.cs +++ b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.CallableWrapperMembers/CallableWrapperMethod.cs @@ -22,11 +22,13 @@ public class CallableWrapperMethod public string? ActivateCall { get; set; } public string JavaName => JavaNameOverride ?? Name; public List Annotations { get; } = new List (); + public CallableWrapperType DeclaringType { get; } public string? ThrowsDeclaration => ThrownTypeNames?.Length > 0 ? $" throws {string.Join (", ", ThrownTypeNames)}" : null; - public CallableWrapperMethod (string name, string method, string jniSignature) + public CallableWrapperMethod (CallableWrapperType declaringType, string name, string method, string jniSignature) { + DeclaringType = declaringType; Name = name; Method = method; JniSignature = jniSignature; diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperReaderOptions.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperReaderOptions.cs new file mode 100644 index 000000000..acd5a9633 --- /dev/null +++ b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/CallableWrapperReaderOptions.cs @@ -0,0 +1,9 @@ +namespace Java.Interop.Tools.JavaCallableWrappers; + +public class CallableWrapperReaderOptions +{ + public string? DefaultApplicationJavaClass { get; set; } + public bool DefaultGenerateOnCreateOverrides { get; set; } + public string? DefaultMonoRuntimeInitialization { get; set; } + public JavaCallableMethodClassifier? MethodClassifier { get; set; } +} diff --git a/tests/Java.Interop.Tools.JavaCallableWrappers-Tests/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGeneratorTests.cs b/tests/Java.Interop.Tools.JavaCallableWrappers-Tests/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGeneratorTests.cs index 033525c52..04e1f6049 100644 --- a/tests/Java.Interop.Tools.JavaCallableWrappers-Tests/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGeneratorTests.cs +++ b/tests/Java.Interop.Tools.JavaCallableWrappers-Tests/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGeneratorTests.cs @@ -106,12 +106,14 @@ public void monodroidClearReferences () static string Generate (Type type, string applicationJavaClass = null, string monoRuntimeInit = null, JavaPeerStyle style = JavaPeerStyle.XAJavaInterop1) { - var td = SupportDeclarations.GetTypeDefinition (type); - var g = CecilImporter.CreateType (td, new TypeDefinitionCache ()); + var reader_options = new CallableWrapperReaderOptions { + DefaultApplicationJavaClass = applicationJavaClass, + DefaultGenerateOnCreateOverrides = true, + DefaultMonoRuntimeInitialization = monoRuntimeInit, + }; - g.ApplicationJavaClass = applicationJavaClass; - g.GenerateOnCreateOverrides = true; - g.MonoRuntimeInitialization = monoRuntimeInit; + var td = SupportDeclarations.GetTypeDefinition (type); + var g = CecilImporter.CreateType (td, new TypeDefinitionCache (), reader_options); var o = new StringWriter (); var dir = Path.GetDirectoryName (typeof (JavaCallableWrapperGeneratorTests).Assembly.Location);