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.Resources;
31 using System.Security.Cryptography;
32 using System.Security;
33 using IKVM.Reflection.Metadata;
34 using IKVM.Reflection.Impl;
35 using IKVM.Reflection.Writer;
37 namespace IKVM.Reflection.Emit
39 public sealed class AssemblyBuilder : Assembly
41 private readonly string name;
42 private ushort majorVersion;
43 private ushort minorVersion;
44 private ushort buildVersion;
45 private ushort revisionVersion;
46 private string culture;
47 private AssemblyNameFlags flags;
48 private AssemblyHashAlgorithm hashAlgorithm;
49 private StrongNameKeyPair keyPair;
50 private byte[] publicKey;
51 internal readonly string dir;
52 private readonly PermissionSet requiredPermissions;
53 private readonly PermissionSet optionalPermissions;
54 private readonly PermissionSet refusedPermissions;
55 private PEFileKinds fileKind = PEFileKinds.Dll;
56 private MethodInfo entryPoint;
57 private VersionInfo versionInfo;
58 private byte[] win32icon;
59 private byte[] win32manifest;
60 private byte[] win32resources;
61 private string imageRuntimeVersion;
62 internal int mdStreamVersion = 0x20000;
63 private Module pseudoManifestModule;
64 private readonly List<ResourceFile> resourceFiles = new List<ResourceFile>();
65 private readonly List<ModuleBuilder> modules = new List<ModuleBuilder>();
66 private readonly List<Module> addedModules = new List<Module>();
67 private readonly List<CustomAttributeBuilder> customAttributes = new List<CustomAttributeBuilder>();
68 private readonly List<CustomAttributeBuilder> declarativeSecurity = new List<CustomAttributeBuilder>();
69 private readonly List<Type> typeForwarders = new List<Type>();
71 private struct ResourceFile
74 internal string FileName;
75 internal ResourceAttributes Attributes;
76 internal ResourceWriter Writer;
79 internal AssemblyBuilder(Universe universe, AssemblyName name, string dir, PermissionSet requiredPermissions, PermissionSet optionalPermissions, PermissionSet refusedPermissions)
82 this.name = name.Name;
83 SetVersionHelper(name.Version);
84 if (!string.IsNullOrEmpty(name.Culture))
86 this.culture = name.Culture;
88 this.flags = name.RawFlags;
89 this.hashAlgorithm = name.HashAlgorithm;
90 if (this.hashAlgorithm == AssemblyHashAlgorithm.None)
92 this.hashAlgorithm = AssemblyHashAlgorithm.SHA1;
94 this.keyPair = name.KeyPair;
95 if (this.keyPair != null)
97 this.publicKey = this.keyPair.PublicKey;
101 byte[] publicKey = name.GetPublicKey();
102 if (publicKey != null && publicKey.Length != 0)
104 this.publicKey = (byte[])publicKey.Clone();
107 this.dir = dir ?? ".";
108 this.requiredPermissions = requiredPermissions;
109 this.optionalPermissions = optionalPermissions;
110 this.refusedPermissions = refusedPermissions;
111 if (universe.HasMscorlib && !universe.Mscorlib.__IsMissing && universe.Mscorlib.ImageRuntimeVersion != null)
113 this.imageRuntimeVersion = universe.Mscorlib.ImageRuntimeVersion;
117 this.imageRuntimeVersion = typeof(object).Assembly.ImageRuntimeVersion;
121 private void SetVersionHelper(Version version)
132 majorVersion = (ushort)version.Major;
133 minorVersion = (ushort)version.Minor;
134 buildVersion = version.Build == -1 ? (ushort)0 : (ushort)version.Build;
135 revisionVersion = version.Revision == -1 ? (ushort)0 : (ushort)version.Revision;
139 private void Rename(AssemblyName oldName)
141 this.fullName = null;
142 universe.RenameAssembly(this, oldName);
145 public void __SetAssemblyVersion(Version version)
147 AssemblyName oldName = GetName();
148 SetVersionHelper(version);
152 public void __SetAssemblyCulture(string cultureName)
154 AssemblyName oldName = GetName();
155 this.culture = cultureName;
159 public void __SetAssemblyKeyPair(StrongNameKeyPair keyPair)
161 AssemblyName oldName = GetName();
162 this.keyPair = keyPair;
165 this.publicKey = keyPair.PublicKey;
170 // this is used in combination with delay signing
171 public void __SetAssemblyPublicKey(byte[] publicKey)
173 AssemblyName oldName = GetName();
174 this.publicKey = publicKey == null ? null : (byte[])publicKey.Clone();
178 public void __SetAssemblyAlgorithmId(AssemblyHashAlgorithm hashAlgorithm)
180 this.hashAlgorithm = hashAlgorithm;
183 public void __SetAssemblyFlags(AssemblyNameFlags flags)
185 AssemblyName oldName = GetName();
190 public override AssemblyNameFlags __AssemblyFlags
192 get { return flags; }
200 public override AssemblyName GetName()
202 AssemblyName n = new AssemblyName();
204 n.Version = new Version(majorVersion, minorVersion, buildVersion, revisionVersion);
205 n.Culture = culture ?? "";
206 n.HashAlgorithm = hashAlgorithm;
208 n.SetPublicKey(publicKey != null ? (byte[])publicKey.Clone() : Empty<byte>.Array);
213 public override string Location
215 get { throw new NotSupportedException(); }
218 public ModuleBuilder DefineDynamicModule(string name, string fileName)
220 return DefineDynamicModule(name, fileName, false);
223 public ModuleBuilder DefineDynamicModule(string name, string fileName, bool emitSymbolInfo)
225 ModuleBuilder module = new ModuleBuilder(this, name, fileName, emitSymbolInfo);
230 public ModuleBuilder GetDynamicModule(string name)
232 foreach (ModuleBuilder module in modules)
234 if (module.Name == name)
242 public void SetCustomAttribute(ConstructorInfo con, byte[] binaryAttribute)
244 SetCustomAttribute(new CustomAttributeBuilder(con, binaryAttribute));
247 public void SetCustomAttribute(CustomAttributeBuilder customBuilder)
249 customAttributes.Add(customBuilder);
252 public void __AddDeclarativeSecurity(CustomAttributeBuilder customBuilder)
254 declarativeSecurity.Add(customBuilder);
257 public void __AddTypeForwarder(Type type)
259 typeForwarders.Add(type);
262 public void SetEntryPoint(MethodInfo entryMethod)
264 SetEntryPoint(entryMethod, PEFileKinds.ConsoleApplication);
267 public void SetEntryPoint(MethodInfo entryMethod, PEFileKinds fileKind)
269 this.entryPoint = entryMethod;
270 this.fileKind = fileKind;
273 public void __Save(Stream stream, PortableExecutableKinds portableExecutableKind, ImageFileMachine imageFileMachine)
275 if (!stream.CanRead || !stream.CanWrite || !stream.CanSeek || stream.Position != 0)
277 throw new ArgumentException("Stream must support read/write/seek and current position must be zero.", "stream");
279 if (modules.Count != 1)
281 throw new NotSupportedException("Saving to a stream is only supported for single module assemblies.");
283 SaveImpl(modules[0].fileName, stream, portableExecutableKind, imageFileMachine);
286 public void Save(string assemblyFileName)
288 Save(assemblyFileName, PortableExecutableKinds.ILOnly, ImageFileMachine.I386);
291 public void Save(string assemblyFileName, PortableExecutableKinds portableExecutableKind, ImageFileMachine imageFileMachine)
293 SaveImpl(assemblyFileName, null, portableExecutableKind, imageFileMachine);
296 private void SaveImpl(string assemblyFileName, Stream streamOrNull, PortableExecutableKinds portableExecutableKind, ImageFileMachine imageFileMachine)
298 ModuleBuilder manifestModule = null;
300 foreach (ModuleBuilder moduleBuilder in modules)
302 moduleBuilder.SetIsSaved();
303 moduleBuilder.PopulatePropertyAndEventTables();
305 if (manifestModule == null
306 && string.Compare(moduleBuilder.fileName, assemblyFileName, StringComparison.OrdinalIgnoreCase) == 0)
308 manifestModule = moduleBuilder;
312 if (manifestModule == null)
314 manifestModule = DefineDynamicModule("RefEmit_OnDiskManifestModule", assemblyFileName, false);
317 AssemblyTable.Record assemblyRecord = new AssemblyTable.Record();
318 assemblyRecord.HashAlgId = (int)hashAlgorithm;
319 assemblyRecord.Name = manifestModule.Strings.Add(name);
320 assemblyRecord.MajorVersion = majorVersion;
321 assemblyRecord.MinorVersion = minorVersion;
322 assemblyRecord.BuildNumber = buildVersion;
323 assemblyRecord.RevisionNumber = revisionVersion;
324 if (publicKey != null)
326 assemblyRecord.PublicKey = manifestModule.Blobs.Add(ByteBuffer.Wrap(publicKey));
327 assemblyRecord.Flags = (int)(flags | AssemblyNameFlags.PublicKey);
331 assemblyRecord.Flags = (int)(flags & ~AssemblyNameFlags.PublicKey);
335 assemblyRecord.Culture = manifestModule.Strings.Add(culture);
337 int token = 0x20000000 + manifestModule.AssemblyTable.AddRecord(assemblyRecord);
339 #pragma warning disable 618
340 // this values are obsolete, but we already know that so we disable the warning
341 System.Security.Permissions.SecurityAction requestMinimum = System.Security.Permissions.SecurityAction.RequestMinimum;
342 System.Security.Permissions.SecurityAction requestOptional = System.Security.Permissions.SecurityAction.RequestOptional;
343 System.Security.Permissions.SecurityAction requestRefuse = System.Security.Permissions.SecurityAction.RequestRefuse;
344 #pragma warning restore 618
345 if (requiredPermissions != null)
347 manifestModule.AddDeclarativeSecurity(token, requestMinimum, requiredPermissions);
349 if (optionalPermissions != null)
351 manifestModule.AddDeclarativeSecurity(token, requestOptional, optionalPermissions);
353 if (refusedPermissions != null)
355 manifestModule.AddDeclarativeSecurity(token, requestRefuse, refusedPermissions);
358 ResourceSection unmanagedResources = versionInfo != null || win32icon != null || win32manifest != null || win32resources != null
359 ? new ResourceSection()
362 if (versionInfo != null)
364 versionInfo.SetName(GetName());
365 versionInfo.SetFileName(assemblyFileName);
366 foreach (CustomAttributeBuilder cab in customAttributes)
368 // .NET doesn't support copying blob custom attributes into the version info
371 versionInfo.SetAttribute(cab);
374 ByteBuffer versionInfoData = new ByteBuffer(512);
375 versionInfo.Write(versionInfoData);
376 unmanagedResources.AddVersionInfo(versionInfoData);
379 if (win32icon != null)
381 unmanagedResources.AddIcon(win32icon);
384 if (win32manifest != null)
386 unmanagedResources.AddManifest(win32manifest, fileKind == PEFileKinds.Dll ? (ushort)2 : (ushort)1);
389 if (win32resources != null)
391 unmanagedResources.ExtractResources(win32resources);
394 foreach (CustomAttributeBuilder cab in customAttributes)
396 // we intentionally don't filter out the version info (pseudo) custom attributes (to be compatible with .NET)
397 manifestModule.SetCustomAttribute(0x20000001, cab);
400 manifestModule.AddDeclarativeSecurity(0x20000001, declarativeSecurity);
402 foreach (Type type in typeForwarders)
404 manifestModule.AddTypeForwarder(type);
407 foreach (ResourceFile resfile in resourceFiles)
409 if (resfile.Writer != null)
411 resfile.Writer.Generate();
412 resfile.Writer.Close();
414 int fileToken = AddFile(manifestModule, resfile.FileName, 1 /*ContainsNoMetaData*/);
415 ManifestResourceTable.Record rec = new ManifestResourceTable.Record();
417 rec.Flags = (int)resfile.Attributes;
418 rec.Name = manifestModule.Strings.Add(resfile.Name);
419 rec.Implementation = fileToken;
420 manifestModule.ManifestResource.AddRecord(rec);
423 int entryPointToken = 0;
425 foreach (ModuleBuilder moduleBuilder in modules)
427 moduleBuilder.FillAssemblyRefTable();
428 moduleBuilder.EmitResources();
429 if (moduleBuilder != manifestModule)
432 if (entryPoint != null && entryPoint.Module == moduleBuilder)
434 ModuleWriter.WriteModule(null, null, moduleBuilder, fileKind, portableExecutableKind, imageFileMachine, moduleBuilder.unmanagedResources, entryPoint.MetadataToken);
435 entryPointToken = fileToken = AddFile(manifestModule, moduleBuilder.fileName, 0 /*ContainsMetaData*/);
439 ModuleWriter.WriteModule(null, null, moduleBuilder, fileKind, portableExecutableKind, imageFileMachine, moduleBuilder.unmanagedResources, 0);
440 fileToken = AddFile(manifestModule, moduleBuilder.fileName, 0 /*ContainsMetaData*/);
442 moduleBuilder.ExportTypes(fileToken, manifestModule);
446 foreach (Module module in addedModules)
448 int fileToken = AddFile(manifestModule, module.FullyQualifiedName, 0 /*ContainsMetaData*/);
449 module.ExportTypes(fileToken, manifestModule);
452 if (entryPointToken == 0 && entryPoint != null)
454 entryPointToken = entryPoint.MetadataToken;
457 // finally, write the manifest module
458 ModuleWriter.WriteModule(keyPair, publicKey, manifestModule, fileKind, portableExecutableKind, imageFileMachine, unmanagedResources ?? manifestModule.unmanagedResources, entryPointToken, streamOrNull);
461 private int AddFile(ModuleBuilder manifestModule, string fileName, int flags)
463 SHA1Managed hash = new SHA1Managed();
464 string fullPath = fileName;
467 fullPath = Path.Combine(dir, fileName);
469 using (FileStream fs = new FileStream(fullPath, FileMode.Open, FileAccess.Read))
471 using (CryptoStream cs = new CryptoStream(Stream.Null, hash, CryptoStreamMode.Write))
473 byte[] buf = new byte[8192];
474 ModuleWriter.HashChunk(fs, cs, buf, (int)fs.Length);
477 return manifestModule.__AddModule(flags, Path.GetFileName(fileName), hash.Hash);
480 public void AddResourceFile(string name, string fileName)
482 AddResourceFile(name, fileName, ResourceAttributes.Public);
485 public void AddResourceFile(string name, string fileName, ResourceAttributes attribs)
487 ResourceFile resfile = new ResourceFile();
489 resfile.FileName = fileName;
490 resfile.Attributes = attribs;
491 resourceFiles.Add(resfile);
494 public IResourceWriter DefineResource(string name, string description, string fileName)
496 return DefineResource(name, description, fileName, ResourceAttributes.Public);
499 public IResourceWriter DefineResource(string name, string description, string fileName, ResourceAttributes attribute)
501 // FXBUG we ignore the description, because there is no such thing
503 string fullPath = fileName;
506 fullPath = Path.Combine(dir, fileName);
508 ResourceWriter rw = new ResourceWriter(fullPath);
509 ResourceFile resfile;
511 resfile.FileName = fileName;
512 resfile.Attributes = attribute;
514 resourceFiles.Add(resfile);
518 public void DefineVersionInfoResource()
520 if (versionInfo != null || win32resources != null)
522 throw new ArgumentException("Native resource has already been defined.");
524 versionInfo = new VersionInfo();
527 public void DefineVersionInfoResource(string product, string productVersion, string company, string copyright, string trademark)
529 if (versionInfo != null || win32resources != null)
531 throw new ArgumentException("Native resource has already been defined.");
533 versionInfo = new VersionInfo();
534 versionInfo.product = product;
535 versionInfo.informationalVersion = productVersion;
536 versionInfo.company = company;
537 versionInfo.copyright = copyright;
538 versionInfo.trademark = trademark;
541 public void __DefineIconResource(byte[] iconFile)
543 if (win32icon != null || win32resources != null)
545 throw new ArgumentException("Native resource has already been defined.");
547 win32icon = (byte[])iconFile.Clone();
550 public void __DefineManifestResource(byte[] manifest)
552 if (win32manifest != null || win32resources != null)
554 throw new ArgumentException("Native resource has already been defined.");
556 win32manifest = (byte[])manifest.Clone();
559 public void __DefineUnmanagedResource(byte[] resource)
561 if (versionInfo != null || win32icon != null || win32manifest != null || win32resources != null)
563 throw new ArgumentException("Native resource has already been defined.");
565 // The standard .NET DefineUnmanagedResource(byte[]) is useless, because it embeds "resource" (as-is) as the .rsrc section,
566 // 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
567 // like DefineUnmanagedResource(string).
568 win32resources = (byte[])resource.Clone();
571 public void DefineUnmanagedResource(string resourceFileName)
573 // This method reads the specified resource file (Win32 .res file) and converts it into the appropriate format and embeds it in the .rsrc section,
574 // also setting the Resource Directory entry.
575 __DefineUnmanagedResource(File.ReadAllBytes(resourceFileName));
578 public override Type[] GetTypes()
580 List<Type> list = new List<Type>();
581 foreach (ModuleBuilder module in modules)
583 module.GetTypesImpl(list);
585 foreach (Module module in addedModules)
587 module.GetTypesImpl(list);
589 return list.ToArray();
592 internal override Type FindType(TypeName typeName)
594 foreach (ModuleBuilder mb in modules)
596 Type type = mb.FindType(typeName);
602 foreach (Module module in addedModules)
604 Type type = module.FindType(typeName);
613 internal override Type FindTypeIgnoreCase(TypeName lowerCaseName)
615 foreach (ModuleBuilder mb in modules)
617 Type type = mb.FindTypeIgnoreCase(lowerCaseName);
623 foreach (Module module in addedModules)
625 Type type = module.FindTypeIgnoreCase(lowerCaseName);
634 public override string ImageRuntimeVersion
636 get { return imageRuntimeVersion; }
639 public void __SetImageRuntimeVersion(string imageRuntimeVersion, int mdStreamVersion)
641 this.imageRuntimeVersion = imageRuntimeVersion;
642 this.mdStreamVersion = mdStreamVersion;
645 public override Module ManifestModule
649 if (pseudoManifestModule == null)
651 pseudoManifestModule = new ManifestModule(this);
653 return pseudoManifestModule;
657 public override MethodInfo EntryPoint
659 get { return entryPoint; }
662 public override AssemblyName[] GetReferencedAssemblies()
664 return Empty<AssemblyName>.Array;
667 public override Module[] GetLoadedModules(bool getResourceModules)
669 return GetModules(getResourceModules);
672 public override Module[] GetModules(bool getResourceModules)
674 List<Module> list = new List<Module>();
675 foreach (ModuleBuilder module in modules)
677 if (getResourceModules || !module.IsResource())
682 foreach (Module module in addedModules)
684 if (getResourceModules || !module.IsResource())
689 return list.ToArray();
692 public override Module GetModule(string name)
694 foreach (ModuleBuilder module in modules)
696 if (module.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase))
701 foreach (Module module in addedModules)
703 if (module.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase))
711 public Module __AddModule(RawModule module)
713 Module mod = module.ToModule(this);
714 addedModules.Add(mod);
718 public override ManifestResourceInfo GetManifestResourceInfo(string resourceName)
720 throw new NotSupportedException();
723 public override string[] GetManifestResourceNames()
725 throw new NotSupportedException();
728 public override Stream GetManifestResourceStream(string resourceName)
730 throw new NotSupportedException();
733 internal override IList<CustomAttributeData> GetCustomAttributesData(Type attributeType)
735 List<CustomAttributeData> list = new List<CustomAttributeData>();
736 foreach (CustomAttributeBuilder cab in customAttributes)
738 if (attributeType == null || attributeType.IsAssignableFrom(cab.Constructor.DeclaringType))
740 list.Add(cab.ToData(this));
746 internal bool IsWindowsRuntime
748 get { return (flags & (AssemblyNameFlags)0x200) != 0; }
752 sealed class ManifestModule : NonPEModule
754 private readonly AssemblyBuilder assembly;
755 private readonly Guid guid = Guid.NewGuid();
757 internal ManifestModule(AssemblyBuilder assembly)
758 : base(assembly.universe)
760 this.assembly = assembly;
763 public override int MDStreamVersion
765 get { return assembly.mdStreamVersion; }
768 public override Assembly Assembly
770 get { return assembly; }
773 internal override Type FindType(TypeName typeName)
778 internal override Type FindTypeIgnoreCase(TypeName lowerCaseName)
783 internal override void GetTypesImpl(List<Type> list)
787 public override string FullyQualifiedName
789 get { return Path.Combine(assembly.dir, "RefEmit_InMemoryManifestModule"); }
792 public override string Name
794 get { return "<In Memory Module>"; }
797 public override Guid ModuleVersionId
802 public override string ScopeName
804 get { return "RefEmit_InMemoryManifestModule"; }
807 protected override Exception NotSupportedException()
809 return new InvalidOperationException();