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>();
68 private struct ResourceFile
71 internal string FileName;
72 internal ResourceAttributes Attributes;
75 internal AssemblyBuilder(Universe universe, AssemblyName name, string dir, PermissionSet requiredPermissions, PermissionSet optionalPermissions, PermissionSet refusedPermissions)
78 this.name = name.Name;
79 SetVersionHelper(name.Version);
80 if (name.CultureInfo != null && !string.IsNullOrEmpty(name.CultureInfo.Name))
82 this.culture = name.CultureInfo.Name;
84 this.flags = name.Flags;
85 this.hashAlgorithm = name.HashAlgorithm;
86 if (this.hashAlgorithm == AssemblyHashAlgorithm.None)
88 this.hashAlgorithm = AssemblyHashAlgorithm.SHA1;
90 this.keyPair = name.KeyPair;
91 if (this.keyPair != null)
93 this.publicKey = this.keyPair.PublicKey;
97 byte[] publicKey = name.GetPublicKey();
98 if (publicKey != null && publicKey.Length != 0)
100 this.publicKey = (byte[])publicKey.Clone();
103 this.dir = dir ?? ".";
104 this.requiredPermissions = requiredPermissions;
105 this.optionalPermissions = optionalPermissions;
106 this.refusedPermissions = refusedPermissions;
107 if (universe.HasMscorlib && universe.Mscorlib.ImageRuntimeVersion != null)
109 this.imageRuntimeVersion = universe.Mscorlib.ImageRuntimeVersion;
113 this.imageRuntimeVersion = typeof(object).Assembly.ImageRuntimeVersion;
117 private void SetVersionHelper(Version version)
128 majorVersion = (ushort)version.Major;
129 minorVersion = (ushort)version.Minor;
130 buildVersion = version.Build == -1 ? (ushort)0 : (ushort)version.Build;
131 revisionVersion = version.Revision == -1 ? (ushort)0 : (ushort)version.Revision;
135 public void __SetAssemblyVersion(Version version)
137 AssemblyName oldName = GetName();
138 SetVersionHelper(version);
139 universe.RenameAssembly(this, oldName);
142 public void __SetAssemblyCulture(string cultureName)
144 AssemblyName oldName = GetName();
145 this.culture = cultureName;
146 universe.RenameAssembly(this, oldName);
149 public void __SetAssemblyKeyPair(StrongNameKeyPair keyPair)
151 AssemblyName oldName = GetName();
152 this.keyPair = keyPair;
155 this.publicKey = keyPair.PublicKey;
157 universe.RenameAssembly(this, oldName);
160 // this is used in combination with delay signing
161 public void __SetAssemblyPublicKey(byte[] publicKey)
163 AssemblyName oldName = GetName();
164 this.publicKey = publicKey == null ? null : (byte[])publicKey.Clone();
165 universe.RenameAssembly(this, oldName);
168 public void __SetAssemblyAlgorithmId(AssemblyHashAlgorithm hashAlgorithm)
170 this.hashAlgorithm = hashAlgorithm;
173 public void __SetAssemblyFlags(AssemblyNameFlags flags)
178 public override AssemblyName GetName()
180 AssemblyName n = new AssemblyName();
182 n.Version = new Version(majorVersion, minorVersion, buildVersion, revisionVersion);
184 n.HashAlgorithm = hashAlgorithm;
186 n.SetPublicKey(publicKey != null ? (byte[])publicKey.Clone() : Empty<byte>.Array);
191 public override string FullName
193 get { return GetName().FullName; }
196 public override string Location
198 get { throw new NotSupportedException(); }
201 public ModuleBuilder DefineDynamicModule(string name, string fileName)
203 return DefineDynamicModule(name, fileName, false);
206 public ModuleBuilder DefineDynamicModule(string name, string fileName, bool emitSymbolInfo)
208 ModuleBuilder module = new ModuleBuilder(this, name, fileName, emitSymbolInfo);
213 public ModuleBuilder GetDynamicModule(string name)
215 foreach (ModuleBuilder module in modules)
217 if (module.Name == name)
225 public void SetCustomAttribute(ConstructorInfo con, byte[] binaryAttribute)
227 SetCustomAttribute(new CustomAttributeBuilder(con, binaryAttribute));
230 public void SetCustomAttribute(CustomAttributeBuilder customBuilder)
232 customAttributes.Add(customBuilder);
235 public void __AddDeclarativeSecurity(CustomAttributeBuilder customBuilder)
237 declarativeSecurity.Add(customBuilder);
240 public void __AddTypeForwarder(Type type)
242 typeForwarders.Add(type);
245 public void SetEntryPoint(MethodInfo entryMethod)
247 SetEntryPoint(entryMethod, PEFileKinds.ConsoleApplication);
250 public void SetEntryPoint(MethodInfo entryMethod, PEFileKinds fileKind)
252 this.entryPoint = entryMethod;
253 this.fileKind = fileKind;
256 public void __Save(Stream stream, PortableExecutableKinds portableExecutableKind, ImageFileMachine imageFileMachine)
258 if (!stream.CanRead || !stream.CanWrite || !stream.CanSeek || stream.Position != 0)
260 throw new ArgumentException("Stream must support read/write/seek and current position must be zero.", "stream");
262 if (modules.Count != 1)
264 throw new NotSupportedException("Saving to a stream is only supported for single module assemblies.");
266 SaveImpl(modules[0].fileName, stream, portableExecutableKind, imageFileMachine);
269 public void Save(string assemblyFileName)
271 Save(assemblyFileName, PortableExecutableKinds.ILOnly, ImageFileMachine.I386);
274 public void Save(string assemblyFileName, PortableExecutableKinds portableExecutableKind, ImageFileMachine imageFileMachine)
276 SaveImpl(assemblyFileName, null, portableExecutableKind, imageFileMachine);
279 private void SaveImpl(string assemblyFileName, Stream streamOrNull, PortableExecutableKinds portableExecutableKind, ImageFileMachine imageFileMachine)
281 ModuleBuilder manifestModule = null;
283 foreach (ModuleBuilder moduleBuilder in modules)
285 moduleBuilder.PopulatePropertyAndEventTables();
287 if (manifestModule == null
288 && string.Compare(moduleBuilder.fileName, assemblyFileName, StringComparison.OrdinalIgnoreCase) == 0)
290 manifestModule = moduleBuilder;
294 if (manifestModule == null)
296 manifestModule = DefineDynamicModule("RefEmit_OnDiskManifestModule", assemblyFileName, false);
299 AssemblyTable.Record assemblyRecord = new AssemblyTable.Record();
300 assemblyRecord.HashAlgId = (int)hashAlgorithm;
301 assemblyRecord.Name = manifestModule.Strings.Add(name);
302 assemblyRecord.MajorVersion = majorVersion;
303 assemblyRecord.MinorVersion = minorVersion;
304 assemblyRecord.BuildNumber = buildVersion;
305 assemblyRecord.RevisionNumber = revisionVersion;
306 if (publicKey != null)
308 assemblyRecord.PublicKey = manifestModule.Blobs.Add(ByteBuffer.Wrap(publicKey));
309 assemblyRecord.Flags = (int)(flags | AssemblyNameFlags.PublicKey);
313 assemblyRecord.Flags = (int)(flags & ~AssemblyNameFlags.PublicKey);
317 assemblyRecord.Culture = manifestModule.Strings.Add(culture);
319 int token = 0x20000000 + manifestModule.AssemblyTable.AddRecord(assemblyRecord);
321 #pragma warning disable 618
322 // this values are obsolete, but we already know that so we disable the warning
323 System.Security.Permissions.SecurityAction requestMinimum = System.Security.Permissions.SecurityAction.RequestMinimum;
324 System.Security.Permissions.SecurityAction requestOptional = System.Security.Permissions.SecurityAction.RequestOptional;
325 System.Security.Permissions.SecurityAction requestRefuse = System.Security.Permissions.SecurityAction.RequestRefuse;
326 #pragma warning restore 618
327 if (requiredPermissions != null)
329 manifestModule.AddDeclarativeSecurity(token, requestMinimum, requiredPermissions);
331 if (optionalPermissions != null)
333 manifestModule.AddDeclarativeSecurity(token, requestOptional, optionalPermissions);
335 if (refusedPermissions != null)
337 manifestModule.AddDeclarativeSecurity(token, requestRefuse, refusedPermissions);
340 if (versionInfo != null)
342 versionInfo.SetName(GetName());
343 versionInfo.SetFileName(assemblyFileName);
344 foreach (CustomAttributeBuilder cab in customAttributes)
346 // .NET doesn't support copying blob custom attributes into the version info
349 versionInfo.SetAttribute(cab);
352 ByteBuffer versionInfoData = new ByteBuffer(512);
353 versionInfo.Write(versionInfoData);
354 if (unmanagedResources == null)
356 unmanagedResources = new ResourceSection();
358 unmanagedResources.AddVersionInfo(versionInfoData);
361 foreach (CustomAttributeBuilder cab in customAttributes)
363 // we intentionally don't filter out the version info (pseudo) custom attributes (to be compatible with .NET)
364 manifestModule.SetCustomAttribute(0x20000001, cab);
367 manifestModule.AddDeclarativeSecurity(0x20000001, declarativeSecurity);
369 foreach (Type type in typeForwarders)
371 manifestModule.AddTypeForwarder(type);
374 foreach (ResourceFile resfile in resourceFiles)
376 int fileToken = AddFile(manifestModule, resfile.FileName, 1 /*ContainsNoMetaData*/);
377 ManifestResourceTable.Record rec = new ManifestResourceTable.Record();
379 rec.Flags = (int)resfile.Attributes;
380 rec.Name = manifestModule.Strings.Add(resfile.Name);
381 rec.Implementation = fileToken;
382 manifestModule.ManifestResource.AddRecord(rec);
385 int entryPointToken = 0;
387 foreach (ModuleBuilder moduleBuilder in modules)
389 moduleBuilder.FillAssemblyRefTable();
390 if (moduleBuilder != manifestModule)
393 if (entryPoint != null && entryPoint.Module == moduleBuilder)
395 ModuleWriter.WriteModule(null, null, moduleBuilder, fileKind, portableExecutableKind, imageFileMachine, moduleBuilder.unmanagedResources, entryPoint.MetadataToken);
396 entryPointToken = fileToken = AddFile(manifestModule, moduleBuilder.fileName, 0 /*ContainsMetaData*/);
400 ModuleWriter.WriteModule(null, null, moduleBuilder, fileKind, portableExecutableKind, imageFileMachine, moduleBuilder.unmanagedResources, 0);
401 fileToken = AddFile(manifestModule, moduleBuilder.fileName, 0 /*ContainsMetaData*/);
403 moduleBuilder.ExportTypes(fileToken, manifestModule);
407 foreach (Module module in addedModules)
409 int fileToken = AddFile(manifestModule, module.FullyQualifiedName, 0 /*ContainsMetaData*/);
410 module.ExportTypes(fileToken, manifestModule);
413 if (entryPointToken == 0 && entryPoint != null)
415 entryPointToken = entryPoint.MetadataToken;
418 // finally, write the manifest module
419 ModuleWriter.WriteModule(keyPair, publicKey, manifestModule, fileKind, portableExecutableKind, imageFileMachine, unmanagedResources ?? manifestModule.unmanagedResources, entryPointToken, streamOrNull);
422 private int AddFile(ModuleBuilder manifestModule, string fileName, int flags)
424 SHA1Managed hash = new SHA1Managed();
425 string fullPath = fileName;
428 fullPath = Path.Combine(dir, fileName);
430 using (FileStream fs = new FileStream(fullPath, FileMode.Open, FileAccess.Read))
432 using (CryptoStream cs = new CryptoStream(Stream.Null, hash, CryptoStreamMode.Write))
434 byte[] buf = new byte[8192];
435 ModuleWriter.HashChunk(fs, cs, buf, (int)fs.Length);
438 FileTable.Record file = new FileTable.Record();
440 file.Name = manifestModule.Strings.Add(Path.GetFileName(fileName));
441 file.HashValue = manifestModule.Blobs.Add(ByteBuffer.Wrap(hash.Hash));
442 return 0x26000000 + manifestModule.File.AddRecord(file);
445 public void AddResourceFile(string name, string fileName)
447 AddResourceFile(name, fileName, ResourceAttributes.Public);
450 public void AddResourceFile(string name, string fileName, ResourceAttributes attribs)
452 ResourceFile resfile = new ResourceFile();
454 resfile.FileName = fileName;
455 resfile.Attributes = attribs;
456 resourceFiles.Add(resfile);
459 public void DefineVersionInfoResource()
461 versionInfo = new VersionInfo();
464 public void DefineVersionInfoResource(string product, string productVersion, string company, string copyright, string trademark)
466 versionInfo = new VersionInfo();
467 versionInfo.product = product;
468 versionInfo.informationalVersion = productVersion;
469 versionInfo.company = company;
470 versionInfo.copyright = copyright;
471 versionInfo.trademark = trademark;
474 public void __DefineIconResource(byte[] iconFile)
476 unmanagedResources = new ResourceSection();
477 unmanagedResources.AddIcon(iconFile);
480 public void __DefineUnmanagedResource(byte[] resource)
482 // The standard .NET DefineUnmanagedResource(byte[]) is useless, because it embeds "resource" (as-is) as the .rsrc section,
483 // 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
484 // like DefineUnmanagedResource(string).
485 unmanagedResources = new ResourceSection();
486 unmanagedResources.ExtractResources(resource);
489 public void DefineUnmanagedResource(string resourceFileName)
491 // This method reads the specified resource file (Win32 .res file) and converts it into the appropriate format and embeds it in the .rsrc section,
492 // also setting the Resource Directory entry.
493 __DefineUnmanagedResource(File.ReadAllBytes(resourceFileName));
496 public override Type[] GetTypes()
498 List<Type> list = new List<Type>();
499 foreach (ModuleBuilder module in modules)
501 module.GetTypesImpl(list);
503 foreach (Module module in addedModules)
505 module.GetTypesImpl(list);
507 return list.ToArray();
510 internal override Type FindType(TypeName typeName)
512 foreach (ModuleBuilder mb in modules)
514 Type type = mb.FindType(typeName);
520 foreach (Module module in addedModules)
522 Type type = module.FindType(typeName);
531 public override string ImageRuntimeVersion
533 get { return imageRuntimeVersion; }
536 public void __SetImageRuntimeVersion(string imageRuntimeVersion, int mdStreamVersion)
538 this.imageRuntimeVersion = imageRuntimeVersion;
539 this.mdStreamVersion = mdStreamVersion;
542 public override Module ManifestModule
546 if (pseudoManifestModule == null)
548 pseudoManifestModule = new ManifestModule(this);
550 return pseudoManifestModule;
554 public override MethodInfo EntryPoint
556 get { return entryPoint; }
559 public override AssemblyName[] GetReferencedAssemblies()
561 return Empty<AssemblyName>.Array;
564 public override Module[] GetLoadedModules(bool getResourceModules)
566 return GetModules(getResourceModules);
569 public override Module[] GetModules(bool getResourceModules)
571 List<Module> list = new List<Module>();
572 foreach (ModuleBuilder module in modules)
574 if (getResourceModules || !module.IsResource())
579 foreach (Module module in addedModules)
581 if (getResourceModules || !module.IsResource())
586 return list.ToArray();
589 public override Module GetModule(string name)
591 foreach (ModuleBuilder module in modules)
593 if (module.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase))
598 foreach (Module module in addedModules)
600 if (module.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase))
608 public Module __AddModule(RawModule module)
610 Module mod = module.ToModule(this);
611 addedModules.Add(mod);
615 public override ManifestResourceInfo GetManifestResourceInfo(string resourceName)
617 throw new NotSupportedException();
620 public override string[] GetManifestResourceNames()
622 throw new NotSupportedException();
625 public override Stream GetManifestResourceStream(string resourceName)
627 throw new NotSupportedException();
630 internal override IList<CustomAttributeData> GetCustomAttributesData(Type attributeType)
632 List<CustomAttributeData> list = new List<CustomAttributeData>();
633 foreach (CustomAttributeBuilder cab in customAttributes)
635 if (attributeType == null || attributeType.IsAssignableFrom(cab.Constructor.DeclaringType))
637 list.Add(cab.ToData(this));
644 sealed class ManifestModule : Module
646 private readonly AssemblyBuilder assembly;
647 private readonly Guid guid = Guid.NewGuid();
649 internal ManifestModule(AssemblyBuilder assembly)
650 : base(assembly.universe)
652 this.assembly = assembly;
655 public override int MDStreamVersion
657 get { return assembly.mdStreamVersion; }
660 public override Assembly Assembly
662 get { return assembly; }
665 internal override Type FindType(TypeName typeName)
670 internal override void GetTypesImpl(List<Type> list)
674 public override string FullyQualifiedName
676 get { return Path.Combine(assembly.dir, "RefEmit_InMemoryManifestModule"); }
679 public override string Name
681 get { return "<In Memory Module>"; }
684 public override Guid ModuleVersionId
689 public override Type ResolveType(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments)
691 throw new ArgumentException();
694 public override MethodBase ResolveMethod(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments)
696 throw new ArgumentException();
699 public override FieldInfo ResolveField(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments)
701 throw new ArgumentException();
704 public override MemberInfo ResolveMember(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments)
706 throw new ArgumentException();
709 public override string ResolveString(int metadataToken)
711 throw new ArgumentException();
714 public override Type[] __ResolveOptionalParameterTypes(int metadataToken)
716 throw new ArgumentException();
719 public override string ScopeName
721 get { return "RefEmit_InMemoryManifestModule"; }
724 public override AssemblyName[] __GetReferencedAssemblies()
726 throw new InvalidOperationException();
729 internal override Type GetModuleType()
731 throw new InvalidOperationException();
734 internal override IKVM.Reflection.Reader.ByteReader GetBlob(int blobIndex)
736 throw new InvalidOperationException();