IKVM11  11
Java SE 11 Virtual Machine for .NET
Loading...
Searching...
No Matches
AssemblyBuilder.cs
Go to the documentation of this file.
1/*
2 Copyright (C) 2008-2013 Jeroen Frijters
3
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.
7
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:
11
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.
19
20 Jeroen Frijters
21 jeroen@frijters.net
22
23*/
24using System;
25using System.Collections.Generic;
26using System.IO;
27using System.Reflection.Metadata;
28using System.Reflection.Metadata.Ecma335;
29using System.Resources;
30using System.Security.Cryptography;
31
34
36{
37
38 internal sealed class AssemblyBuilder : Assembly
39 {
40
41 readonly string name;
42 ushort majorVersion;
43 ushort minorVersion;
44 ushort buildVersion;
45 ushort revisionVersion;
46 string culture;
47 AssemblyNameFlags flags;
48 AssemblyHashAlgorithm hashAlgorithm;
49 StrongNameKeyPair keyPair;
50 byte[] publicKey;
51 internal readonly string dir;
52 PEFileKinds fileKind = PEFileKinds.Dll;
53 MethodInfo entryPoint;
54 VersionInfo versionInfo;
55 byte[] win32icon;
56 byte[] win32manifest;
57 byte[] win32resources;
58 string imageRuntimeVersion;
59 internal int mdStreamVersion = 0x20000;
60 Module pseudoManifestModule;
61 readonly List<ResourceFile> resourceFiles = new List<ResourceFile>();
62 readonly List<ModuleBuilder> modules = new List<ModuleBuilder>();
63 readonly List<Module> addedModules = new List<Module>();
64 readonly List<CustomAttributeBuilder> customAttributes = new List<CustomAttributeBuilder>();
65 readonly List<CustomAttributeBuilder> declarativeSecurity = new List<CustomAttributeBuilder>();
66 readonly List<TypeForwarder> typeForwarders = new List<TypeForwarder>();
67
68 readonly struct TypeForwarder
69 {
70
71 internal readonly Type Type;
72 internal readonly bool IncludeNested;
73
79 internal TypeForwarder(Type type, bool includeNested)
80 {
81 this.Type = type;
82 this.IncludeNested = includeNested;
83 }
84
85 }
86
87 struct ResourceFile
88 {
89
90 internal string Name;
91 internal string FileName;
92 internal ResourceAttributes Attributes;
93 internal ResourceWriter Writer;
94
95 }
96
104 internal AssemblyBuilder(Universe universe, AssemblyName name, string dir, IEnumerable<CustomAttributeBuilder> customAttributes) :
105 base(universe)
106 {
107 this.name = name.Name;
108 SetVersionHelper(name.Version);
109 if (!string.IsNullOrEmpty(name.CultureName))
110 {
111 this.culture = name.CultureName;
112 }
113
114 this.flags = name.RawFlags;
115 this.hashAlgorithm = name.HashAlgorithm;
116 if (this.hashAlgorithm == AssemblyHashAlgorithm.None)
117 this.hashAlgorithm = AssemblyHashAlgorithm.SHA1;
118
119 this.keyPair = name.KeyPair;
120 if (this.keyPair != null)
121 {
122 this.publicKey = this.keyPair.PublicKey;
123 }
124 else
125 {
126 var publicKey = name.GetPublicKey();
127 if (publicKey != null && publicKey.Length != 0)
128 this.publicKey = (byte[])publicKey.Clone();
129 }
130
131 this.dir = dir ?? ".";
132 if (customAttributes != null)
133 this.customAttributes.AddRange(customAttributes);
134
135 if (universe.HasCoreLib && !universe.CoreLib.__IsMissing && universe.CoreLib.ImageRuntimeVersion != null)
136 this.imageRuntimeVersion = universe.CoreLib.ImageRuntimeVersion;
137 else
138 this.imageRuntimeVersion = TypeUtil.GetAssembly(typeof(object)).ImageRuntimeVersion;
139
140 universe.RegisterDynamicAssembly(this);
141 }
142
143 void SetVersionHelper(Version version)
144 {
145 if (version == null)
146 {
147 majorVersion = 0;
148 minorVersion = 0;
149 buildVersion = 0;
150 revisionVersion = 0;
151 }
152 else
153 {
154 majorVersion = (ushort)version.Major;
155 minorVersion = (ushort)version.Minor;
156 buildVersion = version.Build == -1 ? (ushort)0 : (ushort)version.Build;
157 revisionVersion = version.Revision == -1 ? (ushort)0 : (ushort)version.Revision;
158 }
159 }
160
161 void Rename(AssemblyName oldName)
162 {
163 this.fullName = null;
164 Universe.RenameAssembly(this, oldName);
165 }
166
167 public void __SetAssemblyVersion(Version version)
168 {
169 AssemblyName oldName = GetName();
170 SetVersionHelper(version);
171 Rename(oldName);
172 }
173
174 public void __SetAssemblyCulture(string cultureName)
175 {
176 AssemblyName oldName = GetName();
177 this.culture = cultureName;
178 Rename(oldName);
179 }
180
181 public void __SetAssemblyKeyPair(StrongNameKeyPair keyPair)
182 {
183 AssemblyName oldName = GetName();
184 this.keyPair = keyPair;
185 if (keyPair != null)
186 {
187 this.publicKey = keyPair.PublicKey;
188 }
189 Rename(oldName);
190 }
191
192 // this is used in combination with delay signing
193 public void __SetAssemblyPublicKey(byte[] publicKey)
194 {
195 AssemblyName oldName = GetName();
196 this.publicKey = publicKey == null ? null : (byte[])publicKey.Clone();
197 Rename(oldName);
198 }
199
200 public void __SetAssemblyAlgorithmId(AssemblyHashAlgorithm hashAlgorithm)
201 {
202 this.hashAlgorithm = hashAlgorithm;
203 }
204
205 [Obsolete("Use __AssemblyFlags property instead.")]
206 public void __SetAssemblyFlags(AssemblyNameFlags flags)
207 {
208 this.__AssemblyFlags = flags;
209 }
210
211 protected override AssemblyNameFlags GetAssemblyFlags()
212 {
213 return flags;
214 }
215
216 public new AssemblyNameFlags __AssemblyFlags
217 {
218 get { return flags; }
219 set
220 {
221 AssemblyName oldName = GetName();
222 this.flags = value;
223 Rename(oldName);
224 }
225 }
226
227 internal string Name
228 {
229 get { return name; }
230 }
231
232 public override AssemblyName GetName()
233 {
234 AssemblyName n = new AssemblyName();
235 n.Name = name;
236 n.Version = new Version(majorVersion, minorVersion, buildVersion, revisionVersion);
237 n.CultureName = culture ?? "";
238 n.HashAlgorithm = hashAlgorithm;
239 n.RawFlags = flags;
240 n.SetPublicKey(publicKey != null ? (byte[])publicKey.Clone() : Array.Empty<byte>());
241 n.KeyPair = keyPair;
242 return n;
243 }
244
245 public override string Location
246 {
247 get { throw new NotSupportedException(); }
248 }
249
256 public ModuleBuilder DefineDynamicModule(string name, string fileName)
257 {
258 return DefineDynamicModule(name, fileName, false);
259 }
260
268 public ModuleBuilder DefineDynamicModule(string name, string fileName, bool emitSymbolInfo)
269 {
270 var module = new ModuleBuilder(this, name, fileName);
271 module.SetSymWriter(emitSymbolInfo ? Universe.CreateSymbolWriter(module) : null);
272 modules.Add(module);
273 return module;
274 }
275
276 public ModuleBuilder GetDynamicModule(string name)
277 {
278 foreach (var module in modules)
279 if (module.Name == name)
280 return module;
281
282 return null;
283 }
284
285 public void SetCustomAttribute(ConstructorInfo con, byte[] binaryAttribute)
286 {
287 SetCustomAttribute(new CustomAttributeBuilder(con, binaryAttribute));
288 }
289
290 public void SetCustomAttribute(CustomAttributeBuilder customBuilder)
291 {
292 customAttributes.Add(customBuilder);
293 }
294
295 public void __AddDeclarativeSecurity(CustomAttributeBuilder customBuilder)
296 {
297 declarativeSecurity.Add(customBuilder);
298 }
299
300 public void __AddTypeForwarder(Type type)
301 {
302 __AddTypeForwarder(type, true);
303 }
304
305 public void __AddTypeForwarder(Type type, bool includeNested)
306 {
307 typeForwarders.Add(new TypeForwarder(type, includeNested));
308 }
309
310 public void SetEntryPoint(MethodInfo entryMethod)
311 {
312 SetEntryPoint(entryMethod, PEFileKinds.ConsoleApplication);
313 }
314
315 public void SetEntryPoint(MethodInfo entryMethod, PEFileKinds fileKind)
316 {
317 this.entryPoint = entryMethod;
318 this.fileKind = fileKind;
319 }
320
329 public void __Save(Stream peStream, PortableExecutableKinds portableExecutableKind, ImageFileMachine imageFileMachine)
330 {
331 if (modules.Count != 1)
332 throw new NotSupportedException("Saving to a stream is only supported for single module assemblies.");
333
334 __Save(peStream, null, portableExecutableKind, imageFileMachine);
335 }
336
346 public void __Save(Stream peStream, Stream pdbStream, PortableExecutableKinds portableExecutableKind, ImageFileMachine imageFileMachine)
347 {
348 if (peStream.CanWrite == false)
349 throw new ArgumentException("Stream must support write.", nameof(peStream));
350 if (pdbStream != null && pdbStream.CanWrite == false)
351 throw new ArgumentException("Stream must support write.", nameof(pdbStream));
352 if (modules.Count != 1)
353 throw new NotSupportedException("Saving to a stream is only supported for single module assemblies.");
354
355 SaveImpl(modules[0].fileName, peStream, pdbStream, portableExecutableKind, imageFileMachine);
356 }
357
362 public void Save(string assemblyFileName)
363 {
364 Save(assemblyFileName, PortableExecutableKinds.ILOnly, ImageFileMachine.I386);
365 }
366
373 public void Save(string assemblyFileName, PortableExecutableKinds portableExecutableKind, ImageFileMachine imageFileMachine)
374 {
375 SaveImpl(assemblyFileName, null, null, portableExecutableKind, imageFileMachine);
376 }
377
386 void SaveImpl(string assemblyFileName, Stream peStream, Stream pdbStream, PortableExecutableKinds portableExecutableKind, ImageFileMachine imageFileMachine)
387 {
388 ModuleBuilder manifestModule = null;
389
390 // finalize and populate all modules
391 foreach (var moduleBuilder in modules)
392 {
393 moduleBuilder.SetIsSaved();
394 moduleBuilder.PopulatePropertyAndEventTables();
395
396 // is this the default manifest module?
397 if (manifestModule == null && string.Compare(moduleBuilder.fileName, assemblyFileName, StringComparison.OrdinalIgnoreCase) == 0)
398 manifestModule = moduleBuilder;
399 }
400
401 // generate new manifest module
402 manifestModule ??= DefineDynamicModule("RefEmit_OnDiskManifestModule", assemblyFileName, false);
403
404 // assembly record goes on manifest module
405 var assemblyRecord = new AssemblyTable.Record();
406 assemblyRecord.HashAlgId = (int)hashAlgorithm;
407 assemblyRecord.Name = manifestModule.GetOrAddString(name);
408 assemblyRecord.MajorVersion = majorVersion;
409 assemblyRecord.MinorVersion = minorVersion;
410 assemblyRecord.BuildNumber = buildVersion;
411 assemblyRecord.RevisionNumber = revisionVersion;
412
413 if (publicKey != null)
414 {
415 assemblyRecord.PublicKey = manifestModule.GetOrAddBlob(publicKey);
416 assemblyRecord.Flags = (int)(flags | AssemblyNameFlags.PublicKey);
417 }
418 else
419 {
420 assemblyRecord.Flags = (int)(flags & ~AssemblyNameFlags.PublicKey);
421 }
422
423 if (culture != null)
424 assemblyRecord.Culture = manifestModule.GetOrAddString(culture);
425
426 manifestModule.AssemblyTable.AddRecord(assemblyRecord);
427
428 // final copy of manifest module native resources
429 var nativeResources = manifestModule.nativeResources != null ? new ModuleResourceSectionBuilder(manifestModule.nativeResources) : new ModuleResourceSectionBuilder();
430
431 // version info specified on assembly: insert into manifest module
432 if (versionInfo != null)
433 {
434 versionInfo.SetName(GetName());
435 versionInfo.SetFileName(assemblyFileName);
436 foreach (var cab in customAttributes)
437 {
438 // .NET doesn't support copying blob custom attributes into the version info
439 if (cab.HasBlob == false || Universe.DecodeVersionInfoAttributeBlobs)
440 versionInfo.SetAttribute(this, cab);
441 }
442
443 var versionInfoData = new ByteBuffer(512);
444 versionInfo.Write(versionInfoData);
445 nativeResources.AddVersionInfo(versionInfoData);
446 }
447
448 // win32 icon specified on assembly: insert into manifest module
449 if (win32icon != null)
450 nativeResources.AddIcon(win32icon);
451
452 // win32 manifest specified on assembly: insert into manifest module
453 if (win32manifest != null)
454 nativeResources.AddManifest(win32manifest, fileKind == PEFileKinds.Dll ? (ushort)2 : (ushort)1);
455
456 if (win32resources != null)
457 nativeResources.ImportWin32ResourceFile(win32resources);
458
459 // we intentionally don't filter out the version info (pseudo) custom attributes (to be compatible with .NET)
460 foreach (var cab in customAttributes)
461 manifestModule.SetCustomAttribute(0x20000001, cab);
462
463 manifestModule.AddDeclarativeSecurity(0x20000001, declarativeSecurity);
464
465 foreach (var fwd in typeForwarders)
466 manifestModule.AddTypeForwarder(fwd.Type, fwd.IncludeNested);
467
468 // add resource files for assembly to manifest module
469 foreach (var resfile in resourceFiles)
470 {
471 if (resfile.Writer != null)
472 {
473 resfile.Writer.Generate();
474 resfile.Writer.Close();
475 }
476
477 var fileToken = AddFile(manifestModule, resfile.FileName, 1 /*ContainsNoMetaData*/);
478 var rec = new ManifestResourceTable.Record();
479 rec.Offset = 0;
480 rec.Flags = (int)resfile.Attributes;
481 rec.Name = manifestModule.GetOrAddString(resfile.Name);
482 rec.Implementation = MetadataTokens.GetToken(fileToken);
483 manifestModule.ManifestResourceTable.AddRecord(rec);
484 }
485
486 // write each non-manifest module
487 foreach (var module in modules)
488 {
489 module.FillAssemblyRefTable();
490
491 if (module != manifestModule)
492 {
493 var fileToken = default(AssemblyFileHandle);
494 if (entryPoint != null && entryPoint.Module == module)
495 {
496 throw new NotSupportedException("Multi-module assemblies cannot have an entry point in a module other than the manifest module.");
497 }
498 else
499 {
500 ModuleWriter.WriteModule(null, null, module, fileKind, portableExecutableKind, imageFileMachine, module.nativeResources, default);
501 fileToken = AddFile(manifestModule, module.fileName, 0 /*ContainsMetaData*/);
502 }
503
504 module.ExportTypes(fileToken, manifestModule);
505 }
506 }
507
508 // import existing modules
509 foreach (var module in addedModules)
510 {
511 var fileToken = AddFile(manifestModule, module.FullyQualifiedName, 0 /*ContainsMetaData*/);
512 module.ExportTypes(fileToken, manifestModule);
513 }
514
515 // finally, write the manifest module
516 ModuleWriter.WriteModule(keyPair, publicKey, manifestModule, fileKind, portableExecutableKind, imageFileMachine, nativeResources, entryPoint, null, peStream, null, pdbStream);
517 }
518
519 AssemblyFileHandle AddFile(ModuleBuilder manifestModule, string fileName, int flags)
520 {
521 var fullPath = fileName;
522 if (dir != null)
523 fullPath = Path.Combine(dir, fileName);
524
525 using var sha1 = SHA1.Create();
526 using var fs = new FileStream(fullPath, FileMode.Open, FileAccess.Read);
527 var hash = sha1.ComputeHash(fs);
528
529 return MetadataTokens.AssemblyFileHandle(manifestModule.__AddModule(flags, Path.GetFileName(fileName), hash));
530 }
531
532 public void AddResourceFile(string name, string fileName)
533 {
534 AddResourceFile(name, fileName, ResourceAttributes.Public);
535 }
536
537 public void AddResourceFile(string name, string fileName, ResourceAttributes attribs)
538 {
539 var resfile = new ResourceFile();
540 resfile.Name = name;
541 resfile.FileName = fileName;
542 resfile.Attributes = attribs;
543 resourceFiles.Add(resfile);
544 }
545
546 public IResourceWriter DefineResource(string name, string description, string fileName)
547 {
548 return DefineResource(name, description, fileName, ResourceAttributes.Public);
549 }
550
551 public IResourceWriter DefineResource(string name, string description, string fileName, ResourceAttributes attribute)
552 {
553 // FXBUG we ignore the description, because there is no such thing
554
555 var fullPath = fileName;
556 if (dir != null)
557 fullPath = Path.Combine(dir, fileName);
558
559 var rw = new ResourceWriter(fullPath);
560 var resfile = new ResourceFile();
561 resfile.Name = name;
562 resfile.FileName = fileName;
563 resfile.Attributes = attribute;
564 resfile.Writer = rw;
565 resourceFiles.Add(resfile);
566 return rw;
567 }
568
569 public void DefineVersionInfoResource()
570 {
571 if (versionInfo != null || win32resources != null)
572 throw new ArgumentException("Native resource has already been defined.");
573
574 versionInfo = new VersionInfo();
575 }
576
577 public void DefineVersionInfoResource(string product, string productVersion, string company, string copyright, string trademark)
578 {
579 if (versionInfo != null || win32resources != null)
580 throw new ArgumentException("Native resource has already been defined.");
581
582 versionInfo = new VersionInfo();
583 versionInfo.product = product;
584 versionInfo.informationalVersion = productVersion;
585 versionInfo.company = company;
586 versionInfo.copyright = copyright;
587 versionInfo.trademark = trademark;
588 }
589
590 public void __DefineIconResource(byte[] iconFile)
591 {
592 if (win32icon != null || win32resources != null)
593 throw new ArgumentException("Native resource has already been defined.");
594
595 win32icon = (byte[])iconFile.Clone();
596 }
597
598 public void __DefineManifestResource(byte[] manifest)
599 {
600 if (win32manifest != null || win32resources != null)
601 throw new ArgumentException("Native resource has already been defined.");
602
603 win32manifest = (byte[])manifest.Clone();
604 }
605
606 public void __DefineUnmanagedResource(byte[] resource)
607 {
608 if (versionInfo != null || win32icon != null || win32manifest != null || win32resources != null)
609 throw new ArgumentException("Native resource has already been defined.");
610
611 // The standard .NET DefineUnmanagedResource(byte[]) is useless, because it embeds "resource" (as-is) as the .rsrc section,
612 // 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
613 // like DefineUnmanagedResource(string).
614 win32resources = (byte[])resource.Clone();
615 }
616
617 public void DefineUnmanagedResource(string resourceFileName)
618 {
619 // This method reads the specified resource file (Win32 .res file) and converts it into the appropriate format and embeds it in the .rsrc section,
620 // also setting the Resource Directory entry.
621 __DefineUnmanagedResource(File.ReadAllBytes(resourceFileName));
622 }
623
624 public override Type[] GetTypes()
625 {
626 var list = new List<Type>();
627
628 foreach (var module in modules)
629 module.GetTypesImpl(list);
630
631 foreach (var module in addedModules)
632 module.GetTypesImpl(list);
633
634 return list.ToArray();
635 }
636
637 internal override Type FindType(TypeName typeName)
638 {
639 foreach (var mb in modules)
640 {
641 var type = mb.FindType(typeName);
642 if (type != null)
643 return type;
644 }
645
646 foreach (Module module in addedModules)
647 {
648 var type = module.FindType(typeName);
649 if (type != null)
650 return type;
651 }
652
653 return null;
654 }
655
656 internal override Type FindTypeIgnoreCase(TypeName lowerCaseName)
657 {
658 foreach (var mb in modules)
659 {
660 var type = mb.FindTypeIgnoreCase(lowerCaseName);
661 if (type != null)
662 return type;
663 }
664
665 foreach (Module module in addedModules)
666 {
667 var type = module.FindTypeIgnoreCase(lowerCaseName);
668 if (type != null)
669 return type;
670 }
671
672 return null;
673 }
674
675 public override string ImageRuntimeVersion
676 {
677 get { return imageRuntimeVersion; }
678 }
679
680 public void __SetImageRuntimeVersion(string imageRuntimeVersion, int mdStreamVersion)
681 {
682 this.imageRuntimeVersion = imageRuntimeVersion;
683 this.mdStreamVersion = mdStreamVersion;
684 }
685
686 public override Module ManifestModule => pseudoManifestModule ??= new ManifestModule(this);
687
688 public override MethodInfo EntryPoint => entryPoint;
689
690 public override AssemblyName[] GetReferencedAssemblies() => Array.Empty<AssemblyName>();
691
692 public override Module[] GetLoadedModules(bool getResourceModules)
693 {
694 return GetModules(getResourceModules);
695 }
696
697 public override Module[] GetModules(bool getResourceModules)
698 {
699 var list = new List<Module>();
700
701 foreach (var module in modules)
702 if (getResourceModules || !module.IsResource())
703 list.Add(module);
704
705 foreach (var module in addedModules)
706 if (getResourceModules || !module.IsResource())
707 list.Add(module);
708
709 return list.ToArray();
710 }
711
712 public override Module GetModule(string name)
713 {
714 foreach (var module in modules)
715 if (module.Name.Equals(name, StringComparison.OrdinalIgnoreCase))
716 return module;
717
718 foreach (var module in addedModules)
719 if (module.Name.Equals(name, StringComparison.OrdinalIgnoreCase))
720 return module;
721
722 return null;
723 }
724
725 public Module __AddModule(RawModule module)
726 {
727 Module mod = module.ToModule(this);
728 addedModules.Add(mod);
729 return mod;
730 }
731
732 public override ManifestResourceInfo GetManifestResourceInfo(string resourceName)
733 {
734 throw new NotSupportedException();
735 }
736
737 public override string[] GetManifestResourceNames()
738 {
739 throw new NotSupportedException();
740 }
741
742 public override Stream GetManifestResourceStream(string resourceName)
743 {
744 throw new NotSupportedException();
745 }
746
747 public override bool IsDynamic
748 {
749 get { return true; }
750 }
751
752 public static AssemblyBuilder DefineDynamicAssembly(AssemblyName name, AssemblyBuilderAccess access)
753 {
754 return new Universe().DefineDynamicAssembly(name, access);
755 }
756
757 public static AssemblyBuilder DefineDynamicAssembly(AssemblyName name, AssemblyBuilderAccess access, IEnumerable<CustomAttributeBuilder> assemblyAttributes)
758 {
759 return new Universe().DefineDynamicAssembly(name, access, assemblyAttributes);
760 }
761
762 internal override IList<CustomAttributeData> GetCustomAttributesData(Type attributeType)
763 {
764 var list = new List<CustomAttributeData>();
765 foreach (var cab in customAttributes)
766 if (attributeType == null || attributeType.IsAssignableFrom(cab.Constructor.DeclaringType))
767 list.Add(cab.ToData(this));
768
769 return list;
770 }
771
772 internal bool IsWindowsRuntime
773 {
774 get { return (flags & (AssemblyNameFlags)0x200) != 0; }
775 }
776
777 }
778
779}
IKVM.Reflection.Module Module
IKVM.Reflection.Type Type
IKVM.Reflection.Assembly Assembly
IKVM.Reflection.AssemblyName AssemblyName
IKVM.Reflection.ConstructorInfo ConstructorInfo
IKVM.Reflection.MethodInfo MethodInfo
global::java.lang.invoke.LambdaForm.Name Name
Implements a ResourceSectionBuilder for writing IKVM reflection resource section data.