2 Copyright (C) 2008-2011 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 ResourceSection unmanagedResources;
58 private string imageRuntimeVersion;
59 internal int mdStreamVersion = 0x20000;
60 private Module pseudoManifestModule;
61 private readonly List<ResourceFile> resourceFiles = new List<ResourceFile>();
62 private readonly List<ModuleBuilder> modules = new List<ModuleBuilder>();
63 private readonly List<Module> addedModules = new List<Module>();
64 private readonly List<CustomAttributeBuilder> customAttributes = new List<CustomAttributeBuilder>();
65 private readonly List<CustomAttributeBuilder> declarativeSecurity = new List<CustomAttributeBuilder>();
66 private readonly List<Type> typeForwarders = new List<Type>();
67 private Dictionary<string, Type> missingTypes;
69 private struct ResourceFile
72 internal string FileName;
73 internal ResourceAttributes Attributes;
76 internal AssemblyBuilder(Universe universe, AssemblyName name, string dir, PermissionSet requiredPermissions, PermissionSet optionalPermissions, PermissionSet refusedPermissions)
79 this.name = name.Name;
80 SetVersionHelper(name.Version);
81 if (name.CultureInfo != null && !string.IsNullOrEmpty(name.CultureInfo.Name))
83 this.culture = name.CultureInfo.Name;
85 this.flags = name.Flags;
86 this.hashAlgorithm = name.HashAlgorithm;
87 if (this.hashAlgorithm == AssemblyHashAlgorithm.None)
89 this.hashAlgorithm = AssemblyHashAlgorithm.SHA1;
91 this.keyPair = name.KeyPair;
92 if (this.keyPair != null)
94 this.publicKey = this.keyPair.PublicKey;
98 byte[] publicKey = name.GetPublicKey();
99 if (publicKey != null && publicKey.Length != 0)
101 this.publicKey = (byte[])publicKey.Clone();
104 this.dir = dir ?? ".";
105 this.requiredPermissions = requiredPermissions;
106 this.optionalPermissions = optionalPermissions;
107 this.refusedPermissions = refusedPermissions;
108 if (universe.HasMscorlib && universe.Mscorlib.ImageRuntimeVersion != null)
110 this.imageRuntimeVersion = universe.Mscorlib.ImageRuntimeVersion;
114 this.imageRuntimeVersion = typeof(object).Assembly.ImageRuntimeVersion;
118 private void SetVersionHelper(Version version)
129 majorVersion = (ushort)version.Major;
130 minorVersion = (ushort)version.Minor;
131 buildVersion = version.Build == -1 ? (ushort)0 : (ushort)version.Build;
132 revisionVersion = version.Revision == -1 ? (ushort)0 : (ushort)version.Revision;
136 public void __SetAssemblyVersion(Version version)
138 AssemblyName oldName = GetName();
139 SetVersionHelper(version);
140 universe.RenameAssembly(this, oldName);
143 public void __SetAssemblyCulture(string cultureName)
145 AssemblyName oldName = GetName();
146 this.culture = cultureName;
147 universe.RenameAssembly(this, oldName);
150 public void __SetAssemblyKeyPair(StrongNameKeyPair keyPair)
152 AssemblyName oldName = GetName();
153 this.keyPair = keyPair;
156 this.publicKey = keyPair.PublicKey;
158 universe.RenameAssembly(this, oldName);
161 // this is used in combination with delay signing
162 public void __SetAssemblyPublicKey(byte[] publicKey)
164 AssemblyName oldName = GetName();
165 this.publicKey = publicKey == null ? null : (byte[])publicKey.Clone();
166 universe.RenameAssembly(this, oldName);
169 public void __SetAssemblyAlgorithmId(AssemblyHashAlgorithm hashAlgorithm)
171 this.hashAlgorithm = hashAlgorithm;
174 public void __SetAssemblyFlags(AssemblyNameFlags flags)
179 public override AssemblyName GetName()
181 AssemblyName n = new AssemblyName();
183 n.Version = new Version(majorVersion, minorVersion, buildVersion, revisionVersion);
185 n.HashAlgorithm = hashAlgorithm;
187 n.SetPublicKey(publicKey != null ? (byte[])publicKey.Clone() : Empty<byte>.Array);
192 public override string FullName
194 get { return GetName().FullName; }
197 public override string Location
199 get { throw new NotSupportedException(); }
202 public ModuleBuilder DefineDynamicModule(string name, string fileName)
204 return DefineDynamicModule(name, fileName, false);
207 public ModuleBuilder DefineDynamicModule(string name, string fileName, bool emitSymbolInfo)
209 ModuleBuilder module = new ModuleBuilder(this, name, fileName, emitSymbolInfo);
214 public ModuleBuilder GetDynamicModule(string name)
216 foreach (ModuleBuilder module in modules)
218 if (module.Name == name)
226 public void SetCustomAttribute(ConstructorInfo con, byte[] binaryAttribute)
228 SetCustomAttribute(new CustomAttributeBuilder(con, binaryAttribute));
231 public void SetCustomAttribute(CustomAttributeBuilder customBuilder)
233 customAttributes.Add(customBuilder);
236 public void __AddDeclarativeSecurity(CustomAttributeBuilder customBuilder)
238 declarativeSecurity.Add(customBuilder);
241 public void __AddTypeForwarder(Type type)
243 typeForwarders.Add(type);
246 public void SetEntryPoint(MethodInfo entryMethod)
248 SetEntryPoint(entryMethod, PEFileKinds.ConsoleApplication);
251 public void SetEntryPoint(MethodInfo entryMethod, PEFileKinds fileKind)
253 this.entryPoint = entryMethod;
254 this.fileKind = fileKind;
257 public void __Save(Stream stream, PortableExecutableKinds portableExecutableKind, ImageFileMachine imageFileMachine)
259 if (!stream.CanRead || !stream.CanWrite || !stream.CanSeek || stream.Position != 0)
261 throw new ArgumentException("Stream must support read/write/seek and current position must be zero.", "stream");
263 if (modules.Count != 1)
265 throw new NotSupportedException("Saving to a stream is only supported for single module assemblies.");
267 SaveImpl(modules[0].fileName, stream, portableExecutableKind, imageFileMachine);
270 public void Save(string assemblyFileName)
272 Save(assemblyFileName, PortableExecutableKinds.ILOnly, ImageFileMachine.I386);
275 public void Save(string assemblyFileName, PortableExecutableKinds portableExecutableKind, ImageFileMachine imageFileMachine)
277 SaveImpl(assemblyFileName, null, portableExecutableKind, imageFileMachine);
280 private void SaveImpl(string assemblyFileName, Stream streamOrNull, PortableExecutableKinds portableExecutableKind, ImageFileMachine imageFileMachine)
282 ModuleBuilder manifestModule = null;
284 foreach (ModuleBuilder moduleBuilder in modules)
286 moduleBuilder.PopulatePropertyAndEventTables();
288 if (manifestModule == null
289 && string.Compare(moduleBuilder.fileName, assemblyFileName, StringComparison.OrdinalIgnoreCase) == 0)
291 manifestModule = moduleBuilder;
295 if (manifestModule == null)
297 manifestModule = DefineDynamicModule("RefEmit_OnDiskManifestModule", assemblyFileName, false);
300 AssemblyTable.Record assemblyRecord = new AssemblyTable.Record();
301 assemblyRecord.HashAlgId = (int)hashAlgorithm;
302 assemblyRecord.Name = manifestModule.Strings.Add(name);
303 assemblyRecord.MajorVersion = majorVersion;
304 assemblyRecord.MinorVersion = minorVersion;
305 assemblyRecord.BuildNumber = buildVersion;
306 assemblyRecord.RevisionNumber = revisionVersion;
307 if (publicKey != null)
309 assemblyRecord.PublicKey = manifestModule.Blobs.Add(ByteBuffer.Wrap(publicKey));
310 assemblyRecord.Flags = (int)(flags | AssemblyNameFlags.PublicKey);
314 assemblyRecord.Flags = (int)(flags & ~AssemblyNameFlags.PublicKey);
318 assemblyRecord.Culture = manifestModule.Strings.Add(culture);
320 int token = 0x20000000 + manifestModule.AssemblyTable.AddRecord(assemblyRecord);
322 #pragma warning disable 618
323 // this values are obsolete, but we already know that so we disable the warning
324 System.Security.Permissions.SecurityAction requestMinimum = System.Security.Permissions.SecurityAction.RequestMinimum;
325 System.Security.Permissions.SecurityAction requestOptional = System.Security.Permissions.SecurityAction.RequestOptional;
326 System.Security.Permissions.SecurityAction requestRefuse = System.Security.Permissions.SecurityAction.RequestRefuse;
327 #pragma warning restore 618
328 if (requiredPermissions != null)
330 manifestModule.AddDeclarativeSecurity(token, requestMinimum, requiredPermissions);
332 if (optionalPermissions != null)
334 manifestModule.AddDeclarativeSecurity(token, requestOptional, optionalPermissions);
336 if (refusedPermissions != null)
338 manifestModule.AddDeclarativeSecurity(token, requestRefuse, refusedPermissions);
341 if (versionInfo != null)
343 versionInfo.SetName(GetName());
344 versionInfo.SetFileName(assemblyFileName);
345 foreach (CustomAttributeBuilder cab in customAttributes)
347 // .NET doesn't support copying blob custom attributes into the version info
350 versionInfo.SetAttribute(cab);
353 ByteBuffer versionInfoData = new ByteBuffer(512);
354 versionInfo.Write(versionInfoData);
355 if (unmanagedResources == null)
357 unmanagedResources = new ResourceSection();
359 unmanagedResources.AddVersionInfo(versionInfoData);
362 foreach (CustomAttributeBuilder cab in customAttributes)
364 // we intentionally don't filter out the version info (pseudo) custom attributes (to be compatible with .NET)
365 manifestModule.SetCustomAttribute(0x20000001, cab);
368 manifestModule.AddDeclarativeSecurity(0x20000001, declarativeSecurity);
370 foreach (Type type in typeForwarders)
372 manifestModule.AddTypeForwarder(type);
375 foreach (ResourceFile resfile in resourceFiles)
377 int fileToken = AddFile(manifestModule, resfile.FileName, 1 /*ContainsNoMetaData*/);
378 ManifestResourceTable.Record rec = new ManifestResourceTable.Record();
380 rec.Flags = (int)resfile.Attributes;
381 rec.Name = manifestModule.Strings.Add(resfile.Name);
382 rec.Implementation = fileToken;
383 manifestModule.ManifestResource.AddRecord(rec);
386 int entryPointToken = 0;
388 foreach (ModuleBuilder moduleBuilder in modules)
390 moduleBuilder.FillAssemblyRefTable();
391 if (moduleBuilder != manifestModule)
394 if (entryPoint != null && entryPoint.Module == moduleBuilder)
396 ModuleWriter.WriteModule(null, null, moduleBuilder, fileKind, portableExecutableKind, imageFileMachine, moduleBuilder.unmanagedResources, entryPoint.MetadataToken);
397 entryPointToken = fileToken = AddFile(manifestModule, moduleBuilder.fileName, 0 /*ContainsMetaData*/);
401 ModuleWriter.WriteModule(null, null, moduleBuilder, fileKind, portableExecutableKind, imageFileMachine, moduleBuilder.unmanagedResources, 0);
402 fileToken = AddFile(manifestModule, moduleBuilder.fileName, 0 /*ContainsMetaData*/);
404 moduleBuilder.ExportTypes(fileToken, manifestModule);
408 foreach (Module module in addedModules)
410 int fileToken = AddFile(manifestModule, module.FullyQualifiedName, 0 /*ContainsMetaData*/);
411 module.ExportTypes(fileToken, manifestModule);
414 if (entryPointToken == 0 && entryPoint != null)
416 entryPointToken = entryPoint.MetadataToken;
419 // finally, write the manifest module
420 ModuleWriter.WriteModule(keyPair, publicKey, manifestModule, fileKind, portableExecutableKind, imageFileMachine, unmanagedResources ?? manifestModule.unmanagedResources, entryPointToken, streamOrNull);
423 private int AddFile(ModuleBuilder manifestModule, string fileName, int flags)
425 SHA1Managed hash = new SHA1Managed();
426 string fullPath = fileName;
429 fullPath = Path.Combine(dir, fileName);
431 using (FileStream fs = new FileStream(fullPath, FileMode.Open, FileAccess.Read))
433 using (CryptoStream cs = new CryptoStream(Stream.Null, hash, CryptoStreamMode.Write))
435 byte[] buf = new byte[8192];
436 ModuleWriter.HashChunk(fs, cs, buf, (int)fs.Length);
439 FileTable.Record file = new FileTable.Record();
441 file.Name = manifestModule.Strings.Add(Path.GetFileName(fileName));
442 file.HashValue = manifestModule.Blobs.Add(ByteBuffer.Wrap(hash.Hash));
443 return 0x26000000 + manifestModule.File.AddRecord(file);
446 public void AddResourceFile(string name, string fileName)
448 AddResourceFile(name, fileName, ResourceAttributes.Public);
451 public void AddResourceFile(string name, string fileName, ResourceAttributes attribs)
453 ResourceFile resfile = new ResourceFile();
455 resfile.FileName = fileName;
456 resfile.Attributes = attribs;
457 resourceFiles.Add(resfile);
460 public void DefineVersionInfoResource()
462 versionInfo = new VersionInfo();
465 public void DefineVersionInfoResource(string product, string productVersion, string company, string copyright, string trademark)
467 versionInfo = new VersionInfo();
468 versionInfo.product = product;
469 versionInfo.informationalVersion = productVersion;
470 versionInfo.company = company;
471 versionInfo.copyright = copyright;
472 versionInfo.trademark = trademark;
475 public void __DefineIconResource(byte[] iconFile)
477 unmanagedResources = new ResourceSection();
478 unmanagedResources.AddIcon(iconFile);
481 public void __DefineUnmanagedResource(byte[] resource)
483 // The standard .NET DefineUnmanagedResource(byte[]) is useless, because it embeds "resource" (as-is) as the .rsrc section,
484 // 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
485 // like DefineUnmanagedResource(string).
486 unmanagedResources = new ResourceSection();
487 unmanagedResources.ExtractResources(resource);
490 public void DefineUnmanagedResource(string resourceFileName)
492 // This method reads the specified resource file (Win32 .res file) and converts it into the appropriate format and embeds it in the .rsrc section,
493 // also setting the Resource Directory entry.
494 __DefineUnmanagedResource(File.ReadAllBytes(resourceFileName));
497 public override Type[] GetTypes()
499 List<Type> list = new List<Type>();
500 foreach (ModuleBuilder module in modules)
502 module.GetTypesImpl(list);
504 foreach (Module module in addedModules)
506 module.GetTypesImpl(list);
508 return list.ToArray();
511 internal override Type GetTypeImpl(string typeName)
513 foreach (ModuleBuilder mb in modules)
515 Type type = mb.GetTypeImpl(typeName);
521 foreach (Module module in addedModules)
523 Type type = module.GetTypeImpl(typeName);
532 internal override Type ResolveType(string ns, string name)
534 return base.ResolveType(ns, name) ?? GetMissingType(this.ManifestModule, null, ns, name);
537 internal Type GetMissingType(Module module, Type declaringType, string ns, string name)
539 if (missingTypes == null)
543 Type mt = new MissingType(module, declaringType, ns, name);
545 if (!missingTypes.TryGetValue(mt.FullName, out type))
547 missingTypes.Add(mt.FullName, mt);
553 public void __EnableMissingTypeResolution()
555 if (missingTypes == null)
557 missingTypes = new Dictionary<string, Type>();
561 public override string ImageRuntimeVersion
563 get { return imageRuntimeVersion; }
566 public void __SetImageRuntimeVersion(string imageRuntimeVersion, int mdStreamVersion)
568 this.imageRuntimeVersion = imageRuntimeVersion;
569 this.mdStreamVersion = mdStreamVersion;
572 public override Module ManifestModule
576 if (pseudoManifestModule == null)
578 pseudoManifestModule = new ManifestModule(this);
580 return pseudoManifestModule;
584 public override MethodInfo EntryPoint
586 get { return entryPoint; }
589 public override AssemblyName[] GetReferencedAssemblies()
591 return Empty<AssemblyName>.Array;
594 public override Module[] GetLoadedModules(bool getResourceModules)
596 return GetModules(getResourceModules);
599 public override Module[] GetModules(bool getResourceModules)
601 List<Module> list = new List<Module>();
602 foreach (ModuleBuilder module in modules)
604 if (getResourceModules || !module.IsResource())
609 foreach (Module module in addedModules)
611 if (getResourceModules || !module.IsResource())
616 return list.ToArray();
619 public override Module GetModule(string name)
621 foreach (ModuleBuilder module in modules)
623 if (module.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase))
628 foreach (Module module in addedModules)
630 if (module.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase))
638 public Module __AddModule(RawModule module)
640 Module mod = module.ToModule(this);
641 addedModules.Add(mod);
645 public override ManifestResourceInfo GetManifestResourceInfo(string resourceName)
647 throw new NotSupportedException();
650 public override string[] GetManifestResourceNames()
652 throw new NotSupportedException();
655 public override Stream GetManifestResourceStream(string resourceName)
657 throw new NotSupportedException();
660 internal override IList<CustomAttributeData> GetCustomAttributesData(Type attributeType)
662 List<CustomAttributeData> list = new List<CustomAttributeData>();
663 foreach (CustomAttributeBuilder cab in customAttributes)
665 if (attributeType == null || attributeType.IsAssignableFrom(cab.Constructor.DeclaringType))
667 list.Add(cab.ToData(this));
674 sealed class ManifestModule : Module
676 private readonly AssemblyBuilder assembly;
677 private readonly Guid guid = Guid.NewGuid();
679 internal ManifestModule(AssemblyBuilder assembly)
680 : base(assembly.universe)
682 this.assembly = assembly;
685 public override int MDStreamVersion
687 get { return assembly.mdStreamVersion; }
690 public override Assembly Assembly
692 get { return assembly; }
695 internal override Type GetTypeImpl(string typeName)
700 internal override void GetTypesImpl(List<Type> list)
704 public override string FullyQualifiedName
706 get { return Path.Combine(assembly.dir, "RefEmit_InMemoryManifestModule"); }
709 public override string Name
711 get { return "<In Memory Module>"; }
714 public override Guid ModuleVersionId
719 public override Type ResolveType(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments)
721 throw new ArgumentException();
724 public override MethodBase ResolveMethod(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments)
726 throw new ArgumentException();
729 public override FieldInfo ResolveField(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments)
731 throw new ArgumentException();
734 public override MemberInfo ResolveMember(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments)
736 throw new ArgumentException();
739 public override string ResolveString(int metadataToken)
741 throw new ArgumentException();
744 public override Type[] __ResolveOptionalParameterTypes(int metadataToken)
746 throw new ArgumentException();
749 public override string ScopeName
751 get { return "RefEmit_InMemoryManifestModule"; }
754 public override AssemblyName[] __GetReferencedAssemblies()
756 throw new InvalidOperationException();
759 internal override Type GetModuleType()
761 throw new InvalidOperationException();
764 internal override IKVM.Reflection.Reader.ByteReader GetBlob(int blobIndex)
766 throw new InvalidOperationException();