Skip to content
Open
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
Expand Up @@ -947,7 +947,7 @@ List<string> ResolveImplementedInterfaceJavaNames (TypeDefinition typeDef, Assem
return resolved is not null ? ResolveRegisterJniName (resolved.Value.typeName, resolved.Value.assemblyName) : null;
}

static bool TryGetMethodRegisterInfo (MethodDefinition methodDef, AssemblyIndex index, out RegisterInfo? registerInfo, out ExportInfo? exportInfo)
bool TryGetMethodRegisterInfo (MethodDefinition methodDef, AssemblyIndex index, out RegisterInfo? registerInfo, out ExportInfo? exportInfo)
{
exportInfo = null;
foreach (var caHandle in methodDef.GetCustomAttributes ()) {
Expand Down Expand Up @@ -997,7 +997,7 @@ static bool TryGetMethodRegisterInfo (MethodDefinition methodDef, AssemblyIndex
return null;
}

static (RegisterInfo registerInfo, ExportInfo exportInfo) ParseExportAttribute (CustomAttribute ca, MethodDefinition methodDef, AssemblyIndex index)
(RegisterInfo registerInfo, ExportInfo exportInfo) ParseExportAttribute (CustomAttribute ca, MethodDefinition methodDef, AssemblyIndex index)
{
var value = index.DecodeAttribute (ca);

Expand Down Expand Up @@ -1041,7 +1041,7 @@ static bool TryGetMethodRegisterInfo (MethodDefinition methodDef, AssemblyIndex
);
}

static string BuildJniSignatureFromManaged (MethodSignature<string> sig)
string BuildJniSignatureFromManaged (MethodSignature<string> sig)
{
var sb = new System.Text.StringBuilder ();
sb.Append ('(');
Expand All @@ -1058,7 +1058,7 @@ static string BuildJniSignatureFromManaged (MethodSignature<string> sig)
/// [ExportField] methods use the managed method name as the JNI name and have
/// a connector of "__export__" (matching legacy CecilImporter behavior).
/// </summary>
static (RegisterInfo registerInfo, ExportInfo exportInfo) ParseExportFieldAsMethod (CustomAttribute ca, MethodDefinition methodDef, AssemblyIndex index)
(RegisterInfo registerInfo, ExportInfo exportInfo) ParseExportFieldAsMethod (CustomAttribute ca, MethodDefinition methodDef, AssemblyIndex index)
{
var managedName = index.Reader.GetString (methodDef.Name);
var sig = methodDef.DecodeSignature (SignatureTypeProvider.Instance, genericContext: default);
Expand All @@ -1071,10 +1071,11 @@ static string BuildJniSignatureFromManaged (MethodSignature<string> sig)
}

/// <summary>
/// Maps a managed type name to its JNI descriptor. Falls back to
/// "Ljava/lang/Object;" for unknown types (used by [Export] signature computation).
/// Maps a managed type name to its JNI descriptor. Resolves Java-bound types
/// via their [Register] attribute, falling back to "Ljava/lang/Object;" only
/// for types that cannot be resolved (used by [Export] signature computation).
/// </summary>
static string ManagedTypeToJniDescriptor (string managedType)
string ManagedTypeToJniDescriptor (string managedType)
{
var primitive = TryGetPrimitiveJniDescriptor (managedType);
if (primitive is not null) {
Expand All @@ -1085,6 +1086,12 @@ static string ManagedTypeToJniDescriptor (string managedType)
return $"[{ManagedTypeToJniDescriptor (managedType.Substring (0, managedType.Length - 2))}";
}

// Try to resolve as a Java peer type with [Register]
var resolved = TryResolveJniObjectDescriptor (managedType);
if (resolved is not null) {
return resolved;
}

return "Ljava/lang/Object;";
}

Expand Down Expand Up @@ -1506,7 +1513,7 @@ static List<JavaConstructorInfo> BuildJavaConstructors (List<MarshalMethodInfo>
/// Checks a single method for [ExportField] and adds a JavaFieldInfo if found.
/// Called inline during Pass 1 to avoid a separate iteration.
/// </summary>
static void CollectExportField (MethodDefinition methodDef, AssemblyIndex index, List<JavaFieldInfo> fields)
void CollectExportField (MethodDefinition methodDef, AssemblyIndex index, List<JavaFieldInfo> fields)
{
foreach (var caHandle in methodDef.GetCustomAttributes ()) {
var ca = index.Reader.GetCustomAttribute (caHandle);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ public void Scanner_DetectsExportFieldsWithCorrectProperties ()
var staticField = peer.JavaFields.First (f => f.FieldName == "STATIC_INSTANCE");
Assert.True (staticField.IsStatic);
Assert.Equal ("GetInstance", staticField.InitializerMethodName);
// Reference type — mapped via JNI signature, not fallback to java.lang.Object
Assert.Equal ("java.lang.Object", staticField.JavaTypeName);
// Reference type — mapped via JNI signature to the actual Java type
Assert.Equal ("my.app.ExportFieldExample", staticField.JavaTypeName);

var instanceField = peer.JavaFields.First (f => f.FieldName == "VALUE");
Assert.False (instanceField.IsStatic);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,19 @@ public void Scan_MarshalMethod_ConstructorsAndSpecialCases ()
FindFixtureByManagedName ("Android.Views.IOnClickListener").InvokerTypeName);
}

[Theory]
[InlineData ("processView", "(Landroid/view/View;)V")]
[InlineData ("handleClick", "(Landroid/view/View;I)Z")]
[InlineData ("getViewName", "(Landroid/view/View;)Ljava/lang/String;")]
public void Scan_ExportMethod_ResolvesJavaBoundParameterTypes (string jniName, string expectedSig)
{
var method = FindFixtureByJavaName ("my/app/ExportWithJavaBoundParams")
.MarshalMethods.FirstOrDefault (m => m.JniName == jniName);
Assert.NotNull (method);
Assert.Equal (expectedSig, method.JniSignature);
Assert.Null (method.Connector);
}

[Theory]
[InlineData ("android/app/Activity", "Android.App.Activity")]
[InlineData ("my/app/SimpleActivity", "Android.App.Activity")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,23 @@ public class ExportExample : Java.Lang.Object
public void MyExportedMethod () { }
}

/// <summary>
/// Has [Export] methods with non-primitive Java-bound parameter types.
/// The JCW should resolve parameter types via [Register] instead of falling back to Object.
/// </summary>
[Register ("my/app/ExportWithJavaBoundParams")]
public class ExportWithJavaBoundParams : Java.Lang.Object
{
[Java.Interop.Export ("processView")]
public void ProcessView (Android.Views.View view) { }

[Java.Interop.Export ("handleClick")]
public bool HandleClick (Android.Views.View view, int action) { return false; }

[Java.Interop.Export ("getViewName")]
public string GetViewName (Android.Views.View view) { return ""; }
}

/// <summary>
/// Has [Export] methods with different access modifiers.
/// The JCW should respect the C# visibility for [Export] methods.
Expand Down
Loading