2 Copyright (C) 2008-2012 Jeroen Frijters
4 This software is provided 'as-is', without any express or implied
5 warranty. In no event will the authors be held liable for any damages
6 arising from the use of this software.
8 Permission is granted to anyone to use this software for any purpose,
9 including commercial applications, and to alter it and redistribute it
10 freely, subject to the following restrictions:
12 1. The origin of this software must not be misrepresented; you must not
13 claim that you wrote the original software. If you use this software
14 in a product, an acknowledgment in the product documentation would be
15 appreciated but is not required.
16 2. Altered source versions must be plainly marked as such, and must not be
17 misrepresented as being the original software.
18 3. This notice may not be removed or altered from any source distribution.
25 using System.Collections.Generic;
26 using System.Configuration.Assemblies;
28 using System.Diagnostics;
29 using System.Globalization;
30 using System.Security.Cryptography;
31 using System.Security;
32 using IKVM.Reflection.Metadata;
33 using IKVM.Reflection.Impl;
34 using IKVM.Reflection.Writer;
36 namespace IKVM.Reflection.Emit
38 public sealed class AssemblyBuilder : Assembly
40 private readonly string name;
41 private ushort majorVersion;
42 private ushort minorVersion;
43 private ushort buildVersion;
44 private ushort revisionVersion;
45 private string culture;
46 private AssemblyNameFlags flags;
47 private AssemblyHashAlgorithm hashAlgorithm;
48 private StrongNameKeyPair keyPair;
49 private byte[] publicKey;
50 internal readonly string dir;
51 private readonly PermissionSet requiredPermissions;
52 private readonly PermissionSet optionalPermissions;
53 private readonly PermissionSet refusedPermissions;
54 private PEFileKinds fileKind = PEFileKinds.Dll;
55 private MethodInfo entryPoint;
56 private VersionInfo versionInfo;
57 private byte[] win32icon;
58 private byte[] win32manifest;
59 private byte[] win32resources;
60 private string imageRuntimeVersion;
61 internal int mdStreamVersion = 0x20000;
62 private Module pseudoManifestModule;
63 private readonly List<ResourceFile> resourceFiles = new List<ResourceFile>();
64 private readonly List<ModuleBuilder> modules = new List<ModuleBuilder>();
65 private readonly List<Module> addedModules = new List<Module>();
66 private readonly List<CustomAttributeBuilder> customAttributes = new List<CustomAttributeBuilder>();
67 private readonly List<CustomAttributeBuilder> declarativeSecurity = new List<CustomAttributeBuilder>();
68 private readonly List<Type> typeForwarders = new List<Type>();
70 private struct ResourceFile
73 internal string FileName;
74 internal ResourceAttributes Attributes;
77 internal AssemblyBuilder(Universe universe, AssemblyName name, string dir, PermissionSet requiredPermissions, PermissionSet optionalPermissions, PermissionSet refusedPermissions)
80 this.name = name.Name;
81 SetVersionHelper(name.Version);
82 if (!string.IsNullOrEmpty(name.Culture))
84 this.culture = name.Culture;
86 this.flags = name.RawFlags;
87 this.hashAlgorithm = name.HashAlgorithm;
88 if (this.hashAlgorithm == AssemblyHashAlgorithm.None)
90 this.hashAlgorithm = AssemblyHashAlgorithm.SHA1;
92 this.keyPair = name.KeyPair;
93 if (this.keyPair != null)
95 this.publicKey = this.keyPair.PublicKey;
99 byte[] publicKey = name.GetPublicKey();
100 if (publicKey != null && publicKey.Length != 0)
102 this.publicKey = (byte[])publicKey.Clone();
105 this.dir = dir ?? ".";
106 this.requiredPermissions = requiredPermissions;
107 this.optionalPermissions = optionalPermissions;
108 this.refusedPermissions = refusedPermissions;
109 if (universe.HasMscorlib && !universe.Mscorlib.__IsMissing && universe.Mscorlib.ImageRuntimeVersion != null)
111 this.imageRuntimeVersion = universe.Mscorlib.ImageRuntimeVersion;
115 this.imageRuntimeVersion = typeof(object).Assembly.ImageRuntimeVersion;
119 private void SetVersionHelper(Version version)
130 majorVersion = (ushort)version.Major;
131 minorVersion = (ushort)version.Minor;
132 buildVersion = version.Build == -1 ? (ushort)0 : (ushort)version.Build;
133 revisionVersion = version.Revision == -1 ? (ushort)0 : (ushort)version.Revision;
137 private void Rename(AssemblyName oldName)
139 this.fullName = null;
140 universe.RenameAssembly(this, oldName);
143 public void __SetAssemblyVersion(Version version)
145 AssemblyName oldName = GetName();
146 SetVersionHelper(version);
150 public void __SetAssemblyCulture(string cultureName)
152 AssemblyName oldName = GetName();
153 this.culture = cultureName;
157 public void __SetAssemblyKeyPair(StrongNameKeyPair keyPair)
159 AssemblyName oldName = GetName();
160 this.keyPair = keyPair;
163 this.publicKey = keyPair.PublicKey;
168 // this is used in combination with delay signing
169 public void __SetAssemblyPublicKey(byte[] publicKey)
171 AssemblyName oldName = GetName();
172 this.publicKey = publicKey == null ? null : (byte[])publicKey.Clone();
176 public void __SetAssemblyAlgorithmId(AssemblyHashAlgorithm hashAlgorithm)
178 this.hashAlgorithm = hashAlgorithm;
181 public void __SetAssemblyFlags(AssemblyNameFlags flags)
183 AssemblyName oldName = GetName();
188 public override AssemblyNameFlags __AssemblyFlags
190 get { return flags; }
198 public override AssemblyName GetName()
200 AssemblyName n = new AssemblyName();
202 n.Version = new Version(majorVersion, minorVersion, buildVersion, revisionVersion);
203 n.Culture = culture ?? "";
204 n.HashAlgorithm = hashAlgorithm;
206 n.SetPublicKey(publicKey != null ? (byte[])publicKey.Clone() : Empty<byte>.Array);
211 public override string Location
213 get { throw new NotSupportedException(); }
216 public ModuleBuilder DefineDynamicModule(string name, string fileName)
218 return DefineDynamicModule(name, fileName, false);
221 public ModuleBuilder DefineDynamicModule(string name, string fileName, bool emitSymbolInfo)
223 ModuleBuilder module = new ModuleBuilder(this, name, fileName, emitSymbolInfo);
228 public ModuleBuilder GetDynamicModule(string name)
230 foreach (ModuleBuilder module in modules)
232 if (module.Name == name)
240 public void SetCustomAttribute(ConstructorInfo con, byte[] binaryAttribute)
242 SetCustomAttribute(new CustomAttributeBuilder(con, binaryAttribute));
245 public void SetCustomAttribute(CustomAttributeBuilder customBuilder)
247 customAttributes.Add(customBuilder);
250 public void __AddDeclarativeSecurity(CustomAttributeBuilder customBuilder)
252 declarativeSecurity.Add(customBuilder);
255 public void __AddTypeForwarder(Type type)
257 typeForwarders.Add(type);
260 public void SetEntryPoint(MethodInfo entryMethod)
262 SetEntryPoint(entryMethod, PEFileKinds.ConsoleApplication);
265 public void SetEntryPoint(MethodInfo entryMethod, PEFileKinds fileKind)
267 this.entryPoint = entryMethod;
268 this.fileKind = fileKind;
271 public void __Save(Stream stream, PortableExecutableKinds portableExecutableKind, ImageFileMachine imageFileMachine)
273 if (!stream.CanRead || !stream.CanWrite || !stream.CanSeek || stream.Position != 0)
275 throw new ArgumentException("Stream must support read/write/seek and current position must be zero.", "stream");
277 if (modules.Count != 1)
279 throw new NotSupportedException("Saving to a stream is only supported for single module assemblies.");
281 SaveImpl(modules[0].fileName, stream, portableExecutableKind, imageFileMachine);
284 public void Save(string assemblyFileName)
286 Save(assemblyFileName, PortableExecutableKinds.ILOnly, ImageFileMachine.I386);
289 public void Save(string assemblyFileName, PortableExecutableKinds portableExecutableKind, ImageFileMachine imageFileMachine)
291 SaveImpl(assemblyFileName, null, portableExecutableKind, imageFileMachine);
294 private void SaveImpl(string assemblyFileName, Stream streamOrNull, PortableExecutableKinds portableExecutableKind, ImageFileMachine imageFileMachine)
296 ModuleBuilder manifestModule = null;
298 foreach (ModuleBuilder moduleBuilder in modules)
300 moduleBuilder.PopulatePropertyAndEventTables();
302 if (manifestModule == null
303 && string.Compare(moduleBuilder.fileName, assemblyFileName, StringComparison.OrdinalIgnoreCase) == 0)
305 manifestModule = moduleBuilder;
309 if (manifestModule == null)
311 manifestModule = DefineDynamicModule("RefEmit_OnDiskManifestModule", assemblyFileName, false);
314 AssemblyTable.Record assemblyRecord = new AssemblyTable.Record();
315 assemblyRecord.HashAlgId = (int)hashAlgorithm;
316 assemblyRecord.Name = manifestModule.Strings.Add(name);
317 assemblyRecord.MajorVersion = majorVersion;
318 assemblyRecord.MinorVersion = minorVersion;
319 assemblyRecord.BuildNumber = buildVersion;
320 assemblyRecord.RevisionNumber = revisionVersion;
321 if (publicKey != null)
323 assemblyRecord.PublicKey = manifestModule.Blobs.Add(ByteBuffer.Wrap(publicKey));
324 assemblyRecord.Flags = (int)(flags | AssemblyNameFlags.PublicKey);
328 assemblyRecord.Flags = (int)(flags & ~AssemblyNameFlags.PublicKey);
332 assemblyRecord.Culture = manifestModule.Strings.Add(culture);
334 int token = 0x20000000 + manifestModule.AssemblyTable.AddRecord(assemblyRecord);
336 #pragma warning disable 618
337 // this values are obsolete, but we already know that so we disable the warning
338 System.Security.Permissions.SecurityAction requestMinimum = System.Security.Permissions.SecurityAction.RequestMinimum;
339 System.Security.Permissions.SecurityAction requestOptional = System.Security.Permissions.SecurityAction.RequestOptional;
340 System.Security.Permissions.SecurityAction requestRefuse = System.Security.Permissions.SecurityAction.RequestRefuse;
341 #pragma warning restore 618
342 if (requiredPermissions != null)
344 manifestModule.AddDeclarativeSecurity(token, requestMinimum, requiredPermissions);
346 if (optionalPermissions != null)
348 manifestModule.AddDeclarativeSecurity(token, requestOptional, optionalPermissions);
350 if (refusedPermissions != null)
352 manifestModule.AddDeclarativeSecurity(token, requestRefuse, refusedPermissions);
355 ResourceSection unmanagedResources = versionInfo != null || win32icon != null || win32manifest != null || win32resources != null
356 ? new ResourceSection()
359 if (versionInfo != null)
361 versionInfo.SetName(GetName());
362 versionInfo.SetFileName(assemblyFileName);
363 foreach (CustomAttributeBuilder cab in customAttributes)
365 // .NET doesn't support copying blob custom attributes into the version info
368 versionInfo.SetAttribute(cab);
371 ByteBuffer versionInfoData = new ByteBuffer(512);
372 versionInfo.Write(versionInfoData);
373 unmanagedResources.AddVersionInfo(versionInfoData);
376 if (win32icon != null)
378 unmanagedResources.AddIcon(win32icon);
381 if (win32manifest != null)
383 unmanagedResources.AddManifest(win32manifest, fileKind == PEFileKinds.Dll ? (ushort)2 : (ushort)1);
386 if (win32resources != null)
388 unmanagedResources.ExtractResources(win32resources);
391 foreach (CustomAttributeBuilder cab in customAttributes)
393 // we intentionally don't filter out the version info (pseudo) custom attributes (to be compatible with .NET)
394 manifestModule.SetCustomAttribute(0x20000001, cab);
397 manifestModule.AddDeclarativeSecurity(0x20000001, declarativeSecurity);
399 foreach (Type type in typeForwarders)
401 manifestModule.AddTypeForwarder(type);
404 foreach (ResourceFile resfile in resourceFiles)
406 int fileToken = AddFile(manifestModule, resfile.FileName, 1 /*ContainsNoMetaData*/);
407 ManifestResourceTable.Record rec = new ManifestResourceTable.Record();
409 rec.Flags = (int)resfile.Attributes;
410 rec.Name = manifestModule.Strings.Add(resfile.Name);
411 rec.Implementation = fileToken;
412 manifestModule.ManifestResource.AddRecord(rec);
415 int entryPointToken = 0;
417 foreach (ModuleBuilder moduleBuilder in modules)
419 moduleBuilder.FillAssemblyRefTable();
420 if (moduleBuilder != manifestModule)
423 if (entryPoint != null && entryPoint.Module == moduleBuilder)
425 ModuleWriter.WriteModule(null, null, moduleBuilder, fileKind, portableExecutableKind, imageFileMachine, moduleBuilder.unmanagedResources, entryPoint.MetadataToken);
426 entryPointToken = fileToken = AddFile(manifestModule, moduleBuilder.fileName, 0 /*ContainsMetaData*/);
430 ModuleWriter.WriteModule(null, null, moduleBuilder, fileKind, portableExecutableKind, imageFileMachine, moduleBuilder.unmanagedResources, 0);
431 fileToken = AddFile(manifestModule, moduleBuilder.fileName, 0 /*ContainsMetaData*/);
433 moduleBuilder.ExportTypes(fileToken, manifestModule);
437 foreach (Module module in addedModules)
439 int fileToken = AddFile(manifestModule, module.FullyQualifiedName, 0 /*ContainsMetaData*/);
440 module.ExportTypes(fileToken, manifestModule);
443 if (entryPointToken == 0 && entryPoint != null)
445 entryPointToken = entryPoint.MetadataToken;
448 // finally, write the manifest module
449 ModuleWriter.WriteModule(keyPair, publicKey, manifestModule, fileKind, portableExecutableKind, imageFileMachine, unmanagedResources ?? manifestModule.unmanagedResources, entryPointToken, streamOrNull);
452 private int AddFile(ModuleBuilder manifestModule, string fileName, int flags)
454 SHA1Managed hash = new SHA1Managed();
455 string fullPath = fileName;
458 fullPath = Path.Combine(dir, fileName);
460 using (FileStream fs = new FileStream(fullPath, FileMode.Open, FileAccess.Read))
462 using (CryptoStream cs = new CryptoStream(Stream.Null, hash, CryptoStreamMode.Write))
464 byte[] buf = new byte[8192];
465 ModuleWriter.HashChunk(fs, cs, buf, (int)fs.Length);
468 return manifestModule.__AddModule(flags, Path.GetFileName(fileName), hash.Hash);
471 public void AddResourceFile(string name, string fileName)
473 AddResourceFile(name, fileName, ResourceAttributes.Public);
476 public void AddResourceFile(string name, string fileName, ResourceAttributes attribs)
478 ResourceFile resfile = new ResourceFile();
480 resfile.FileName = fileName;
481 resfile.Attributes = attribs;
482 resourceFiles.Add(resfile);
485 public void DefineVersionInfoResource()
487 if (versionInfo != null || win32resources != null)
489 throw new ArgumentException("Native resource has already been defined.");
491 versionInfo = new VersionInfo();
494 public void DefineVersionInfoResource(string product, string productVersion, string company, string copyright, string trademark)
496 if (versionInfo != null || win32resources != null)
498 throw new ArgumentException("Native resource has already been defined.");
500 versionInfo = new VersionInfo();
501 versionInfo.product = product;
502 versionInfo.informationalVersion = productVersion;
503 versionInfo.company = company;
504 versionInfo.copyright = copyright;
505 versionInfo.trademark = trademark;
508 public void __DefineIconResource(byte[] iconFile)
510 if (win32icon != null || win32resources != null)
512 throw new ArgumentException("Native resource has already been defined.");
514 win32icon = (byte[])iconFile.Clone();
517 public void __DefineManifestResource(byte[] manifest)
519 if (win32manifest != null || win32resources != null)
521 throw new ArgumentException("Native resource has already been defined.");
523 win32manifest = (byte[])manifest.Clone();
526 public void __DefineUnmanagedResource(byte[] resource)
528 if (versionInfo != null || win32icon != null || win32manifest != null || win32resources != null)
530 throw new ArgumentException("Native resource has already been defined.");
532 // The standard .NET DefineUnmanagedResource(byte[]) is useless, because it embeds "resource" (as-is) as the .rsrc section,
533 // but it doesn't set the PE file Resource Directory entry to point to it. That's why we have a renamed version, which behaves
534 // like DefineUnmanagedResource(string).
535 win32resources = (byte[])resource.Clone();
538 public void DefineUnmanagedResource(string resourceFileName)
540 // This method reads the specified resource file (Win32 .res file) and converts it into the appropriate format and embeds it in the .rsrc section,
541 // also setting the Resource Directory entry.
542 __DefineUnmanagedResource(File.ReadAllBytes(resourceFileName));
545 public override Type[] GetTypes()
547 List<Type> list = new List<Type>();
548 foreach (ModuleBuilder module in modules)
550 module.GetTypesImpl(list);
552 foreach (Module module in addedModules)
554 module.GetTypesImpl(list);
556 return list.ToArray();
559 internal override Type FindType(TypeName typeName)
561 foreach (ModuleBuilder mb in modules)
563 Type type = mb.FindType(typeName);
569 foreach (Module module in addedModules)
571 Type type = module.FindType(typeName);
580 internal override Type FindTypeIgnoreCase(TypeName lowerCaseName)
582 foreach (ModuleBuilder mb in modules)
584 Type type = mb.FindTypeIgnoreCase(lowerCaseName);
590 foreach (Module module in addedModules)
592 Type type = module.FindTypeIgnoreCase(lowerCaseName);
601 public override string ImageRuntimeVersion
603 get { return imageRuntimeVersion; }
606 public void __SetImageRuntimeVersion(string imageRuntimeVersion, int mdStreamVersion)
608 this.imageRuntimeVersion = imageRuntimeVersion;
609 this.mdStreamVersion = mdStreamVersion;
612 public override Module ManifestModule
616 if (pseudoManifestModule == null)
618 pseudoManifestModule = new ManifestModule(this);
620 return pseudoManifestModule;
624 public override MethodInfo EntryPoint
626 get { return entryPoint; }
629 public override AssemblyName[] GetReferencedAssemblies()
631 return Empty<AssemblyName>.Array;
634 public override Module[] GetLoadedModules(bool getResourceModules)
636 return GetModules(getResourceModules);
639 public override Module[] GetModules(bool getResourceModules)
641 List<Module> list = new List<Module>();
642 foreach (ModuleBuilder module in modules)
644 if (getResourceModules || !module.IsResource())
649 foreach (Module module in addedModules)
651 if (getResourceModules || !module.IsResource())
656 return list.ToArray();
659 public override Module GetModule(string name)
661 foreach (ModuleBuilder module in modules)
663 if (module.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase))
668 foreach (Module module in addedModules)
670 if (module.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase))
678 public Module __AddModule(RawModule module)
680 Module mod = module.ToModule(this);
681 addedModules.Add(mod);
685 public override ManifestResourceInfo GetManifestResourceInfo(string resourceName)
687 throw new NotSupportedException();
690 public override string[] GetManifestResourceNames()
692 throw new NotSupportedException();
695 public override Stream GetManifestResourceStream(string resourceName)
697 throw new NotSupportedException();
700 internal override IList<CustomAttributeData> GetCustomAttributesData(Type attributeType)
702 List<CustomAttributeData> list = new List<CustomAttributeData>();
703 foreach (CustomAttributeBuilder cab in customAttributes)
705 if (attributeType == null || attributeType.IsAssignableFrom(cab.Constructor.DeclaringType))
707 list.Add(cab.ToData(this));
714 sealed class ManifestModule : NonPEModule
716 private readonly AssemblyBuilder assembly;
717 private readonly Guid guid = Guid.NewGuid();
719 internal ManifestModule(AssemblyBuilder assembly)
720 : base(assembly.universe)
722 this.assembly = assembly;
725 public override int MDStreamVersion
727 get { return assembly.mdStreamVersion; }
730 public override Assembly Assembly
732 get { return assembly; }
735 internal override Type FindType(TypeName typeName)
740 internal override Type FindTypeIgnoreCase(TypeName lowerCaseName)
745 internal override void GetTypesImpl(List<Type> list)
749 public override string FullyQualifiedName
751 get { return Path.Combine(assembly.dir, "RefEmit_InMemoryManifestModule"); }
754 public override string Name
756 get { return "<In Memory Module>"; }
759 public override Guid ModuleVersionId
764 public override string ScopeName
766 get { return "RefEmit_InMemoryManifestModule"; }
769 protected override Exception NotSupportedException()
771 return new InvalidOperationException();