Skip to content

Commit 7587345

Browse files
radekdoulikjonpryor
authored andcommitted
[jnimarshalmethod-gen] Move generated helpers to the original assembly
Move the generated marshaling methods back to the original assembly in the nested classes. So for example a `MainActivity` class, derived from `Android.App.Activity`, which overrides `OnCreate` method, will get new nested class named `__<$>_jni_marshal_methods`. The nested class will contain these helper methods: ``` System.Void n_onCreate_Landroid_os_Bundle_(System.IntPtr,System.IntPtr,System.IntPtr) System.Void __RegisterNativeMembers(Java.Interop.JniNativeMethodRegistrationArguments) ``` These methods will be used during the native type registration.
1 parent 7a96efe commit 7587345

File tree

3 files changed

+292
-17
lines changed

3 files changed

+292
-17
lines changed

tools/jnimarshalmethod-gen/App.cs

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,13 @@
1414

1515
namespace Xamarin.Android.Tools.JniMarshalMethodGenerator {
1616

17-
class App {
17+
class App
18+
{
1819

1920
internal const string Name = "jnimarshalmethod-gen";
20-
static DirectoryAssemblyResolver resolver = new DirectoryAssemblyResolver (logger: (l, v) => { Console.WriteLine (v); }, loadDebugSymbols: false);
21+
static DirectoryAssemblyResolver resolver = new DirectoryAssemblyResolver (logger: (l, v) => { Console.WriteLine (v); }, loadDebugSymbols: true, loadReaderParameters: new ReaderParameters () { ReadSymbols = true });
2122
static Dictionary<string, TypeBuilder> definedTypes = new Dictionary<string, TypeBuilder> ();
22-
static bool verbose;
23+
static public bool Verbose;
2324

2425
public static int Main (string [] args)
2526
{
@@ -40,7 +41,7 @@ public static int Main (string [] args)
4041
v => help = v != null },
4142
{ "v|verbose",
4243
"Show this message and exit",
43-
v => verbose = true },
44+
v => Verbose = true },
4445
};
4546

4647
var jvm = CreateJavaVM ();
@@ -112,8 +113,8 @@ static void CreateMarshalMethodAssembly (string path)
112113
var destPath = assemblyName.Name + ".dll";
113114
var builder = CreateExportedMemberBuilder ();
114115

115-
if (verbose)
116-
ColorMessage ($"Preparing marshal method assembly '{assemblyName}'", ConsoleColor.Cyan);
116+
if (Verbose)
117+
ColorWriteLine ($"Preparing marshal method assembly '{assemblyName}'", ConsoleColor.Cyan);
117118

118119
var da = AppDomain.CurrentDomain.DefineDynamicAssembly (
119120
assemblyName,
@@ -131,7 +132,7 @@ static void CreateMarshalMethodAssembly (string path)
131132
var td = ad.MainModule.FindType (type);
132133

133134
if (td == null) {
134-
if (verbose)
135+
if (Verbose)
135136
Warning ($"Unable to find cecil's TypeDefinition of type {type}");
136137
continue;
137138
}
@@ -161,7 +162,7 @@ static void CreateMarshalMethodAssembly (string path)
161162
var md = td.GetMethodDefinition (method);
162163

163164
if (md == null) {
164-
if (verbose)
165+
if (Verbose)
165166
Warning ($"Unable to find cecil's MethodDefinition of method {method}");
166167
continue;
167168
}
@@ -173,11 +174,9 @@ static void CreateMarshalMethodAssembly (string path)
173174
if (dt == null)
174175
dt = GetTypeBuilder (dm, type);
175176

176-
if (verbose) {
177+
if (Verbose) {
177178
Console.Write ("Adding marshal method for ");
178-
Console.ForegroundColor = ConsoleColor.Green;
179-
Console.WriteLine ($"{method}");
180-
Console.ResetColor ();
179+
ColorWriteLine ($"{method}", ConsoleColor.Green );
181180
}
182181

183182
var mb = dt.DefineMethod (
@@ -206,8 +205,12 @@ static void CreateMarshalMethodAssembly (string path)
206205

207206
da.Save (destPath);
208207

209-
if (verbose)
210-
ColorMessage ($"Marshal method assembly '{assemblyName}' created", ConsoleColor.Cyan);
208+
if (Verbose)
209+
ColorWriteLine ($"Marshal method assembly '{assemblyName}' created", ConsoleColor.Cyan);
210+
211+
var dstAssembly = resolver.GetAssembly (destPath);
212+
var mover = new TypeMover (dstAssembly, ad, definedTypes);
213+
mover.Move ();
211214
}
212215

213216
static readonly MethodInfo Delegate_CreateDelegate = typeof (Delegate).GetMethod ("CreateDelegate", new[] {
@@ -253,14 +256,19 @@ static void AddRegisterNativeMembers (TypeBuilder dt, ParameterExpression target
253256
lambda.CompileToMethod (rb);
254257
}
255258

256-
static void ColorMessage (string message, ConsoleColor color, TextWriter writer)
259+
static void ColorMessage (string message, ConsoleColor color, TextWriter writer, bool writeLine = true)
257260
{
258261
Console.ForegroundColor = color;
259-
writer.WriteLine (message);
262+
if (writeLine)
263+
writer.WriteLine (message);
264+
else
265+
writer.Write (message);
260266
Console.ResetColor ();
261267
}
262268

263-
static void ColorMessage (string message, ConsoleColor color) => ColorMessage (message, color, Console.Out);
269+
public static void ColorWriteLine (string message, ConsoleColor color) => ColorMessage (message, color, Console.Out);
270+
271+
public static void ColorWrite (string message, ConsoleColor color) => ColorMessage (message, color, Console.Out, false);
264272

265273
public static void Error (string message) => ColorMessage ($"Error: {Name}: {message}", ConsoleColor.Red, Console.Error);
266274

Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
using System;
2+
using System.IO;
3+
using System.Collections.Generic;
4+
5+
using Mono.Cecil;
6+
using Mono.Cecil.Cil;
7+
8+
using Java.Interop.Tools.Cecil;
9+
10+
namespace Xamarin.Android.Tools.JniMarshalMethodGenerator
11+
{
12+
public class TypeMover
13+
{
14+
AssemblyDefinition Source { get; }
15+
AssemblyDefinition Destination { get; }
16+
Dictionary<string, System.Reflection.Emit.TypeBuilder> Types { get; }
17+
18+
public TypeMover (AssemblyDefinition source, AssemblyDefinition destination, Dictionary<string, System.Reflection.Emit.TypeBuilder> types)
19+
{
20+
Source = source;
21+
Destination = destination;
22+
Types = types;
23+
}
24+
25+
public void Move ()
26+
{
27+
foreach (var type in Types.Values)
28+
Move (type);
29+
30+
var newName = $"{Path.Combine (Path.GetDirectoryName (Destination.MainModule.FileName), Path.GetFileNameWithoutExtension (Destination.MainModule.FileName))}-new{Path.GetExtension (Destination.MainModule.FileName)}";
31+
Destination.Write (newName, new WriterParameters () { WriteSymbols = true });
32+
33+
App.ColorWriteLine ($"Wrote {newName} assembly", ConsoleColor.Cyan);
34+
}
35+
36+
static readonly string nestedName = "__<$>_jni_marshal_methods";
37+
38+
void Move (Type type)
39+
{
40+
var typeSrc = Source.MainModule.GetType (type.GetCecilName ());
41+
var typeDst = Destination.MainModule.GetType (type.GetCecilName ());
42+
var jniType = new TypeDefinition ("", nestedName, TypeAttributes.NestedPrivate | TypeAttributes.Sealed);
43+
44+
if (App.Verbose) {
45+
Console.Write ($"Moving type ");
46+
App.ColorWrite ($"{typeSrc.FullName},{typeSrc.Module.FileName}", ConsoleColor.Yellow);
47+
Console.Write (" to ");
48+
App.ColorWriteLine ($"{Destination.MainModule.FileName}", ConsoleColor.Yellow);
49+
}
50+
51+
jniType.BaseType = GetUpdatedType (typeSrc.BaseType, Destination.MainModule);
52+
typeDst.NestedTypes.Add (jniType);
53+
54+
foreach (var m in typeSrc.Methods) {
55+
if (App.Verbose) {
56+
Console.Write ("Moving method ");
57+
App.ColorWriteLine ($"{m}", ConsoleColor.Green);
58+
}
59+
jniType.Methods.Add (Duplicate (m, Destination.MainModule, typeDst));
60+
}
61+
}
62+
63+
static Dictionary<TypeReference, TypeReference> typeMap = new Dictionary<TypeReference, TypeReference> ();
64+
static Dictionary<TypeReference, TypeDefinition> resolvedTypeMap = new Dictionary<TypeReference, TypeDefinition> ();
65+
66+
static TypeDefinition Resolve (TypeReference type)
67+
{
68+
if (resolvedTypeMap.ContainsKey (type))
69+
return resolvedTypeMap [type];
70+
71+
var resolved = type.Resolve ();
72+
73+
resolvedTypeMap [type] = resolved;
74+
75+
return resolved;
76+
}
77+
78+
static TypeReference GetUpdatedType (TypeReference type, ModuleDefinition module)
79+
{
80+
if (typeMap.ContainsKey (type))
81+
return typeMap [type];
82+
83+
if (type is GenericInstanceType giType)
84+
return GetUpdatedGenericType (giType, module);
85+
86+
var td = Resolve (type);
87+
88+
var tr = td.Module.FileName != module.FileName
89+
? module.ImportReference (type)
90+
: module.GetType (td.FullName);
91+
92+
if (type.IsArray)
93+
tr = new ArrayType (tr);
94+
95+
typeMap [type] = tr;
96+
97+
return tr;
98+
}
99+
100+
static TypeReference GetUpdatedGenericType (GenericInstanceType type, ModuleDefinition module)
101+
{
102+
if (typeMap.ContainsKey (type))
103+
return typeMap [type];
104+
105+
var td = Resolve (type);
106+
var newType = new GenericInstanceType (GetUpdatedType (td, module));
107+
108+
if (type.HasGenericArguments)
109+
foreach (var ga in type.GenericArguments)
110+
newType.GenericArguments.Add (GetUpdatedType (ga, module));
111+
112+
if (type.HasGenericParameters)
113+
foreach (var gp in type.GenericParameters)
114+
newType.GenericParameters.Add (gp);
115+
116+
var tr = td.Module.FileName != module.FileName
117+
? module.ImportReference (newType)
118+
: module.GetType (newType.FullName);
119+
120+
if (type.IsArray)
121+
tr = new ArrayType (tr);
122+
123+
typeMap [type] = tr;
124+
125+
return tr;
126+
}
127+
128+
129+
static Dictionary<MethodReference, MethodReference> methodMap = new Dictionary<MethodReference, MethodReference> ();
130+
static Dictionary<MethodReference, MethodDefinition> resolvedMethodMap = new Dictionary<MethodReference, MethodDefinition> ();
131+
132+
static MethodDefinition ResolveMethod (MethodReference method)
133+
{
134+
if (resolvedMethodMap.ContainsKey (method))
135+
return resolvedMethodMap [method];
136+
137+
var resolved = method.Resolve ();
138+
139+
resolvedMethodMap [method] = resolved;
140+
141+
return resolved;
142+
}
143+
144+
static MethodReference GetUpdatedMethod (MethodReference method, ModuleDefinition module)
145+
{
146+
if (methodMap.ContainsKey (method))
147+
return methodMap [method];
148+
149+
MethodDefinition md = ResolveMethod (method.IsGenericInstance ? (method as GenericInstanceMethod).ElementMethod : method);
150+
MethodReference mr;
151+
152+
if (method.IsGenericInstance) {
153+
var newGenericMethod = new GenericInstanceMethod (md);
154+
155+
var genericInstance = method as GenericInstanceMethod;
156+
if (genericInstance.HasGenericArguments)
157+
foreach (var ga in genericInstance.GenericArguments)
158+
newGenericMethod.GenericArguments.Add (GetUpdatedType (ga, module));
159+
160+
mr = module.ImportReference (newGenericMethod);
161+
} else
162+
mr = module.ImportReference (md.Module != null && md.Module.FileName == module.FileName ? md : method);
163+
164+
foreach (var p in mr.Parameters)
165+
p.ParameterType = GetUpdatedType (p.ParameterType, module);
166+
167+
methodMap [method] = mr;
168+
169+
return mr;
170+
}
171+
172+
static FieldReference GetUpdatedField (FieldReference fr, ModuleDefinition module)
173+
{
174+
FieldReference newField = new FieldReference (fr.Name, GetUpdatedType (fr.FieldType, module));
175+
newField.DeclaringType = GetUpdatedType (fr.DeclaringType, module);
176+
177+
return newField;
178+
}
179+
180+
static Instruction GetUpdatedInstruction (Instruction il, TypeDefinition type, MethodDefinition method, ModuleDefinition module)
181+
{
182+
if (il.Operand == null)
183+
return Instruction.Create (il.OpCode);
184+
185+
var typeName = type.FullName.Replace ('/', '+');
186+
if (method.Name == "__RegisterNativeMembers" && il.OpCode == OpCodes.Ldstr && il.Operand is string opStr && opStr != null && opStr == typeName)
187+
il.Operand = $"{typeName}+{nestedName}";
188+
189+
if (il.Operand is MethodReference mr)
190+
return Instruction.Create (il.OpCode, GetUpdatedMethod (mr, module));
191+
192+
if (il.Operand is GenericInstanceType giType)
193+
return Instruction.Create (il.OpCode, GetUpdatedGenericType (giType, module));
194+
195+
if (il.Operand is TypeReference tr)
196+
return Instruction.Create (il.OpCode, GetUpdatedType (tr, module));
197+
198+
if (il.Operand is FieldReference fr)
199+
return Instruction.Create (il.OpCode, GetUpdatedField (fr, module));
200+
201+
return il;
202+
}
203+
204+
static ExceptionHandler GetUpdatedExceptionHandler (ExceptionHandler eh, Dictionary<Instruction, Instruction> instructionMap, ModuleDefinition module)
205+
{
206+
var handler = new ExceptionHandler (eh.HandlerType);
207+
208+
if (handler.CatchType != null)
209+
handler.CatchType = GetUpdatedType (eh.CatchType, module);
210+
211+
if (eh.TryStart != null)
212+
handler.TryStart = instructionMap [eh.TryStart];
213+
214+
if (eh.TryEnd != null)
215+
handler.TryEnd = instructionMap [eh.TryEnd];
216+
217+
if (eh.FilterStart != null)
218+
handler.FilterStart = instructionMap [eh.FilterStart];
219+
220+
if (eh.HandlerStart != null)
221+
handler.HandlerStart = instructionMap [eh.HandlerStart];
222+
223+
if (eh.HandlerEnd != null)
224+
handler.HandlerEnd = instructionMap [eh.HandlerEnd];
225+
226+
return handler;
227+
}
228+
229+
static MethodDefinition Duplicate (MethodDefinition src, ModuleDefinition module, TypeDefinition type)
230+
{
231+
var md = new MethodDefinition (src.Name, src.Attributes, GetUpdatedType (src.ReturnType, module));
232+
233+
if (src.HasCustomAttributes)
234+
foreach (var ca in src.CustomAttributes)
235+
md.CustomAttributes.Add (new CustomAttribute (GetUpdatedMethod (ca.Constructor, module), ca.GetBlob ()));
236+
237+
foreach (var p in src.Parameters)
238+
md.Parameters.Add (new ParameterDefinition (p.Name, p.Attributes, GetUpdatedType (p.ParameterType, module)));
239+
240+
md.Body.InitLocals = src.Body.InitLocals;
241+
242+
if (src.Body.HasVariables)
243+
foreach (var v in src.Body.Variables)
244+
md.Body.Variables.Add (new VariableDefinition (GetUpdatedType (v.VariableType, module)));
245+
246+
var instructionMap = new Dictionary<Instruction, Instruction> ();
247+
var instructions = src.Body.Instructions;
248+
var newInstructions = md.Body.Instructions;
249+
var count = instructions.Count;
250+
for (int i = 0; i < count; i++) {
251+
var il = instructions [i];
252+
Instruction newInstruction = GetUpdatedInstruction (il, type, src, module);
253+
newInstructions.Add (newInstruction);
254+
instructionMap [il] = newInstruction;
255+
}
256+
257+
if (src.Body.HasExceptionHandlers)
258+
foreach (var eh in src.Body.ExceptionHandlers)
259+
md.Body.ExceptionHandlers.Add (GetUpdatedExceptionHandler (eh, instructionMap, module));
260+
261+
md.Body.MaxStackSize = src.Body.MaxStackSize;
262+
263+
return md;
264+
}
265+
}
266+
}

tools/jnimarshalmethod-gen/Xamarin.Android.Tools.JniMarshalMethodGenerator.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
<ItemGroup>
3939
<Compile Include="Properties\AssemblyInfo.cs" />
4040
<Compile Include="App.cs" />
41+
<Compile Include="TypeMover.cs" />
4142
</ItemGroup>
4243
<Import Project="..\..\src\Java.Interop.NamingCustomAttributes\Java.Interop.NamingCustomAttributes.projitems" Label="Shared" Condition="Exists('..\..\src\Java.Interop.NamingCustomAttributes\Java.Interop.NamingCustomAttributes.projitems')" />
4344
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />

0 commit comments

Comments
 (0)