Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[jcw-gen, jnimarshalmethod-gen] Native method consistency #1160

Merged
merged 1 commit into from
Nov 13, 2023
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,3 +1,4 @@
#nullable enable
using System;

namespace Java.Interop {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#nullable enable
using System;

namespace Java.Interop {

[AttributeUsage (AttributeTargets.Constructor, AllowMultiple=false)]
public sealed class JavaCallableConstructorAttribute : Attribute {

public JavaCallableConstructorAttribute ()
{
}

public string? SuperConstructorExpression {get; set;}
public string? Signature {get; set;}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public JniNativeMethodRegistration CreateMarshalToManagedMethodRegistration (Jav

string GetJniMethodName (JavaCallableAttribute export, MethodInfo method)
{
return export.Name ?? "n_" + method.Name;
return "n_" + method.Name;
}

public string GetJniMethodSignature (JavaCallableAttribute export, MethodInfo method)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq.Expressions;
using System.Runtime.InteropServices;
using System.Text;

using Java.Interop;
Expand Down Expand Up @@ -58,7 +59,7 @@ static MethodDefinition CreateMethodDefinition (AssemblyDefinition declaringAsse
return mmDef;
}

public MethodDefinition CreateRegistrationMethod (IList<ExpressionMethodRegistration> methods)
public void AddRegistrationMethod (TypeDefinition declaringType, IList<ExpressionMethodRegistration> methods)
{
var registrations = new MethodDefinition (
name: "__RegisterNativeMembers",
Expand All @@ -70,6 +71,8 @@ public MethodDefinition CreateRegistrationMethod (IList<ExpressionMethodRegistra
},
};

declaringType.Methods.Add (registrations);

var ctor = typeof (JniAddNativeMethodRegistrationAttribute).GetConstructor (Type.EmptyTypes);
var attr = new CustomAttribute (DeclaringAssemblyDefinition.MainModule.ImportReference (ctor));
registrations.CustomAttributes.Add (attr);
Expand All @@ -83,7 +86,7 @@ public MethodDefinition CreateRegistrationMethod (IList<ExpressionMethodRegistra
registrations.Body.Variables.Add (array);

var il = registrations.Body.GetILProcessor ();
EmitConsoleWriteLine (il, $"# jonp: called __RegisterNativeMembers w/ {methods.Count} methods to register.");
EmitConsoleWriteLine (il, $"# jonp: called `{declaringType.FullName}.__RegisterNativeMembers()` w/ {methods.Count} methods to register.");
il.Emit (OpCodes.Ldc_I4, methods.Count);
il.Emit (OpCodes.Newarr, DeclaringAssemblyDefinition.MainModule.ImportReference (arrayType.GetElementType ()));
// il.Emit (OpCodes.Stloc_0);
Expand Down Expand Up @@ -116,9 +119,6 @@ public MethodDefinition CreateRegistrationMethod (IList<ExpressionMethodRegistra
il.Emit (OpCodes.Ldloc_0);
il.Emit (OpCodes.Call, DeclaringAssemblyDefinition.MainModule.ImportReference (addRegistrations.Method));
il.Emit (OpCodes.Ret);


return registrations;
}

void EmitConsoleWriteLine (ILProcessor il, string message)
Expand Down Expand Up @@ -187,6 +187,15 @@ public TypeDefinition CreateMarshalMethodDelegateType (string delegateName, ILis
);
delegateDef.BaseType = DeclaringAssemblyDefinition.MainModule.ImportReference (typeof (MulticastDelegate));

var ufpCtor = typeof (UnmanagedFunctionPointerAttribute).GetConstructor (new[]{typeof (CallingConvention)});
var ufpCtorRef = DeclaringAssemblyDefinition.MainModule.ImportReference (ufpCtor);
var ufpAttr = new CustomAttribute (ufpCtorRef);
ufpAttr.ConstructorArguments.Add (
new CustomAttributeArgument (
DeclaringAssemblyDefinition.MainModule.ImportReference (typeof (CallingConvention)),
CallingConvention.Winapi));
delegateDef.CustomAttributes.Add (ufpAttr);

var delegateCtor = new MethodDefinition (
name: ".ctor",
attributes: MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
Expand Down Expand Up @@ -237,22 +246,10 @@ public void Write (string path)
};
var newAsm = AssemblyDefinition.ReadAssembly (c, rp);
module = newAsm.MainModule;
var systemRuntimeRef = module.AssemblyReferences.FirstOrDefault (r => r.Name == "System.Runtime");
var privateCorelibRef = module.AssemblyReferences.FirstOrDefault (r => r.Name == "System.Private.CoreLib");

if (systemRuntimeRef == null && privateCorelibRef != null) {
systemRuntimeRef = GetSystemRuntimeReference ();
module.AssemblyReferences.Add (systemRuntimeRef);
}

var selfRef = module.AssemblyReferences.FirstOrDefault (r => r.Name == newAsm.Name.Name);
foreach (var member in module.GetMemberReferences ()) {
Logger (TraceLevel.Verbose, $"# jonp: looking at ref for member: [{member.DeclaringType.Scope?.Name}]{member}");
if (member.DeclaringType.Scope == privateCorelibRef) {
Logger (TraceLevel.Verbose, $"# jonp: Fixing scope ref for member: {member}");
member.DeclaringType.Scope = systemRuntimeRef;
continue;
}
if (member.DeclaringType.Scope == selfRef) {
Logger (TraceLevel.Verbose, $"# jonp: Fixing scope self ref for member: {member}");
member.DeclaringType.Scope = null;
Expand All @@ -261,18 +258,12 @@ public void Write (string path)
}
foreach (var type in module.GetTypeReferences ()) {
Logger (TraceLevel.Verbose, $"# jonp: looking at ref for type: [{type.Scope}]{type}");
if (type.Scope == privateCorelibRef) {
Logger (TraceLevel.Verbose, $"# jonp: Fixing scope ref for type: {type}");
type.Scope = systemRuntimeRef;
continue;
}
if (type.Scope == selfRef) {
Logger (TraceLevel.Verbose, $"# jonp: Fixing scope self ref for type: {type}");
type.Scope = null;
continue;
}
}
module.AssemblyReferences.Remove (privateCorelibRef);
if (selfRef != null) {
module.AssemblyReferences.Remove (selfRef);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,13 @@ void AddNestedTypes (TypeDefinition type)
var baseRegisteredMethod = GetBaseRegisteredMethod (minfo);
if (baseRegisteredMethod != null)
AddMethod (baseRegisteredMethod, minfo);
else if (minfo.AnyCustomAttributes (typeof(ExportFieldAttribute))) {
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))) {
Expand Down Expand Up @@ -295,7 +301,7 @@ void AddConstructor (MethodDefinition ctor, TypeDefinition type, string? outerTy
if (!string.IsNullOrEmpty (eattr.Name)) {
// Diagnostic.Warning (log, "Use of ExportAttribute.Name property is invalid on constructors");
}
ctors.Add (new Signature (ctor, eattr, cache));
ctors.Add (new Signature (ctor, eattr, managedParameters, cache));
curCtors.Add (ctor);
return;
}
Expand Down Expand Up @@ -412,6 +418,25 @@ ExportAttribute ToExportAttribute (CustomAttribute attr, IMemberDefinition decla
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);
Expand Down Expand Up @@ -447,7 +472,11 @@ static IEnumerable<RegisterAttribute> GetMethodRegistrationAttributes (Mono.Ceci

IEnumerable<ExportAttribute> GetExportAttributes (IMemberDefinition p)
{
return GetAttributes<ExportAttribute> (p, a => ToExportAttribute (a, p));
return GetAttributes<ExportAttribute> (p, a => ToExportAttribute (a, p))
.Concat (GetAttributes<ExportAttribute> (p, "Java.Interop.JavaCallableAttribute",
a => ToExportAttributeFromJavaCallableAttribute (a, p)))
.Concat (GetAttributes<ExportAttribute> (p, "Java.Interop.JavaCallableConstructorAttribute",
a => ToExportAttributeFromJavaCallableConstructorAttribute (a, p)));
}

static IEnumerable<ExportFieldAttribute> GetExportFieldAttributes (Mono.Cecil.ICustomAttributeProvider p)
Expand All @@ -458,7 +487,13 @@ static IEnumerable<ExportFieldAttribute> GetExportFieldAttributes (Mono.Cecil.IC
static IEnumerable<TAttribute> GetAttributes<TAttribute> (Mono.Cecil.ICustomAttributeProvider p, Func<CustomAttribute, TAttribute?> selector)
where TAttribute : class
{
return p.GetCustomAttributes (typeof (TAttribute))
return GetAttributes (p, typeof (TAttribute).FullName, selector);
}

static IEnumerable<TAttribute> GetAttributes<TAttribute> (Mono.Cecil.ICustomAttributeProvider p, string attributeName, Func<CustomAttribute, TAttribute?> selector)
where TAttribute : class
{
return p.GetCustomAttributes (attributeName)
.Select (selector)
.Where (v => v != null)
.Select (v => v!);
Expand All @@ -481,7 +516,7 @@ void AddMethod (MethodDefinition? registeredMethod, MethodDefinition implemented
if (type.HasGenericParameters)
Diagnostic.Error (4206, LookupSource (implementedMethod), Localization.Resources.JavaCallableWrappers_XA4206);

var msig = new Signature (implementedMethod, attr, cache);
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");
}
Expand Down Expand Up @@ -834,14 +869,15 @@ public Signature (MethodDefinition method, RegisterAttribute register, string? m
IsDynamicallyRegistered = shouldBeDynamicallyRegistered;
}

public Signature (MethodDefinition method, ExportAttribute export, IMetadataResolver cache)
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);
}

Expand Down
16 changes: 12 additions & 4 deletions src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -412,11 +412,17 @@ protected bool TryRegisterNativeMembers (JniType nativeClass, Type type, string?
bool TryLoadJniMarshalMethods (JniType nativeClass, Type type, string? methods)
{
var marshalType = type?.GetNestedType ("__<$>_jni_marshal_methods", BindingFlags.NonPublic);
if (marshalType == null)
if (marshalType == null) {
return false;
}

var registerMethod = marshalType.GetRuntimeMethod ("__RegisterNativeMembers", registerMethodParameters);

var registerMethod = marshalType.GetMethod (
name: "__RegisterNativeMembers",
bindingAttr: BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public,
binder: null,
callConvention: default,
types: registerMethodParameters,
modifiers: null);
return TryRegisterNativeMembers (nativeClass, marshalType, methods, registerMethod);
}

Expand Down Expand Up @@ -466,8 +472,10 @@ bool FindAndCallRegisterMethod (Type marshalType, JniNativeMethodRegistrationArg
continue;
}

var declaringTypeName = methodInfo.DeclaringType?.FullName ?? "<no-decl-type>";

if ((methodInfo.Attributes & MethodAttributes.Static) != MethodAttributes.Static) {
throw new InvalidOperationException ($"The method {methodInfo} marked with {nameof (JniAddNativeMethodRegistrationAttribute)} must be static");
throw new InvalidOperationException ($"The method `{declaringTypeName}.{methodInfo}` marked with [{nameof (JniAddNativeMethodRegistrationAttribute)}] must be static!");
}

var register = (Action<JniNativeMethodRegistrationArguments>)methodInfo.CreateDelegate (typeof (Action<JniNativeMethodRegistrationArguments>));
Expand Down
2 changes: 2 additions & 0 deletions src/Java.Interop/Java.Interop/JniTypeSignatureAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ public sealed class JniTypeSignatureAttribute : Attribute {

public JniTypeSignatureAttribute (string simpleReference)
{
#if !JCW_ONLY_TYPE_NAMES
JniRuntime.JniTypeManager.AssertSimpleReference (simpleReference, nameof (simpleReference));
#endif // !JCW_ONLY_TYPE_NAMES

SimpleReference = simpleReference;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Java.Interop/Java.Interop/ManagedPeer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ static unsafe void RegisterNativeMembers (

}
catch (Exception e) when (JniEnvironment.Runtime.ExceptionShouldTransitionToJni (e)) {
Debug.WriteLine (e.ToString ());
Debug.WriteLine ($"Exception when trying to register native methods with JNI: {e}");
envp.SetPendingException (e);
}
finally {
Expand Down
4 changes: 4 additions & 0 deletions src/Java.Runtime.Environment/Java.Interop/JreTypeManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ public override void RegisterNativeMembers (JniType nativeClass, Type type, Read
registrations.Add (new JniNativeMethodRegistration (name, signature, marshaler!));
}

foreach (var registration in Runtime.MarshalMemberBuilder.GetExportedMemberRegistrations (type)) {
registrations.Add (registration);
}

nativeClass.RegisterNativeMethods (registrations.ToArray ());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,16 @@

<ItemGroup>
<ProjectReference Include="..\..\src\Java.Interop\Java.Interop.csproj" />
<ProjectReference Include="..\..\src\Java.Base\Java.Base.csproj" />
<ProjectReference Include="..\..\src\Java.Interop.Export\Java.Interop.Export.csproj" />
<ProjectReference Include="..\..\src\Java.Runtime.Environment\Java.Runtime.Environment.csproj" />
<ProjectReference Include="..\TestJVM\TestJVM.csproj" />
</ItemGroup>

<ItemGroup>
<JavaExportTestJar Include="$(MSBuildThisFileDirectory)java\com\xamarin\interop\export\ExportType.java" />
<JavaExportTestJar Include="$(MSBuildThisFileDirectory)java\**\*.java" />
</ItemGroup>

<Target Name="BuildExportTestJar" BeforeTargets="Build" Inputs="@(JavaExportTestJar)" Outputs="$(OutputPath)export-test.jar">
<MakeDir Directories="$(IntermediateOutputPath)et-classes" />
<Exec Command="&quot;$(JavaCPath)&quot; -classpath &quot;$(OutputPath)java-interop.jar&quot; $(_JavacSourceOptions) -d &quot;$(IntermediateOutputPath)et-classes&quot; @(JavaExportTestJar->'%(Identity)', ' ')" />
<Exec Command="&quot;$(JarPath)&quot; cf &quot;$(OutputPath)export-test.jar&quot; -C &quot;$(IntermediateOutputPath)et-classes&quot; ." />
</Target>
<Import Project="Java.Interop.Export-Tests.targets" />

</Project>
53 changes: 53 additions & 0 deletions tests/Java.Interop.Export-Tests/Java.Interop.Export-Tests.targets
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<Project>

<Target Name="_CreateJavaCallableWrappers"
Condition=" '$(TargetPath)' != '' "
BeforeTargets="BuildExportTestJar"
Inputs="$(TargetPath)"
Outputs="$(IntermediateOutputPath)java\.stamp">
<RemoveDir Directories="$(IntermediateOutputPath)java" />
<MakeDir Directories="$(IntermediateOutputPath)java" />
<ItemGroup>
<!-- I can't find a good way to trim the trailing `\`, so append with `.` so we can sanely quote for $(_Libpath) -->
<_RefAsmDirs Include="@(ReferencePathWithRefAssemblies->'%(RootDir)%(Directory).'->Distinct())" />
</ItemGroup>
<PropertyGroup>
<_JcwGen>"$(UtilityOutputFullPath)/jcw-gen.dll"</_JcwGen>
<_Target>--codegen-target JavaInterop1</_Target>
<_Output>-o "$(IntermediateOutputPath)/java"</_Output>
<_Libpath>@(_RefAsmDirs->'-L "%(Identity)"', ' ')</_Libpath>
</PropertyGroup>
<Exec Command="$(DotnetToolPath) $(_JcwGen) &quot;$(TargetPath)&quot; $(_Target) $(_Output) $(_Libpath)" />
<Touch Files="$(IntermediateOutputPath)java\.stamp" AlwaysCreate="True" />
</Target>

<Target Name="BuildExportTestJar"
AfterTargets="Build"
Inputs="$(TargetPath);@(JavaExportTestJar)"
Outputs="$(OutputPath)export-test.jar">
<MakeDir Directories="$(IntermediateOutputPath)et-classes" />
<ItemGroup>
<_JcwSource Include="$(IntermediateOutputPath)java\**\*.java" />
</ItemGroup>
<ItemGroup>
<_Source Include="@(_JcwSource->Replace('%5c', '/'))" />
<_Source Include="@(JavaExportTestJar->Replace('%5c', '/'))" />
</ItemGroup>
<WriteLinesToFile
File="$(IntermediateOutputPath)_java_sources.txt"
Lines="@(_Source)"
Overwrite="True"
/>
<ItemGroup>
<_JavacOpt Include="$(_JavacSourceOptions)" />
<_JavacOpt Include="-d &quot;$(IntermediateOutputPath)et-classes&quot; " />
<_JavacOpt Include="-classpath &quot;$(OutputPath)java-interop.jar&quot; " />
<_JavacOpt Include="&quot;@$(IntermediateOutputPath)_java_sources.txt&quot;" />
<_JavacOpt Include="-h &quot;$(IntermediateOutputPath)et-classes&quot; " />
</ItemGroup>
<Exec Command="&quot;$(JavaCPath)&quot; @(_JavacOpt, ' ')" />
<Delete Files="$(IntermediateOutputPath)_java_sources.txt" />
<Exec Command="&quot;$(JarPath)&quot; cf &quot;$(OutputPath)export-test.jar&quot; -C &quot;$(IntermediateOutputPath)et-classes&quot; ." />
</Target>

</Project>
Loading