|
| 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 | +} |
0 commit comments