Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System;

using Mono.Cecil;

namespace Android.Runtime {

[AttributeUsage (AttributeTargets.Class | AttributeTargets.Constructor | AttributeTargets.Field | AttributeTargets.Interface | AttributeTargets.Method | AttributeTargets.Property)]
Expand All @@ -23,6 +25,21 @@ public RegisterAttribute (string name, string signature, string connector)
this.connector = connector;
this.signature = signature;
}
#if HAVE_CECIL
public RegisterAttribute (string name, CustomAttribute originAttribute)
: this (name)
{
OriginAttribute = originAttribute;
}

public RegisterAttribute (string name, string signature, string connector, CustomAttribute originAttribute)
: this (name, signature, connector)
{
OriginAttribute = originAttribute;
}

public CustomAttribute OriginAttribute { get; }
#endif // HAVE_CECIL

public string Connector {
get { return connector; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,40 +25,9 @@ public enum JavaPeerStyle {
JavaInterop1,
}

public class OverriddenMethodDescriptor
public abstract class JavaCallableMethodClassifier
{
static readonly char[] methodDescSplitChars = new char[] { ':' };

public string JavaPackageName { get; }
public string NativeName { get; }
public string JniSignature { get; }
public string Connector { get; }
public string ManagedTypeName { get; }
public string OriginalDescString { get; }

public OverriddenMethodDescriptor (string javaPackageName, string methodDescription, string fallbackManagedTypeName)
{
OriginalDescString = methodDescription;
JavaPackageName = javaPackageName;
string[] parts = methodDescription.Split (methodDescSplitChars, 4);

if (parts.Length < 2) {
throw new InvalidOperationException ($"Unexpected format for method description. Expected at least 2 parts, got {parts.Length} from: '{methodDescription}'");
}

NativeName = parts[0];
JniSignature = parts[1];
if (parts.Length > 2) {
Connector = parts[2];
if (parts.Length > 3) {
ManagedTypeName = TypeDefinitionRocks.CecilTypeNameToReflectionTypeName (parts[3]);
}
}

if (String.IsNullOrEmpty (ManagedTypeName)) {
ManagedTypeName = fallbackManagedTypeName;
}
}
public abstract bool ShouldBeDynamicallyRegistered (TypeDefinition topType, MethodDefinition registeredMethod, MethodDefinition implementedMethod, CustomAttribute registerAttribute);
}

public class JavaCallableWrapperGenerator {
Expand Down Expand Up @@ -95,36 +64,47 @@ public string GetJavaAccess ()
List<Signature> methods = new List<Signature> ();
List<Signature> ctors = new List<Signature> ();
List<JavaCallableWrapperGenerator> children;
List<OverriddenMethodDescriptor> overriddenMethodDescriptors;

readonly IMetadataResolver cache;
readonly JavaCallableMethodClassifier methodClassifier;

[Obsolete ("Use the TypeDefinitionCache overload for better performance.")]
public JavaCallableWrapperGenerator (TypeDefinition type, Action<string, object []> log)
: this (type, null, log, resolver: null)
: this (type, log, resolver: null, methodClassifier: null)
{ }

public JavaCallableWrapperGenerator (TypeDefinition type, Action<string, object[]> log, TypeDefinitionCache cache)
: this (type, log, (IMetadataResolver) cache)
: this (type, log, (IMetadataResolver) cache, methodClassifier: null)
{ }

public JavaCallableWrapperGenerator (TypeDefinition type, Action<string, object[]> log, TypeDefinitionCache cache, JavaCallableMethodClassifier methodClassifier)
: this (type, log, (IMetadataResolver) cache, methodClassifier)
{
}

public JavaCallableWrapperGenerator (TypeDefinition type, Action<string, object[]> log, IMetadataResolver resolver)
: this (type, null, log, resolver)
: this (type, log, resolver, methodClassifier: null)
{ }

public JavaCallableWrapperGenerator (TypeDefinition type, Action<string, object[]> log, IMetadataResolver resolver, JavaCallableMethodClassifier methodClassifier)
: this (type, null, log, resolver, methodClassifier)
{
if (type.HasNestedTypes) {
children = new List<JavaCallableWrapperGenerator> ();
AddNestedTypes (type);
}
}

public IList<OverriddenMethodDescriptor> OverriddenMethodDescriptors => overriddenMethodDescriptors;
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);

/// <summary>
/// The Java source code to be included in Instrumentation.onCreate
///
Expand Down Expand Up @@ -152,8 +132,9 @@ void AddNestedTypes (TypeDefinition type)
HasExport |= children.Any (t => t.HasExport);
}

JavaCallableWrapperGenerator (TypeDefinition type, string outerType, Action<string, object[]> log, IMetadataResolver resolver)
JavaCallableWrapperGenerator (TypeDefinition type, string outerType, Action<string, object[]> log, IMetadataResolver resolver, JavaCallableMethodClassifier methodClassifier = null)
{
this.methodClassifier = methodClassifier;
this.type = type;
this.log = log;
this.cache = resolver ?? new TypeDefinitionCache ();
Expand Down Expand Up @@ -366,12 +347,13 @@ internal static RegisterAttribute ToRegisterAttribute (CustomAttribute attr)
// attr.Resolve ();
RegisterAttribute r = null;
if (attr.ConstructorArguments.Count == 1)
r = new RegisterAttribute ((string) attr.ConstructorArguments [0].Value);
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);
(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;
Expand All @@ -384,7 +366,7 @@ internal static RegisterAttribute RegisterFromJniTypeSignatureAttribute (CustomA
// attr.Resolve ();
RegisterAttribute r = null;
if (attr.ConstructorArguments.Count == 1)
r = new RegisterAttribute ((string) attr.ConstructorArguments [0].Value);
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) {
Expand All @@ -403,7 +385,8 @@ internal static RegisterAttribute RegisterFromJniMethodSignatureAttribute (Custo
if (attr.ConstructorArguments.Count == 2)
r = new RegisterAttribute ((string) attr.ConstructorArguments [0].Value,
(string) attr.ConstructorArguments [1].Value,
"");
"",
attr);
return r;
}

Expand Down Expand Up @@ -467,7 +450,8 @@ void AddMethod (MethodDefinition registeredMethod, MethodDefinition implementedM
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);

var msig = new Signature (implementedMethod, attr);
bool shouldBeDynamicallyRegistered = methodClassifier?.ShouldBeDynamicallyRegistered (type, registeredMethod, implementedMethod, attr.OriginAttribute) ?? true;
var msig = new Signature (implementedMethod, attr, shouldBeDynamicallyRegistered);
if (!registeredMethod.IsConstructor && !methods.Any (m => m.Name == msig.Name && m.Params == msig.Params))
methods.Add (msig);
}
Expand Down Expand Up @@ -542,21 +526,38 @@ public void Generate (TextWriter writer)

GenerateHeader (writer);

writer.WriteLine ("/** @hide */");
writer.WriteLine ("\tpublic static final String __md_methods;");
if (children != null) {
foreach (var i in Enumerable.Range (1, children.Count))
writer.WriteLine ("\tstatic final String __md_{0}_methods;", i);
bool needCtor = false;
if (HasDynamicallyRegisteredMethods) {
needCtor = true;
writer.WriteLine ("/** @hide */");
writer.WriteLine ("\tpublic static final String __md_methods;");
}
writer.WriteLine ("\tstatic {");
GenerateRegisterType (writer, this, "__md_methods");

if (children != null) {
for (int i = 0; i < children.Count; ++i) {
string methods = string.Format ("__md_{0}_methods", i + 1);
GenerateRegisterType (writer, children [i], methods);
for (int i = 0; i < children.Count; i++) {
if (!children[i].HasDynamicallyRegisteredMethods) {
continue;
}
needCtor = true;
writer.WriteLine ("\tstatic final String __md_{0}_methods;", i + 1);
}
}
writer.WriteLine ("\t}");

if (needCtor) {
writer.WriteLine ("\tstatic {");

if (HasDynamicallyRegisteredMethods) {
GenerateRegisterType (writer, this, "__md_methods");
}

if (children != null) {
for (int i = 0; i < children.Count; ++i) {
string methods = string.Format ("__md_{0}_methods", i + 1);
GenerateRegisterType (writer, children [i], methods);
}
}
writer.WriteLine ("\t}");
}

GenerateBody (writer);

Expand Down Expand Up @@ -710,16 +711,18 @@ void GenerateBody (TextWriter sw)

void GenerateRegisterType (TextWriter sw, JavaCallableWrapperGenerator self, string field)
{
if (overriddenMethodDescriptors == null) {
overriddenMethodDescriptors = new List<OverriddenMethodDescriptor> ();
if (!self.HasDynamicallyRegisteredMethods) {
return;
}

sw.WriteLine ("\t\t{0} = ", field);
string managedTypeName = self.type.GetPartialAssemblyQualifiedName (cache);
string javaTypeName = $"{package}.{name}";

foreach (Signature method in self.methods) {
sw.WriteLine ("\t\t\t\"{0}\\n\" +", method.Method);
overriddenMethodDescriptors.Add (new OverriddenMethodDescriptor (javaTypeName, method.Method, managedTypeName));
if (method.IsDynamicallyRegistered) {
sw.WriteLine ("\t\t\t\"{0}\\n\" +", method.Method);
}
}
sw.WriteLine ("\t\t\t\"\";");
if (CannotRegisterInStaticConstructor (self.type))
Expand Down Expand Up @@ -772,12 +775,13 @@ bool CannotRegisterInStaticConstructor (TypeDefinition type)

class Signature {

public Signature (MethodDefinition method, RegisterAttribute register) : this (method, register, null, null) {}
public Signature (MethodDefinition method, RegisterAttribute register, bool shouldBeDynamicallyRegistered = true) : this (method, register, null, null, shouldBeDynamicallyRegistered) {}

public Signature (MethodDefinition method, RegisterAttribute register, string managedParameters, string outerType)
public Signature (MethodDefinition method, RegisterAttribute register, string managedParameters, string outerType, bool shouldBeDynamicallyRegistered = true)
: this (register.Name, register.Signature, register.Connector, managedParameters, outerType, null)
{
Annotations = JavaCallableWrapperGenerator.GetAnnotationsString ("\t", method.CustomAttributes);
IsDynamicallyRegistered = shouldBeDynamicallyRegistered;
}

public Signature (MethodDefinition method, ExportAttribute export, IMetadataResolver cache)
Expand Down Expand Up @@ -880,6 +884,7 @@ public string ThrowsDeclaration {
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;
}
Expand Down