/ILRepack/ILRepack.cs

https://code.google.com/p/il-repack/ · C# · 1434 lines · 1377 code · 30 blank · 27 comment · 31 complexity · d2ceb04d97ba3c1beb6903e9d7a80901 MD5 · raw file

  1. //
  2. // Copyright (c) 2011 Francois Valdy
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License");
  5. // you may not use this file except in compliance with the License.
  6. // You may obtain a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software
  11. // distributed under the License is distributed on an "AS IS" BASIS,
  12. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. // See the License for the specific language governing permissions and
  14. // limitations under the License.
  15. //
  16. using System;
  17. using System.Collections;
  18. using System.Collections.Generic;
  19. using System.IO;
  20. using System.Linq;
  21. using System.Reflection;
  22. using System.Runtime.Serialization.Formatters.Binary;
  23. using Mono.Cecil;
  24. using Mono.Cecil.Cil;
  25. using Mono.Collections.Generic;
  26. using CustomAttributeNamedArgument = Mono.Cecil.CustomAttributeNamedArgument;
  27. using MethodBody = Mono.Cecil.Cil.MethodBody;
  28. using System.Threading;
  29. namespace ILRepacking
  30. {
  31. public class ILRepack
  32. {
  33. // keep ILMerge syntax (both command-line & api) for compatibility (commented out: not implemented yet)
  34. public void AllowDuplicateType(string typeName)
  35. {
  36. allowedDuplicateTypes[typeName] = typeName;
  37. }
  38. public bool AllowDuplicateResources { get; set; }
  39. public bool AllowMultipleAssemblyLevelAttributes { get; set; }
  40. public bool AllowWildCards { get; set; }
  41. public bool AllowZeroPeKind { get; set; }
  42. public string AttributeFile { get; set; }
  43. public bool Closed { get; set; } // UNIMPL
  44. public bool CopyAttributes { get; set; }
  45. public bool DebugInfo { get; set; }
  46. public bool DelaySign { get; set; }
  47. public string ExcludeFile { get; set; }
  48. public int FileAlignment { get; set; } // UNIMPL, not supported by cecil
  49. public string[] InputAssemblies { get; set; }
  50. public bool Internalize { get; set; }
  51. public string KeyFile { get; set; }
  52. public bool Parallel { get; set; }
  53. public bool PauseBeforeExit { get; set; }
  54. public bool Log { get; set; }
  55. public string LogFile { get; set; }
  56. public void Merge()
  57. {
  58. Repack();
  59. }
  60. public string OutputFile { get; set; }
  61. public bool PublicKeyTokens { get; set; } // UNIMPL
  62. public void SetInputAssemblies(string[] inputAssemblies)
  63. {
  64. InputAssemblies = inputAssemblies;
  65. }
  66. public void SetSearchDirectories(string[] dirs)
  67. {
  68. foreach (var dir in dirs)
  69. {
  70. ((DefaultAssemblyResolver)GlobalAssemblyResolver.Instance).AddSearchDirectory(dir);
  71. }
  72. }
  73. public void SetTargetPlatform(string targetPlatformVersion, string targetPlatformDirectory)
  74. {
  75. TargetPlatformVersion = targetPlatformVersion;
  76. TargetPlatformDirectory = targetPlatformDirectory;
  77. }
  78. public bool StrongNameLost { get; private set; }
  79. public Kind? TargetKind { get; set; }
  80. public string TargetPlatformDirectory { get; set; }
  81. public string TargetPlatformVersion { get; set; }
  82. public bool UnionMerge { get; set; }
  83. public Version Version { get; set; }
  84. public bool XmlDocumentation { get; set; }
  85. // end of ILMerge-similar attributes
  86. public bool LogVerbose { get; set; }
  87. public bool NoRepackRes { get; set; }
  88. public bool KeepOtherVersionReferences { get; set; }
  89. internal List<string> MergedAssemblyFiles { get; set; }
  90. // contains all 'other' assemblies, but not the primary assembly
  91. internal List<AssemblyDefinition> OtherAssemblies { get; set; }
  92. // contains all assemblies, primary and 'other'
  93. internal List<AssemblyDefinition> MergedAssemblies { get; set; }
  94. internal AssemblyDefinition TargetAssemblyDefinition { get; set; }
  95. internal AssemblyDefinition PrimaryAssemblyDefinition { get; set; }
  96. // helpers
  97. internal ModuleDefinition TargetAssemblyMainModule { get { return TargetAssemblyDefinition.MainModule; } }
  98. private ModuleDefinition PrimaryAssemblyMainModule { get { return PrimaryAssemblyDefinition.MainModule; } }
  99. private StreamWriter logFile;
  100. private System.Collections.Hashtable allowedDuplicateTypes = new System.Collections.Hashtable();
  101. private List<System.Text.RegularExpressions.Regex> excludeInternalizeMatches;
  102. PlatformFixer platformFixer;
  103. private HashSet<string> mergeAsmNames;
  104. DuplicateHandler duplicateHandler;
  105. public ILRepack()
  106. {
  107. // default values
  108. LogVerbose = false;
  109. }
  110. private void AlwaysLog(object str)
  111. {
  112. string logStr = str.ToString();
  113. Console.WriteLine(logStr);
  114. if (logFile != null)
  115. logFile.WriteLine(logStr);
  116. }
  117. internal void LogOutput(object str)
  118. {
  119. if (Log)
  120. {
  121. AlwaysLog(str);
  122. }
  123. }
  124. private void InitializeLogFile()
  125. {
  126. if (!string.IsNullOrEmpty(LogFile))
  127. {
  128. Log = true;
  129. logFile = new StreamWriter(LogFile);
  130. }
  131. }
  132. private void CloseLogFile()
  133. {
  134. if (logFile != null)
  135. {
  136. logFile.Flush();
  137. logFile.Close();
  138. logFile.Dispose();
  139. logFile = null;
  140. }
  141. }
  142. internal void ERROR(string msg)
  143. {
  144. AlwaysLog("ERROR: " + msg);
  145. }
  146. internal void WARN(string msg)
  147. {
  148. AlwaysLog("WARN: " + msg);
  149. }
  150. internal void INFO(string msg)
  151. {
  152. LogOutput("INFO: " + msg);
  153. }
  154. internal void VERBOSE(string msg)
  155. {
  156. if (LogVerbose)
  157. {
  158. LogOutput("INFO: " + msg);
  159. }
  160. }
  161. internal void IGNOREDUP(string ignoredType, object ignoredObject)
  162. {
  163. // TODO: put on a list and log a summary
  164. //INFO("Ignoring duplicate " + ignoredType + " " + ignoredObject);
  165. }
  166. [STAThread]
  167. public static int Main(string[] args)
  168. {
  169. ILRepack repack = new ILRepack();
  170. int rc = -1;
  171. try
  172. {
  173. repack.ReadArguments(args);
  174. repack.Repack();
  175. rc = 0;
  176. }
  177. catch (Exception e)
  178. {
  179. repack.LogOutput(e);
  180. repack.CloseLogFile();
  181. rc = 1;
  182. }
  183. finally
  184. {
  185. repack.CloseLogFile();
  186. if (repack.PauseBeforeExit)
  187. {
  188. Console.WriteLine("Press Any Key To Continue");
  189. Console.ReadKey(true);
  190. }
  191. }
  192. return rc;
  193. }
  194. void Exit(int exitCode)
  195. {
  196. CloseLogFile();
  197. Environment.Exit(exitCode);
  198. }
  199. private void ReadArguments(string[] args)
  200. {
  201. CommandLine cmd = new CommandLine(args);
  202. if (cmd.Modifier("?") | cmd.Modifier("help") | cmd.Modifier("h") | args.Length == 0)
  203. {
  204. Usage();
  205. Exit(2);
  206. }
  207. AllowDuplicateResources = cmd.Modifier("allowduplicateresources");
  208. foreach (string dupType in cmd.Options("allowdup"))
  209. AllowDuplicateType(dupType);
  210. AllowMultipleAssemblyLevelAttributes = cmd.Modifier("allowmultiple");
  211. AllowWildCards = cmd.Modifier("wildcards");
  212. AllowZeroPeKind = cmd.Modifier("zeropekind");
  213. Parallel = cmd.Modifier("parallel");
  214. PauseBeforeExit = cmd.Modifier("pause");
  215. AttributeFile = cmd.Option("attr");
  216. Closed = cmd.Modifier("closed");
  217. CopyAttributes = cmd.Modifier("copyattrs");
  218. DebugInfo = !cmd.Modifier("ndebug");
  219. DelaySign = cmd.Modifier("delaysign");
  220. cmd.Option("align"); // not supported, just prevent interpreting this as file...
  221. Internalize = cmd.HasOption("internalize");
  222. if (Internalize)
  223. {
  224. // this file shall contain one regex per line to compare agains FullName of types NOT to internalize
  225. ExcludeFile = cmd.Option("internalize");
  226. }
  227. KeyFile = cmd.Option("keyfile");
  228. Log = cmd.HasOption("log");
  229. if (Log)
  230. LogFile = cmd.Option("log");
  231. OutputFile = cmd.Option("out");
  232. PublicKeyTokens = cmd.Modifier("usefullpublickeyforreferences");
  233. var targetKind = cmd.Option("target");
  234. if (string.IsNullOrEmpty(targetKind))
  235. targetKind = cmd.Option("t");
  236. if (!string.IsNullOrEmpty(targetKind))
  237. {
  238. switch (targetKind)
  239. {
  240. case ("library"):
  241. TargetKind = Kind.Dll;
  242. break;
  243. case ("exe"):
  244. TargetKind = Kind.Exe;
  245. break;
  246. case ("winexe"):
  247. TargetKind = Kind.WinExe;
  248. break;
  249. default:
  250. Console.WriteLine("Invalid target: \"" + targetKind + "\".");
  251. Usage();
  252. Exit(2);
  253. break;
  254. }
  255. }
  256. // TargetPlatformDirectory -> how does cecil handle that?
  257. TargetPlatformVersion = cmd.Option("targetplatform");
  258. if (cmd.Modifier("v1"))
  259. TargetPlatformVersion = "v1";
  260. if (cmd.Modifier("v1.1"))
  261. TargetPlatformVersion = "v1.1";
  262. if (cmd.Modifier("v2"))
  263. TargetPlatformVersion = "v2";
  264. if (cmd.Modifier("v4"))
  265. TargetPlatformVersion = "v4";
  266. UnionMerge = cmd.Modifier("union");
  267. var version = cmd.Option("ver");
  268. if (!string.IsNullOrEmpty(version))
  269. Version = new Version(version);
  270. XmlDocumentation = cmd.Modifier("xmldocs");
  271. NoRepackRes = cmd.Modifier("norepackres");
  272. KeepOtherVersionReferences = cmd.Modifier("keepotherversionreferences");
  273. SetSearchDirectories(cmd.Options("lib"));
  274. // private cmdline-options:
  275. LogVerbose = cmd.Modifier("verbose");
  276. if (string.IsNullOrEmpty(KeyFile) && DelaySign)
  277. WARN("Option 'delaysign' is only valid with 'keyfile'.");
  278. if (AllowMultipleAssemblyLevelAttributes && !CopyAttributes)
  279. WARN("Option 'allowMultiple' is only valid with 'copyattrs'.");
  280. if (!string.IsNullOrEmpty(AttributeFile) && (CopyAttributes))
  281. WARN("Option 'attr' can not be used with 'copyattrs'.");
  282. // everything that doesn't start with a '/' must be a file to merge (verify when loading the files)
  283. InputAssemblies = cmd.OtherAguments;
  284. }
  285. private void Usage()
  286. {
  287. Console.WriteLine(@"IL Repack - assembly merging using Mono.Cecil 0.9.4.0 - Version " + typeof(ILRepack).Assembly.GetName().Version.ToString(2));
  288. Console.WriteLine(@"Syntax: ILRepack.exe [options] /out:<path> <path_to_primary> [<other_assemblies> ...]");
  289. Console.WriteLine(@" - /help displays this usage");
  290. Console.WriteLine(@" - /keyfile:<path> specifies a keyfile to sign the output assembly");
  291. Console.WriteLine(@" - /log:<logfile> enable logging (to a file, if given) (default is disabled)");
  292. Console.WriteLine(@" - /ver:M.X.Y.Z target assembly version");
  293. Console.WriteLine(@" - /union merges types with identical names into one");
  294. Console.WriteLine(@" - /ndebug disables symbol file generation");
  295. Console.WriteLine(@" - /copyattrs copy assembly attributes (by default only the primary assembly attributes are copied)");
  296. Console.WriteLine(@" - /attr:<path> take assembly attributes from the given assembly file");
  297. Console.WriteLine(@" - /allowMultiple when copyattrs is specified, allows multiple attributes (if type allows)");
  298. Console.WriteLine(@" - /target:kind specify target assembly kind (library, exe, winexe supported, default is same as first assembly)");
  299. Console.WriteLine(@" - /targetplatform:P specify target platform (v1, v1.1, v2, v4 supported)");
  300. Console.WriteLine(@" - /xmldocs merges XML documentation as well");
  301. Console.WriteLine(@" - /lib:<path> adds the path to the search directories for referenced assemblies (can be specified multiple times)");
  302. Console.WriteLine(@" - /internalize sets all types but the ones from the first assembly 'internal'");
  303. Console.WriteLine(@" - /delaysign sets the key, but don't sign the assembly");
  304. Console.WriteLine(@" - /noRepackRes do not add the resource 'ILRepack.List with all merged assembly names");
  305. Console.WriteLine(@" - /usefullpublickeyforreferences - NOT IMPLEMENTED");
  306. Console.WriteLine(@" - /align - NOT IMPLEMENTED");
  307. Console.WriteLine(@" - /closed - NOT IMPLEMENTED");
  308. Console.WriteLine(@" - /allowdup:Type allows the specified type for being duplicated in input assemblies");
  309. Console.WriteLine(@" - /allowduplicateresources allows to duplicate resources in output assembly (by default they're ignored)");
  310. Console.WriteLine(@" - /zeropekind allows assemblies with Zero PeKind (but obviously only IL will get merged)");
  311. Console.WriteLine(@" - /wildcards allows (and resolves) file wildcards (e.g. *.dll) in input assemblies");
  312. Console.WriteLine(@" - /parallel use as many CPUs as possible to merge the assemblies");
  313. Console.WriteLine(@" - /pause pause execution once completed (good for debugging)");
  314. Console.WriteLine(@" - /verbose shows more logs");
  315. Console.WriteLine(@" - /out:<path> target assembly path, symbol/config/doc files will be written here as well");
  316. Console.WriteLine(@" - <path_to_primary> primary assembly, gives the name, version to the merged one");
  317. Console.WriteLine(@" - <other_assemblies> ...");
  318. Console.WriteLine(@"");
  319. Console.WriteLine(@"Note: for compatibility purposes, all options are case insensitive, and can be specified using '/', '-' or '--' prefix.");
  320. }
  321. private void ReadInputAssemblies()
  322. {
  323. MergedAssemblyFiles = InputAssemblies.SelectMany(x => ResolveFile(x)).ToList();
  324. OtherAssemblies = new List<AssemblyDefinition>();
  325. // TODO: this could be parallelized to gain speed
  326. bool mergedDebugInfo = false;
  327. foreach (string assembly in MergedAssemblyFiles)
  328. {
  329. INFO("Adding assembly for merge: " + assembly);
  330. try
  331. {
  332. ReaderParameters rp = new ReaderParameters(ReadingMode.Immediate);
  333. // read PDB/MDB?
  334. if (DebugInfo && (File.Exists(Path.ChangeExtension(assembly, "pdb")) || File.Exists(Path.ChangeExtension(assembly, "mdb"))))
  335. {
  336. rp.ReadSymbols = true;
  337. }
  338. AssemblyDefinition mergeAsm;
  339. try
  340. {
  341. mergeAsm = AssemblyDefinition.ReadAssembly(assembly, rp);
  342. }
  343. catch
  344. {
  345. // cope with invalid symbol file
  346. if (rp.ReadSymbols)
  347. {
  348. rp.ReadSymbols = false;
  349. mergeAsm = AssemblyDefinition.ReadAssembly(assembly, rp);
  350. INFO("Failed to load debug information for " + assembly);
  351. }
  352. else
  353. {
  354. throw;
  355. }
  356. }
  357. if (!AllowZeroPeKind && (mergeAsm.MainModule.Attributes & ModuleAttributes.ILOnly) == 0)
  358. throw new ArgumentException("Failed to load assembly with Zero PeKind: " + assembly);
  359. if (rp.ReadSymbols)
  360. mergedDebugInfo = true;
  361. if (PrimaryAssemblyDefinition == null)
  362. PrimaryAssemblyDefinition = mergeAsm;
  363. else
  364. OtherAssemblies.Add(mergeAsm);
  365. }
  366. catch
  367. {
  368. ERROR("Failed to load assembly " + assembly);
  369. throw;
  370. }
  371. }
  372. // prevent writing PDB if we haven't read any
  373. DebugInfo = mergedDebugInfo;
  374. MergedAssemblies = new List<AssemblyDefinition>(OtherAssemblies);
  375. MergedAssemblies.Add(PrimaryAssemblyDefinition);
  376. }
  377. private void ReadInputAssembliesParallel()
  378. {
  379. INFO("Reading in Parallel");
  380. MergedAssemblyFiles = InputAssemblies.SelectMany(x => ResolveFile(x)).ToList();
  381. // TODO: this could be parallelized to gain speed
  382. bool mergedDebugInfo = false;
  383. AssemblyDefinition[] readAsms = new AssemblyDefinition[MergedAssemblyFiles.Count];
  384. int remain = MergedAssemblyFiles.Count;
  385. EventWaitHandle evt = new ManualResetEvent(false);
  386. for (int i = 0; i < MergedAssemblyFiles.Count; i++)
  387. {
  388. int idx = i;
  389. string assembly = MergedAssemblyFiles[idx];
  390. ThreadPool.QueueUserWorkItem((WaitCallback)((_) =>
  391. {
  392. INFO("Adding assembly for merge: " + assembly);
  393. try
  394. {
  395. ReaderParameters rp = new ReaderParameters(ReadingMode.Immediate);
  396. // read PDB/MDB?
  397. if (DebugInfo && (File.Exists(Path.ChangeExtension(assembly, "pdb")) || File.Exists(Path.ChangeExtension(assembly, "mdb"))))
  398. {
  399. rp.ReadSymbols = true;
  400. }
  401. AssemblyDefinition mergeAsm;
  402. try
  403. {
  404. mergeAsm = AssemblyDefinition.ReadAssembly(assembly, rp);
  405. }
  406. catch
  407. {
  408. // cope with invalid symbol file
  409. if (rp.ReadSymbols)
  410. {
  411. rp.ReadSymbols = false;
  412. mergeAsm = AssemblyDefinition.ReadAssembly(assembly, rp);
  413. INFO("Failed to load debug information for " + assembly);
  414. }
  415. else
  416. {
  417. throw;
  418. }
  419. }
  420. if (!AllowZeroPeKind && (mergeAsm.MainModule.Attributes & ModuleAttributes.ILOnly) == 0)
  421. throw new ArgumentException("Failed to load assembly with Zero PeKind: " + assembly);
  422. if (rp.ReadSymbols)
  423. mergedDebugInfo = true;
  424. readAsms[idx] = mergeAsm;
  425. }
  426. catch
  427. {
  428. ERROR("Failed to load assembly " + assembly);
  429. throw;
  430. }
  431. finally
  432. {
  433. if (Interlocked.Decrement(ref remain) == 0)
  434. evt.Set();
  435. }
  436. }));
  437. }
  438. evt.WaitOne();
  439. // prevent writing PDB if we haven't read any
  440. DebugInfo = mergedDebugInfo;
  441. MergedAssemblies = new List<AssemblyDefinition>(readAsms);
  442. PrimaryAssemblyDefinition = readAsms[0];
  443. OtherAssemblies = new List<AssemblyDefinition>(readAsms);
  444. OtherAssemblies.RemoveAt(0);
  445. }
  446. private IEnumerable<string> ResolveFile(string s)
  447. {
  448. if (!AllowWildCards || s.IndexOfAny(new []{'*', '?'}) == -1)
  449. return new []{s};
  450. if (Path.GetDirectoryName(s).IndexOfAny(new[] { '*', '?' }) != -1)
  451. throw new Exception("Invalid path: " + s);
  452. string dir = Path.GetDirectoryName(s);
  453. if(String.IsNullOrEmpty(dir)) dir = Directory.GetCurrentDirectory();
  454. return Directory.GetFiles(Path.GetFullPath(dir), Path.GetFileName(s));
  455. }
  456. public enum Kind
  457. {
  458. Dll,
  459. Exe,
  460. WinExe,
  461. SameAsPrimaryAssembly
  462. }
  463. /// <summary>
  464. /// Parse contents of properties: central point for checking (set on assembly or through command-line).
  465. /// </summary>
  466. private void ParseProperties()
  467. {
  468. if (string.IsNullOrEmpty(OutputFile))
  469. {
  470. throw new ArgumentException("No output file given.");
  471. }
  472. if ((InputAssemblies == null) || (InputAssemblies.Length == 0))
  473. {
  474. throw new ArgumentException("No input files given.");
  475. }
  476. if ((KeyFile != null) && !File.Exists(KeyFile))
  477. {
  478. throw new ArgumentException("KeyFile does not exist: \"" + KeyFile + "\".");
  479. }
  480. if (Internalize && !string.IsNullOrEmpty(ExcludeFile))
  481. {
  482. string[] lines = File.ReadAllLines(ExcludeFile);
  483. excludeInternalizeMatches = new List<System.Text.RegularExpressions.Regex>(lines.Length);
  484. foreach (string line in lines)
  485. excludeInternalizeMatches.Add(new System.Text.RegularExpressions.Regex(line));
  486. }
  487. }
  488. protected TargetRuntime ParseTargetPlatform()
  489. {
  490. TargetRuntime runtime = PrimaryAssemblyMainModule.Runtime;
  491. if (TargetPlatformVersion != null)
  492. {
  493. switch (TargetPlatformVersion)
  494. {
  495. case "v1": runtime = TargetRuntime.Net_1_0; break;
  496. case "v1.1": runtime = TargetRuntime.Net_1_1; break;
  497. case "v2": runtime = TargetRuntime.Net_2_0; break;
  498. case "v4": runtime = TargetRuntime.Net_4_0; break;
  499. default: throw new ArgumentException("Invalid TargetPlatformVersion: \"" + TargetPlatformVersion + "\".");
  500. }
  501. platformFixer.ParseTargetPlatformDirectory(runtime, TargetPlatformDirectory);
  502. }
  503. return runtime;
  504. }
  505. /// <summary>
  506. /// Check if a type's FullName matches a Reges to exclude it from internalizing.
  507. /// </summary>
  508. private bool ExcludeInternalizeMatches(string typeFullName)
  509. {
  510. string withSquareBrackets = "[" + typeFullName + "]";
  511. foreach (System.Text.RegularExpressions.Regex r in excludeInternalizeMatches)
  512. if (r.IsMatch(typeFullName) || r.IsMatch(withSquareBrackets))
  513. return true;
  514. return false;
  515. }
  516. /// <summary>
  517. /// The actual repacking process, called by main after parsing arguments.
  518. /// When referencing this assembly, call this after setting the merge properties.
  519. /// </summary>
  520. public void Repack()
  521. {
  522. InitializeLogFile();
  523. ParseProperties();
  524. // Read input assemblies only after all properties are set.
  525. if (Parallel)
  526. ReadInputAssembliesParallel();
  527. else
  528. ReadInputAssemblies();
  529. var asmNames = KeepOtherVersionReferences ?
  530. MergedAssemblies.Select(x => x.FullName) :
  531. MergedAssemblies.Select(x => x.Name.Name);
  532. mergeAsmNames = new HashSet<string>(asmNames);
  533. platformFixer = new PlatformFixer(PrimaryAssemblyMainModule.Runtime);
  534. duplicateHandler = new DuplicateHandler();
  535. bool hadStrongName = PrimaryAssemblyDefinition.Name.HasPublicKey;
  536. ModuleKind kind = PrimaryAssemblyMainModule.Kind;
  537. if (TargetKind.HasValue)
  538. {
  539. switch (TargetKind.Value)
  540. {
  541. case Kind.Dll: kind = ModuleKind.Dll; break;
  542. case Kind.Exe: kind = ModuleKind.Console; break;
  543. case Kind.WinExe: kind = ModuleKind.Windows; break;
  544. }
  545. }
  546. TargetRuntime runtime = ParseTargetPlatform();
  547. // change assembly's name to correspond to the file we create
  548. string mainModuleName = Path.GetFileNameWithoutExtension(OutputFile);
  549. if (TargetAssemblyDefinition == null)
  550. {
  551. AssemblyNameDefinition asmName = Clone(PrimaryAssemblyDefinition.Name);
  552. asmName.Name = mainModuleName;
  553. TargetAssemblyDefinition = AssemblyDefinition.CreateAssembly(asmName, mainModuleName,
  554. new ModuleParameters()
  555. {
  556. Kind = kind,
  557. Architecture = PrimaryAssemblyMainModule.Architecture,
  558. Runtime = runtime
  559. });
  560. TargetAssemblyMainModule.ImportWin32Resources(PrimaryAssemblyMainModule);
  561. }
  562. else
  563. {
  564. // TODO: does this work or is there more to do?
  565. TargetAssemblyMainModule.Kind = kind;
  566. TargetAssemblyMainModule.Runtime = runtime;
  567. TargetAssemblyDefinition.Name.Name = mainModuleName;
  568. TargetAssemblyMainModule.Name = mainModuleName;
  569. }
  570. if (Version != null)
  571. TargetAssemblyDefinition.Name.Version = Version;
  572. // TODO: Win32 version/icon properties seem not to be copied... limitation in cecil 0.9x?
  573. System.Reflection.StrongNameKeyPair snkp = null;
  574. if (KeyFile != null && File.Exists(KeyFile))
  575. {
  576. using (var stream = new FileStream(KeyFile, FileMode.Open))
  577. {
  578. snkp = new System.Reflection.StrongNameKeyPair(stream);
  579. }
  580. TargetAssemblyDefinition.Name.PublicKey = snkp.PublicKey;
  581. TargetAssemblyDefinition.Name.Attributes |= AssemblyAttributes.PublicKey;
  582. TargetAssemblyMainModule.Attributes |= ModuleAttributes.StrongNameSigned;
  583. }
  584. else
  585. {
  586. TargetAssemblyDefinition.Name.PublicKey = null;
  587. TargetAssemblyMainModule.Attributes &= ~ModuleAttributes.StrongNameSigned;
  588. }
  589. INFO("Processing references");
  590. // Add all AssemblyReferences to merged assembly (probably not necessary)
  591. foreach (var z in MergedAssemblies.SelectMany(x => x.Modules).SelectMany(x => x.AssemblyReferences))
  592. {
  593. string name = z.Name;
  594. if (!MergedAssemblies.Any(y => y.Name.Name == name) &&
  595. TargetAssemblyDefinition.Name.Name != name &&
  596. !TargetAssemblyMainModule.AssemblyReferences.Any(y => y.Name == name))
  597. {
  598. // TODO: fix .NET runtime references?
  599. // - to target a specific runtime version or
  600. // - to target a single version if merged assemblies target different versions
  601. VERBOSE("- add reference " + z);
  602. AssemblyNameReference fixedRef = platformFixer.FixPlatformVersion(z);
  603. TargetAssemblyMainModule.AssemblyReferences.Add(fixedRef);
  604. }
  605. }
  606. // add all module references (pinvoke dlls)
  607. foreach (var z in MergedAssemblies.SelectMany(x => x.Modules).SelectMany(x => x.ModuleReferences))
  608. {
  609. string name = z.Name;
  610. if (!TargetAssemblyMainModule.ModuleReferences.Any(y => y.Name == name))
  611. {
  612. TargetAssemblyMainModule.ModuleReferences.Add(z);
  613. }
  614. }
  615. INFO("Processing resources");
  616. // merge resources
  617. List<string> repackList = null;
  618. EmbeddedResource repackListRes = null;
  619. if (!NoRepackRes)
  620. {
  621. repackList = MergedAssemblies.Select(a => a.FullName).ToList();
  622. repackListRes = GetRepackListResource(repackList);
  623. TargetAssemblyMainModule.Resources.Add(repackListRes);
  624. }
  625. foreach (var r in MergedAssemblies.SelectMany(x => x.Modules).SelectMany(x => x.Resources))
  626. {
  627. if (!NoRepackRes && r.Name == "ILRepack.List" && r is EmbeddedResource)
  628. {
  629. // Merge r into repackList
  630. string[] others = (string[])new BinaryFormatter().Deserialize(((EmbeddedResource)r).GetResourceStream());
  631. repackList = repackList.Union(others).ToList();
  632. EmbeddedResource repackListRes2 = GetRepackListResource(repackList);
  633. TargetAssemblyMainModule.Resources.Remove(repackListRes);
  634. TargetAssemblyMainModule.Resources.Add(repackListRes2);
  635. repackListRes = repackListRes2;
  636. }
  637. else
  638. {
  639. if (!AllowDuplicateResources && TargetAssemblyMainModule.Resources.Any(x => x.Name == r.Name))
  640. {
  641. // Not much we can do about 'ikvm__META-INF!MANIFEST.MF'
  642. // TODO: but might have to merge 'ikvm.exports'
  643. VERBOSE("- Ignoring duplicate resource " + r.Name);
  644. }
  645. else
  646. {
  647. VERBOSE("- Importing " + r.Name);
  648. TargetAssemblyMainModule.Resources.Add(r);
  649. }
  650. }
  651. }
  652. INFO("Processing types");
  653. // merge types, this differs between 'primary' and 'other' assemblies regarding internalizing
  654. foreach (var r in PrimaryAssemblyDefinition.Modules.SelectMany(x => x.Types))
  655. {
  656. VERBOSE("- Importing " + r);
  657. Import(r, TargetAssemblyMainModule.Types, false);
  658. }
  659. ReferenceFixator fixator = new ReferenceFixator(this, duplicateHandler);
  660. foreach (var m in OtherAssemblies.SelectMany(x => x.Modules))
  661. {
  662. duplicateHandler.Reset();
  663. List<TypeDefinition> importedTypes = new List<TypeDefinition>();
  664. foreach (var r in m.Types)
  665. {
  666. VERBOSE("- Importing " + r);
  667. bool internalize = Internalize;
  668. if (excludeInternalizeMatches != null)
  669. internalize = !ExcludeInternalizeMatches(r.FullName);
  670. importedTypes.Add(Import(r, TargetAssemblyMainModule.Types, internalize));
  671. }
  672. foreach (var t in importedTypes)
  673. {
  674. fixator.FixReferences(t);
  675. }
  676. }
  677. if (CopyAttributes)
  678. {
  679. foreach (var ass in MergedAssemblies)
  680. {
  681. CopyCustomAttributes(ass.CustomAttributes, TargetAssemblyDefinition.CustomAttributes, AllowMultipleAssemblyLevelAttributes, null);
  682. }
  683. foreach (var mod in MergedAssemblies.SelectMany(x => x.Modules))
  684. {
  685. CopyCustomAttributes(mod.CustomAttributes, TargetAssemblyMainModule.CustomAttributes, AllowMultipleAssemblyLevelAttributes, null);
  686. }
  687. }
  688. else if (AttributeFile != null)
  689. {
  690. AssemblyDefinition attributeAsm = AssemblyDefinition.ReadAssembly(AttributeFile);
  691. CopyCustomAttributes(attributeAsm.CustomAttributes, TargetAssemblyDefinition.CustomAttributes, null);
  692. CopyCustomAttributes(attributeAsm.CustomAttributes, TargetAssemblyMainModule.CustomAttributes, null);
  693. // TODO: should copy Win32 resources, too
  694. }
  695. else
  696. {
  697. CopyCustomAttributes(PrimaryAssemblyDefinition.CustomAttributes, TargetAssemblyDefinition.CustomAttributes, null);
  698. CopyCustomAttributes(PrimaryAssemblyMainModule.CustomAttributes, TargetAssemblyMainModule.CustomAttributes, null);
  699. // TODO: should copy Win32 resources, too
  700. }
  701. CopySecurityDeclarations(PrimaryAssemblyDefinition.SecurityDeclarations, TargetAssemblyDefinition.SecurityDeclarations, null);
  702. if (PrimaryAssemblyMainModule.EntryPoint != null)
  703. {
  704. TargetAssemblyMainModule.EntryPoint = fixator.Fix(Import(PrimaryAssemblyDefinition.EntryPoint), null).Resolve();
  705. }
  706. INFO("Fixing references");
  707. // this step travels through all TypeRefs & replaces them by matching TypeDefs
  708. foreach (var r in TargetAssemblyMainModule.Types)
  709. {
  710. fixator.FixReferences(r);
  711. }
  712. fixator.FixReferences(TargetAssemblyDefinition.CustomAttributes, null);
  713. fixator.FixReferences(TargetAssemblyDefinition.SecurityDeclarations, null);
  714. fixator.FixReferences(TargetAssemblyMainModule.CustomAttributes, null);
  715. fixator.FixReferences(TargetAssemblyMainModule.Resources);
  716. // final reference cleanup (Cecil Import automatically added them)
  717. foreach (AssemblyDefinition asm in MergedAssemblies)
  718. {
  719. foreach (var refer in TargetAssemblyMainModule.AssemblyReferences.ToArray())
  720. {
  721. // remove all referenced assemblies with same same, as we didn't bother on the version when merging
  722. // in case we reference same assemblies with different versions, there might be prior errors if we don't merge the 'largest one'
  723. if (KeepOtherVersionReferences ? refer.FullName == asm.FullName : refer.Name == asm.Name.Name)
  724. {
  725. TargetAssemblyMainModule.AssemblyReferences.Remove(refer);
  726. }
  727. }
  728. }
  729. INFO("Writing output assembly to disk");
  730. var parameters = new WriterParameters();
  731. if ((snkp != null) && !DelaySign)
  732. parameters.StrongNameKeyPair = snkp;
  733. // write PDB/MDB?
  734. if (DebugInfo)
  735. parameters.WriteSymbols = true;
  736. TargetAssemblyDefinition.Write(OutputFile, parameters);
  737. if (hadStrongName && !TargetAssemblyDefinition.Name.HasPublicKey)
  738. StrongNameLost = true;
  739. // nice to have, merge .config (assembly configuration file) & .xml (assembly documentation)
  740. ConfigMerger.Process(this);
  741. if(XmlDocumentation)
  742. DocumentationMerger.Process(this);
  743. // TODO: we're done here, the code below is only test code which can be removed once it's all running fine
  744. // 'verify' generated assembly
  745. AssemblyDefinition asm2 = AssemblyDefinition.ReadAssembly(OutputFile, new ReaderParameters(ReadingMode.Immediate));
  746. // lazy match on the name (not full) to catch requirements about merging different versions
  747. bool failed = false;
  748. foreach (var a in asm2.MainModule.AssemblyReferences.Where(x => MergedAssemblies.Any(y => KeepOtherVersionReferences ? x.FullName == y.FullName : x.Name == y.Name.Name)))
  749. {
  750. // failed
  751. ERROR("Merged assembly still references " + a.FullName);
  752. failed = true;
  753. }
  754. if (failed)
  755. throw new Exception("Merging failed, see above errors");
  756. }
  757. private EmbeddedResource GetRepackListResource(List<string> repackList)
  758. {
  759. repackList.Sort();
  760. EmbeddedResource repackListRes;
  761. var stream = new MemoryStream();
  762. new BinaryFormatter().Serialize(stream, repackList.ToArray());
  763. stream.Seek(0, 0);
  764. repackListRes = new EmbeddedResource("ILRepack.List", ManifestResourceAttributes.Public, stream);
  765. return repackListRes;
  766. }
  767. // Real stuff below //
  768. // These methods are somehow a merge between the clone methods of Cecil 0.6 and the import ones of 0.9
  769. // They use Cecil's MetaDataImporter to rebase imported stuff into the new assembly, but then another pass is required
  770. // to clean the TypeRefs Cecil keeps around (although the generated IL would be kind-o valid without, whatever 'valid' means)
  771. private AssemblyNameDefinition Clone(AssemblyNameDefinition assemblyName)
  772. {
  773. AssemblyNameDefinition asmName = new AssemblyNameDefinition(assemblyName.Name, assemblyName.Version);
  774. asmName.Attributes = assemblyName.Attributes;
  775. asmName.Culture = assemblyName.Culture;
  776. asmName.Hash = assemblyName.Hash;
  777. asmName.HashAlgorithm = assemblyName.HashAlgorithm;
  778. asmName.PublicKey = assemblyName.PublicKey;
  779. asmName.PublicKeyToken = assemblyName.PublicKeyToken;
  780. return asmName;
  781. }
  782. /// <summary>
  783. /// Clones a field to a newly created type
  784. /// </summary>
  785. private void CloneTo(FieldDefinition field, TypeDefinition nt)
  786. {
  787. if (nt.Fields.Any(x => x.Name == field.Name))
  788. {
  789. IGNOREDUP("field", field);
  790. return;
  791. }
  792. FieldDefinition nf = new FieldDefinition(field.Name, field.Attributes, Import(field.FieldType, nt));
  793. nt.Fields.Add(nf);
  794. if (field.HasConstant)
  795. nf.Constant = field.Constant;
  796. if (field.HasMarshalInfo)
  797. nf.MarshalInfo = field.MarshalInfo;
  798. if (field.InitialValue != null && field.InitialValue.Length > 0)
  799. nf.InitialValue = field.InitialValue;
  800. if (field.HasLayoutInfo)
  801. nf.Offset = field.Offset;
  802. CopyCustomAttributes(field.CustomAttributes, nf.CustomAttributes, nt);
  803. }
  804. /// <summary>
  805. /// Clones a parameter into a newly created method
  806. /// </summary>
  807. private void CloneTo(ParameterDefinition param, MethodDefinition context, Collection<ParameterDefinition> col)
  808. {
  809. ParameterDefinition pd = new ParameterDefinition(param.Name, param.Attributes, Import(param.ParameterType, context));
  810. if (param.HasMarshalInfo)
  811. pd.MarshalInfo = param.MarshalInfo;
  812. if (param.HasCustomAttributes)
  813. CopyCustomAttributes(param.CustomAttributes, pd.CustomAttributes, context);
  814. col.Add(pd);
  815. }
  816. private CustomAttributeArgument Copy(CustomAttributeArgument arg, IGenericParameterProvider context)
  817. {
  818. return new CustomAttributeArgument(Import(arg.Type, context), arg.Value);
  819. }
  820. private CustomAttributeNamedArgument Copy(CustomAttributeNamedArgument namedArg, IGenericParameterProvider context)
  821. {
  822. return new CustomAttributeNamedArgument(namedArg.Name, Copy(namedArg.Argument, context));
  823. }
  824. /// <summary>
  825. /// Clones a collection of SecurityDeclarations
  826. /// </summary>
  827. private void CopySecurityDeclarations(Collection<SecurityDeclaration> input, Collection<SecurityDeclaration> output, IGenericParameterProvider context)
  828. {
  829. foreach (SecurityDeclaration sec in input)
  830. {
  831. SecurityDeclaration newSec;
  832. if (PermissionsetHelper.IsXmlPermissionSet(sec))
  833. {
  834. newSec = PermissionsetHelper.Xml2PermissionSet(sec, TargetAssemblyMainModule);
  835. }
  836. else
  837. {
  838. newSec = new SecurityDeclaration(sec.Action);
  839. foreach (SecurityAttribute sa in sec.SecurityAttributes)
  840. {
  841. SecurityAttribute newSa = new SecurityAttribute(Import(sa.AttributeType, context));
  842. if (sa.HasFields)
  843. {
  844. foreach (CustomAttributeNamedArgument cana in sa.Fields)
  845. {
  846. newSa.Fields.Add(Copy(cana, context));
  847. }
  848. }
  849. if (sa.HasProperties)
  850. {
  851. foreach (CustomAttributeNamedArgument cana in sa.Properties)
  852. {
  853. newSa.Properties.Add(Copy(cana, context));
  854. }
  855. }
  856. newSec.SecurityAttributes.Add(newSa);
  857. }
  858. }
  859. output.Add(newSec);
  860. }
  861. }
  862. // helper
  863. private static void Copy<T>(Collection<T> input, Collection<T> output, Action<T, T> action)
  864. {
  865. if (input.Count != output.Count)
  866. throw new InvalidOperationException();
  867. for (int i = 0; i < input.Count; i++)
  868. {
  869. action.Invoke(input[i], output[i]);
  870. }
  871. }
  872. private void CopyGenericParameters(Collection<GenericParameter> input, Collection<GenericParameter> output, IGenericParameterProvider nt)
  873. {
  874. foreach (GenericParameter gp in input)
  875. {
  876. GenericParameter ngp = new GenericParameter(gp.Name, nt);
  877. ngp.Attributes = gp.Attributes;
  878. output.Add(ngp);
  879. }
  880. // delay copy to ensure all generics parameters are already present
  881. Copy(input, output, (gp, ngp) => CopyTypeReferences(gp.Constraints, ngp.Constraints, nt));
  882. Copy(input, output, (gp, ngp) => CopyCustomAttributes(gp.CustomAttributes, ngp.CustomAttributes, nt));
  883. }
  884. private void CloneTo(EventDefinition evt, TypeDefinition nt, Collection<EventDefinition> col)
  885. {
  886. // ignore duplicate event
  887. if (nt.Events.Any(x => x.Name == evt.Name))
  888. {
  889. IGNOREDUP("event", evt);
  890. return;
  891. }
  892. EventDefinition ed = new EventDefinition(evt.Name, evt.Attributes, Import(evt.EventType, nt));
  893. col.Add(ed);
  894. if (evt.AddMethod != null)
  895. ed.AddMethod = FindMethodInNewType(nt, evt.AddMethod);
  896. if (evt.RemoveMethod != null)
  897. ed.RemoveMethod = FindMethodInNewType(nt, evt.RemoveMethod);
  898. if (evt.InvokeMethod != null)
  899. ed.InvokeMethod = FindMethodInNewType(nt, evt.InvokeMethod);
  900. if (evt.HasOtherMethods)
  901. {
  902. // TODO
  903. throw new InvalidOperationException();
  904. }
  905. CopyCustomAttributes(evt.CustomAttributes, ed.CustomAttributes, nt);
  906. }
  907. private MethodDefinition FindMethodInNewType(TypeDefinition nt, MethodDefinition methodDefinition)
  908. {
  909. var methFullName = methodDefinition.FullName;
  910. var meths = nt.FullName == methodDefinition.DeclaringType.FullName ?
  911. nt.Methods.Where(x => x.FullName == methFullName) :
  912. nt.Methods.Where(x => x.Name == methodDefinition.Name); // for renames (could check parameters as well)
  913. var ret = meths.FirstOrDefault();
  914. if (ret == null)
  915. {
  916. WARN("Method '" + methFullName + "' not found in merged type '" + nt.FullName + "'");
  917. }
  918. return ret;
  919. }
  920. private void CopyCustomAttributes(Collection<CustomAttribute> input, Collection<CustomAttribute> output, IGenericParameterProvider context)
  921. {
  922. CopyCustomAttributes(input, output, true, context);
  923. }
  924. private CustomAttribute Copy(CustomAttribute ca, IGenericParameterProvider context)
  925. {
  926. CustomAttribute newCa = new CustomAttribute(Import(ca.Constructor));
  927. foreach (var arg in ca.ConstructorArguments)
  928. newCa.ConstructorArguments.Add(Copy(arg, context));
  929. foreach (var arg in ca.Fields)
  930. newCa.Fields.Add(Copy(arg, context));
  931. foreach (var arg in ca.Properties)
  932. newCa.Properties.Add(Copy(arg, context));
  933. return newCa;
  934. }
  935. private void CopyCustomAttributes(Collection<CustomAttribute> input, Collection<CustomAttribute> output, bool allowMultiple, IGenericParameterProvider context)
  936. {
  937. foreach (CustomAttribute ca in input)
  938. {
  939. var caType = ca.AttributeType;
  940. if (!output.Any(attr => ReflectionHelper.AreSame(attr.AttributeType, caType)) ||
  941. (allowMultiple && CustomAttributeTypeAllowsMultiple(caType)))
  942. {
  943. output.Add(Copy(ca, context));
  944. }
  945. }
  946. }
  947. private bool CustomAttributeTypeAllowsMultiple(TypeReference type)
  948. {
  949. if (type.FullName == "IKVM.Attributes.JavaModuleAttribute" || type.FullName == "IKVM.Attributes.PackageListAttribute")
  950. {
  951. // IKVM module attributes, although they don't allow multiple, IKVM supports the attribute being specified multiple times
  952. return true;
  953. }
  954. TypeDefinition typeDef = type.Resolve();
  955. if (typeDef != null)
  956. {
  957. var ca = typeDef.CustomAttributes.FirstOrDefault(x => x.AttributeType.FullName == "System.AttributeUsageAttribute");
  958. if (ca != null)
  959. {
  960. var prop = ca.Properties.FirstOrDefault(y => y.Name == "AllowMultiple");
  961. if (prop.Argument.Value is bool)
  962. {
  963. return (bool)prop.Argument.Value;
  964. }
  965. }
  966. }
  967. // default is false
  968. return false;
  969. }
  970. private void CopyTypeReferences(Collection<TypeReference> input, Collection<TypeReference> output, IGenericParameterProvider context)
  971. {
  972. foreach (TypeReference ta in input)
  973. {
  974. output.Add(Import(ta, context));
  975. }
  976. }
  977. public bool IsMerged(TypeReference reference)
  978. {
  979. var refer = reference.Scope as AssemblyNameReference;
  980. // don't fix reference to an unmerged type (even if a merged one exists with same name)
  981. if (refer == null)
  982. return false;
  983. return KeepOtherVersionReferences ?
  984. mergeAsmNames.Contains(refer.FullName) :
  985. mergeAsmNames.Contains(refer.Name);
  986. }
  987. private TypeReference Import(TypeReference reference, IGenericParameterProvider context)
  988. {
  989. reference = duplicateHandler.Rename(reference);
  990. if (IsMerged(reference))
  991. {
  992. // first a shortcut, avoids fixing references afterwards (but completely optional)
  993. TypeDefinition type = TargetAssemblyMainModule.GetType(reference.FullName);
  994. if (type != null)
  995. return type;
  996. }
  997. reference = platformFixer.FixPlatformVersion(reference);
  998. if (context is MethodReference)
  999. return TargetAssemblyMainModule.Import(reference, (MethodReference)context);
  1000. else if (context is TypeReference)
  1001. return TargetAssemblyMainModule.Import(reference, (TypeReference)context);
  1002. else if (context == null)
  1003. {
  1004. // we come here when importing types used for assembly-level custom attributes
  1005. return TargetAssemblyMainModule.Import(reference);
  1006. }
  1007. throw new InvalidOperationException();
  1008. }
  1009. private FieldReference Import(FieldReference reference, IGenericParameterProvider context)
  1010. {
  1011. FieldReference importReference = platformFixer.FixPlatformVersion(reference);
  1012. if (context is MethodReference)
  1013. return TargetAssemblyMainModule.Import(importReference, (MethodReference)context);
  1014. if (context is TypeReference)
  1015. return TargetAssemblyMainModule.Import(importReference, (TypeReference)context);
  1016. throw new InvalidOperationException();
  1017. }
  1018. private MethodReference Import(MethodReference reference)
  1019. {
  1020. MethodReference importReference = platformFixer.FixPlatformVersion(reference);
  1021. return TargetAssemblyMainModule.Import(importReference);
  1022. }
  1023. private MethodReference Import(MethodReference reference, IGenericParameterProvider context)
  1024. {
  1025. // If this is a Method/TypeDefinition, it will be corrected to a definition again later
  1026. MethodReference importReference = platformFixer.FixPlatformVersion(reference);
  1027. if (context is MethodReference)
  1028. return TargetAssemblyMainModule.Import(importReference, (MethodReference)context);
  1029. if (context is TypeReference)
  1030. return TargetAssemblyMainModule.Import(importReference, (TypeReference)context);
  1031. throw new InvalidOperationException();
  1032. }
  1033. private void CloneTo(PropertyDefinition prop, TypeDefinition nt, Collection<PropertyDefinition> col)
  1034. {
  1035. // ignore duplicate property
  1036. if (nt.Properties.Any(x => x.Name == prop.Name))
  1037. {
  1038. IGNOREDUP("property", prop);
  1039. return;
  1040. }
  1041. PropertyDefinition pd = new PropertyDefinition(prop.Name, prop.Attributes, Import(prop.PropertyType, nt));
  1042. col.Add(pd);
  1043. if (prop.SetMethod != null)
  1044. pd.SetMethod = FindMethodInNewType(nt, prop.SetMethod);
  1045. if (prop.GetMethod != null)
  1046. pd.GetMethod = FindMethodInNewType(nt, prop.GetMethod);
  1047. if (prop.HasOtherMethods)
  1048. {
  1049. // TODO
  1050. throw new NotSupportedException("Property has other methods");
  1051. }
  1052. CopyCustomAttributes(prop.CustomAttributes, pd.CustomAttributes, nt);
  1053. }
  1054. private void CloneTo(MethodDefinition meth, TypeDefinition type, bool typeJustCreated)
  1055. {
  1056. // ignore duplicate method
  1057. if (!typeJustCreated &&
  1058. type.Methods.Count > 0 &&
  1059. type.Methods.Any(x =>
  1060. (x.Name == meth.Name) &&
  1061. (x.Parameters.Count == meth.Parameters.Count) &&
  1062. (x.ToString() == meth.ToString()))) // TODO: better/faster comparation of parameter types?
  1063. {
  1064. IGNOREDUP("method", meth);
  1065. return;
  1066. }
  1067. // use void placeholder as we'll do the return type import later on (after generic parameters)
  1068. MethodDefinition nm = new MethodDefinition(meth.Name, meth.Attributes, TargetAssemblyMainModule.TypeSystem.Void);
  1069. nm.ImplAttributes = meth.ImplAttributes;
  1070. type.Methods.Add(nm);
  1071. CopyGenericParameters(meth.GenericParameters, nm.GenericParameters, nm);
  1072. if (meth.HasPInvokeInfo)
  1073. {
  1074. nm.PInvokeInfo = new PInvokeInfo(meth.PInvokeInfo.Attributes, meth.PInvokeInfo.EntryPoint, meth.PInvokeInfo.Module);
  1075. }
  1076. foreach (ParameterDefinition param in meth.Parameters)
  1077. CloneTo(param, nm, nm.Parameters);
  1078. foreach (MethodReference ov in meth.Overrides)
  1079. nm.Overrides.Add(Import(ov, nm));
  1080. CopySecurityDeclarations(meth.SecurityDeclarations, nm.SecurityDeclarations, nm);
  1081. CopyCustomAttributes(meth.CustomAttributes, nm.CustomAttributes, nm);
  1082. nm.ReturnType = Import(meth.ReturnType, nm);
  1083. if (meth.HasBody)
  1084. CloneTo(meth.Body, nm);
  1085. nm.IsAddOn = meth.IsAddOn;
  1086. nm.IsRemoveOn = meth.IsRemoveOn;
  1087. nm.IsGetter = meth.IsGetter;
  1088. nm.IsSetter = meth.IsSetter;
  1089. nm.CallingConvention = meth.CallingConvention;
  1090. }
  1091. private void CloneTo(MethodBody body, MethodDefinition parent)
  1092. {
  1093. MethodBody nb = new MethodBody(parent);
  1094. parent.Body = nb;
  1095. nb.MaxStackSize = body.MaxStackSize;
  1096. nb.InitLocals = body.InitLocals;
  1097. nb.LocalVarToken = body.LocalVarToken;
  1098. foreach (VariableDefinition var in body.Variables)
  1099. nb.Variables.Add(new VariableDefinition(var.Name,
  1100. Import(var.VariableType, parent)));
  1101. foreach (Instruction instr in body.Instructions)
  1102. {
  1103. Instruction ni;
  1104. if (instr.OpCode.Code == Code.Calli)
  1105. {
  1106. ni = Instruction.Create(instr.OpCode, (CallSite)instr.Operand);
  1107. }
  1108. else switch (instr.OpCode.OperandType)
  1109. {
  1110. case OperandType.InlineArg:
  1111. case OperandType.ShortInlineArg:
  1112. if (instr.Operand == body.ThisParameter)
  1113. {
  1114. ni = Instruction.Create(instr.OpCode, nb.ThisParameter);
  1115. }
  1116. else
  1117. {
  1118. int param = body.Method.Parameters.IndexOf((ParameterDefinition)instr.Operand);
  1119. ni = Instruction.Create(instr.OpCode, parent.Parameters[param]);
  1120. }
  1121. break;
  1122. case OperandType.InlineVar:
  1123. case OperandType.ShortInlineVar:
  1124. int var = body.Variables.IndexOf((VariableDefinition)instr.Operand);
  1125. ni = Instruction.Create(instr.OpCode, nb.Variables[var]);
  1126. break;
  1127. case OperandType.InlineField:
  1128. ni = Instruction.Create(instr.OpCode, Import((FieldReference)instr.Operand, parent));
  1129. break;
  1130. case OperandType.InlineMethod:
  1131. ni = Instruction.Create(instr.OpCode, Import((MethodReference)instr.Operand, parent));
  1132. break;
  1133. case OperandType.InlineType:
  1134. ni = Instruction.Create(instr.OpCode, Import((TypeReference)instr.Operand, parent));
  1135. break;
  1136. case OperandType.InlineTok:
  1137. if (instr.Operand is TypeReference)
  1138. ni = Instruction.Create(instr.OpCode, Import((TypeReference)instr.Operand, parent));
  1139. else if (instr.Operand is FieldReference)
  1140. ni = Instruction.Create(instr.OpCode, Import((FieldReference)instr.Operand, parent));
  1141. else if (instr.Operand is MethodReference)
  1142. ni = Instruction.Create(instr.OpCode, Import((MethodReference)instr.Operand, parent));
  1143. else
  1144. throw new InvalidOperationException();
  1145. break;
  1146. case OperandType.ShortInlineBrTarget:
  1147. case OperandType.InlineBrTarget:
  1148. ni = Instruction.Create(instr.OpCode, (Instruction)instr.Operand); // TODO review
  1149. break;
  1150. case OperandType.InlineSwitch:
  1151. ni = Instruction.Create(instr.OpCode, (Instruction[])instr.Operand); // TODO review
  1152. break;
  1153. case OperandType.InlineR:
  1154. ni = Instruction.Create(instr.OpCode, (double)instr.Operand);
  1155. break;
  1156. case OperandType.ShortInlineR:
  1157. ni = Instruction.Create(instr.OpCode, (float)instr.Operand);
  1158. break;
  1159. case OperandType.InlineNone:
  1160. ni = Instruction.Create(instr.OpCode);
  1161. break;
  1162. case OperandType.InlineString:
  1163. ni = Instruction.Create(instr.OpCode, (string)instr.Operand);
  1164. break;
  1165. case OperandType.ShortInlineI:
  1166. if (instr.OpCode == OpCodes.Ldc_I4_S)
  1167. ni = Instruction.Create(instr.OpCode, (sbyte)instr.Operand);
  1168. else
  1169. ni = Instruction.Create(instr.OpCode, (byte)instr.Operand);
  1170. break;
  1171. case OperandType.InlineI8:
  1172. ni = Instruction.Create(instr.OpCode, (long)instr.Operand);
  1173. break;
  1174. case OperandType.InlineI:
  1175. ni = Instruction.Create(instr.OpCode, (int)instr.Operand);
  1176. break;
  1177. default:
  1178. throw new InvalidOperationException();
  1179. }
  1180. ni.SequencePoint = instr.SequencePoint;
  1181. nb.Instructions.Add(ni);
  1182. }
  1183. for (int i = 0; i < body.Instructions.Count; i++)
  1184. {
  1185. Instruction instr = nb.Instructions[i];
  1186. if (instr.OpCode.OperandType != OperandType.ShortInlineBrTarget &&
  1187. instr.OpCode.OperandType != OperandType.InlineBrTarget)
  1188. continue;
  1189. instr.Operand = GetInstruction(body, nb, (Instruction)body.Instructions[i].Operand);
  1190. }
  1191. foreach (ExceptionHandler eh in body.ExceptionHandlers)
  1192. {
  1193. ExceptionHandler neh = new ExceptionHandler(eh.HandlerType);
  1194. neh.TryStart = GetInstruction(body, nb, eh.TryStart);
  1195. neh.TryEnd = GetInstruction(body, nb, eh.TryEnd);
  1196. neh.HandlerStart = GetInstruction(body, nb, eh.HandlerStart);
  1197. neh.HandlerEnd = GetInstruction(body, nb, eh.HandlerEnd);
  1198. switch (eh.HandlerType)
  1199. {
  1200. case ExceptionHandlerType.Catch:
  1201. neh.CatchType = Import(eh.CatchType, parent);
  1202. break;
  1203. case ExceptionHandlerType.Filter:
  1204. neh.FilterStart = GetInstruction(body, nb, eh.FilterStart);
  1205. break;
  1206. }
  1207. nb.ExceptionHandlers.Add(neh);
  1208. }
  1209. }
  1210. internal static Instruction GetInstruction(MethodBody oldBody, MethodBody newBody, Instruction i)
  1211. {
  1212. int pos = oldBody.Instructions.IndexOf(i);
  1213. if (pos > -1 && pos < newBody.Instructions.Count)
  1214. return newBody.Instructions[pos];
  1215. return null /*newBody.Instructions.Outside*/;
  1216. }
  1217. internal TypeDefinition Import(TypeDefinition type, Collection<TypeDefinition> col, bool internalize)
  1218. {
  1219. TypeDefinition nt = TargetAssemblyMainModule.GetType(type.FullName);
  1220. bool justCreatedType = false;
  1221. if (nt == null)
  1222. {
  1223. nt = CreateType(type, col, internalize, null);
  1224. justCreatedType = true;
  1225. }
  1226. else if (type.FullName != "<Module>" && !type.IsPublic)
  1227. {
  1228. // rename it
  1229. string other = duplicateHandler.Get(type.FullName, type.Name);
  1230. INFO("Renaming " + type.FullName + " into " + other);
  1231. nt = CreateType(type, col, internalize, other);
  1232. justCreatedType = true;
  1233. }
  1234. else if (UnionMerge || DuplicateTypeAllowed(type.FullName))
  1235. {
  1236. INFO("Merging " + type);
  1237. }
  1238. else
  1239. {
  1240. ERROR("Duplicate type " + type);
  1241. throw new InvalidOperationException("Duplicate type " + type);
  1242. }
  1243. // nested types first (are never internalized)
  1244. foreach (TypeDefinition nested in type.NestedTypes)
  1245. Import(nested, nt.NestedTypes, false);
  1246. foreach (FieldDefinition field in type.Fields)
  1247. CloneTo(field, nt);
  1248. // methods before fields / events
  1249. foreach (MethodDefinition meth in type.Methods)
  1250. CloneTo(meth, nt, justCreatedType);
  1251. foreach (EventDefinition evt in type.Events)
  1252. CloneTo(evt, nt, nt.Events);
  1253. foreach (PropertyDefinition prop in type.Properties)
  1254. CloneTo(prop, nt, nt.Properties);
  1255. return nt;
  1256. }
  1257. private TypeDefinition CreateType(TypeDefinition type, Collection<TypeDefinition> col, bool internalize, string rename)
  1258. {
  1259. TypeDefinition nt = new TypeDefinition(type.Namespace, rename ?? type.Name, type.Attributes);
  1260. col.Add(nt);
  1261. // only top-level types are internalized
  1262. if (internalize && (nt.DeclaringType == null) && nt.IsPublic)
  1263. nt.IsPublic = false;
  1264. CopyGenericParameters(type.GenericParameters, nt.GenericParameters, nt);
  1265. if (type.BaseType != null)
  1266. nt.BaseType = Import(type.BaseType, nt);
  1267. if (type.HasLayoutInfo)
  1268. {
  1269. nt.ClassSize = type.ClassSize;
  1270. nt.PackingSize = type.PackingSize;
  1271. }
  1272. // don't copy these twice if UnionMerge==true
  1273. // TODO: we can move this down if we chek for duplicates when adding
  1274. CopySecurityDeclarations(type.SecurityDeclarations, nt.SecurityDeclarations, nt);
  1275. CopyTypeReferences(type.Interfaces, nt.Interfaces, nt);
  1276. CopyCustomAttributes(type.CustomAttributes, nt.CustomAttributes, nt);
  1277. return nt;
  1278. }
  1279. private bool DuplicateTypeAllowed(string fullName)
  1280. {
  1281. // Merging module because IKVM uses this class to store some fields.
  1282. // Doesn't fully work yet, as IKVM is nice enough to give all the fields the same name...
  1283. if (fullName == "<Module>")
  1284. return true;
  1285. // Merge should be OK since member's names are pretty unique,
  1286. // but renaming duplicate members would be safer...
  1287. if (fullName == "<PrivateImplementationDetails>")
  1288. return true;
  1289. if (allowedDuplicateTypes.Contains(fullName))
  1290. return true;
  1291. return false;
  1292. }
  1293. }
  1294. }