IKVM11  11
Java SE 11 Virtual Machine for .NET
Loading...
Searching...
No Matches
ImportClassLoader.cs
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2014 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.Diagnostics;
27using System.IO;
28using System.IO.Compression;
29using System.Linq;
30using System.Security;
31using System.Text;
32using System.Threading;
33using System.Xml.Linq;
34
35using IKVM.Attributes;
36using IKVM.ByteCode;
38using IKVM.Reflection;
40using IKVM.Runtime;
41
42using Type = IKVM.Reflection.Type;
43
44namespace IKVM.Tools.Importer
45{
46
51 {
52
53 const string DEFAULT_RUNTIME_ARGS_PREFIX = "-J";
54
55 readonly IDiagnosticHandler diagnostics;
56 Dictionary<string, Jar.Item> classes;
57 Dictionary<string, RemapperTypeWrapper> remapped = new Dictionary<string, RemapperTypeWrapper>();
58 string assemblyName;
59 string assemblyFile;
60 string assemblyDir;
61 bool targetIsModule;
62 AssemblyBuilder assemblyBuilder;
63 MapXml.Attribute[] assemblyAttributes;
64 ImportState state;
65 private readonly StaticCompiler compiler;
66 RuntimeAssemblyClassLoader[] referencedAssemblies;
67 Dictionary<string, string> nameMappings = new Dictionary<string, string>();
68 Packages packages;
69 Dictionary<string, List<RuntimeJavaType>> ghosts;
70 RuntimeJavaType[] mappedExceptions;
71 bool[] mappedExceptionsAllSubClasses;
72 Dictionary<string, MapXml.Class> mapxml_Classes;
73 Dictionary<MethodKey, MapXml.InstructionList> mapxml_MethodBodies;
74 Dictionary<MethodKey, MapXml.ReplaceMethodCall[]> mapxml_ReplacedMethods;
75 Dictionary<MethodKey, MapXml.InstructionList> mapxml_MethodPrologues;
76 MapXml.Root map;
77 List<string> classesToCompile;
78 readonly List<ImportClassLoader> peerReferences = new List<ImportClassLoader>();
79 readonly Dictionary<string, string> peerLoading = new Dictionary<string, string>();
80 readonly List<RuntimeClassLoader> internalsVisibleTo = new List<RuntimeClassLoader>();
81 readonly List<RuntimeJavaType> dynamicallyImportedTypes = new List<RuntimeJavaType>();
82 readonly List<string> jarList = new List<string>();
83 List<RuntimeJavaType> javaTypes;
84 FakeTypes fakeTypes;
85
99 public ImportClassLoader(RuntimeContext context, StaticCompiler compiler, IDiagnosticHandler diagnostics, RuntimeAssemblyClassLoader[] referencedAssemblies, ImportState options, FileInfo assemblyPath, bool targetIsModule, string assemblyName, Dictionary<string, Jar.Item> classes) :
100 base(context, options.codegenoptions, null)
101 {
102 this.compiler = compiler ?? throw new ArgumentNullException(nameof(compiler));
103 this.diagnostics = diagnostics ?? throw new ArgumentNullException(nameof(diagnostics));
104 this.referencedAssemblies = referencedAssemblies;
105 this.state = options;
106 this.classes = classes;
107 this.assemblyName = assemblyName;
108 this.assemblyFile = assemblyPath.Name;
109 this.assemblyDir = assemblyPath.DirectoryName;
110 this.targetIsModule = targetIsModule;
111 Diagnostics.GenericCompilerInfo($"Instantiate CompilerClassLoader for {assemblyName}");
112 }
113
115 public override IDiagnosticHandler Diagnostics => diagnostics;
116
117 internal bool ReserveName(string javaName)
118 {
119 return !classes.ContainsKey(javaName) && GetTypeWrapperFactory().ReserveName(javaName);
120 }
121
122 internal void AddNameMapping(string javaName, string typeName)
123 {
124 nameMappings.Add(javaName, typeName);
125 }
126
127 internal void AddReference(RuntimeAssemblyClassLoader acl)
128 {
129 referencedAssemblies = ArrayUtil.Concat(referencedAssemblies, acl);
130 }
131
132 internal void AddReference(ImportClassLoader ccl)
133 {
134 peerReferences.Add(ccl);
135 }
136
137 internal AssemblyName GetAssemblyName()
138 {
139 return assemblyBuilder.GetName();
140 }
141
142 private static PermissionSet Combine(PermissionSet p1, PermissionSet p2)
143 {
144 if (p1 == null)
145 {
146 return p2;
147 }
148 if (p2 == null)
149 {
150 return p1;
151 }
152 return p1.Union(p2);
153 }
154
155 internal ModuleBuilder CreateModuleBuilder()
156 {
157 var name = new AssemblyName();
158 name.Name = assemblyName;
159 if (state.keyPair != null)
160 name.KeyPair = state.keyPair;
161 else if (state.publicKey != null)
162 name.SetPublicKey(state.publicKey);
163
164 name.Version = state.version;
165
166 // define a dynamic assembly and module
167 assemblyBuilder = Context.StaticCompiler.Universe.DefineDynamicAssembly(name, AssemblyBuilderAccess.ReflectionOnly, assemblyDir);
168 var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName, assemblyFile, EmitSymbols);
169
170 // if configured to emit stack trace info set source file
171 if (EmitStackTraceInfo)
172 Context.AttributeHelper.SetSourceFile(moduleBuilder, null);
173
174 // latest roslyn emits ignore symbol store always, but only disables optimizations if specified through args
175 var debugModes = DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints;
176 if (EnableOptimizations == false)
177 debugModes |= DebuggableAttribute.DebuggingModes.DisableOptimizations;
178
179 Context.AttributeHelper.SetDebuggingModes(assemblyBuilder, debugModes);
180 Context.AttributeHelper.SetRuntimeCompatibilityAttribute(assemblyBuilder);
181
182 if (state.baseAddress != 0)
183 moduleBuilder.__ImageBase = state.baseAddress;
184 if (state.fileAlignment != 0)
185 moduleBuilder.__FileAlignment = state.fileAlignment;
186 if (state.highentropyva)
187 moduleBuilder.__DllCharacteristics |= DllCharacteristics.HighEntropyVA;
188
189 // allow the runtime to "inject" dynamic classes into the assembly
190 var mainAssemblyName = state.sharedclassloader != null && state.sharedclassloader[0] != this
191 ? state.sharedclassloader[0].assemblyName
192 : assemblyName;
193 if (!DisableDynamicBinding)
194 Context.AttributeHelper.SetInternalsVisibleToAttribute(assemblyBuilder, mainAssemblyName + Context.Options.DynamicAssemblySuffixAndPublicKey);
195
196 return moduleBuilder;
197 }
198
199 public override string ToString()
200 {
201 return "CompilerClassLoader:" + state.assembly;
202 }
203
204 protected override RuntimeJavaType LoadClassImpl(string name, LoadMode mode)
205 {
206 foreach (RuntimeAssemblyClassLoader acl in referencedAssemblies)
207 {
208 var tw = acl.DoLoad(name);
209 if (tw != null)
210 return tw;
211 }
212
213 if (!peerLoading.ContainsKey(name))
214 {
215 peerLoading.Add(name, null);
216 try
217 {
218 foreach (ImportClassLoader ccl in peerReferences)
219 {
220 var tw = ccl.PeerLoad(name);
221 if (tw != null)
222 return tw;
223 }
224 if (state.sharedclassloader != null && state.sharedclassloader[0] != this)
225 {
226 var tw = state.sharedclassloader[0].PeerLoad(name);
227 if (tw != null)
228 return tw;
229 }
230 }
231 finally
232 {
233 peerLoading.Remove(name);
234 }
235 }
236
237 var tw1 = GetTypeWrapperCompilerHook(name);
238 if (tw1 != null)
239 {
240 return tw1;
241 }
242
243 // HACK the peer loading mess above may have indirectly loaded the classes without returning it,
244 // so we try once more here
245 tw1 = FindLoadedClass(name);
246 if (tw1 != null)
247 {
248 return tw1;
249 }
250
251 return FindOrLoadGenericClass(name, mode);
252 }
253
254 private RuntimeJavaType PeerLoad(string name)
255 {
256 // To keep the performance acceptable in cases where we're compiling many targets, we first check if the load can
257 // possibly succeed on this class loader, otherwise we'll end up doing a lot of futile recursive loading attempts.
258 if (classes.ContainsKey(name) || remapped.ContainsKey(name) || FindLoadedClass(name) != null)
259 {
260 var tw = TryLoadClassByName(name);
261 // HACK we don't want to load classes referenced by peers, hence the "== this" check
262 if (tw != null && tw.ClassLoader == this)
263 {
264 return tw;
265 }
266 }
267 if (state.sharedclassloader != null && state.sharedclassloader[0] == this)
268 {
269 foreach (ImportClassLoader ccl in state.sharedclassloader)
270 {
271 if (ccl != this)
272 {
273 var tw = ccl.PeerLoad(name);
274 if (tw != null)
275 {
276 return tw;
277 }
278 }
279 }
280 }
281 return null;
282 }
283
284 RuntimeJavaType GetTypeWrapperCompilerHook(string name)
285 {
286 if (remapped.TryGetValue(name, out var rtw))
287 {
288 return rtw;
289 }
290 else
291 {
292 if (classes.TryGetValue(name, out var itemRef))
293 {
294 classes.Remove(name);
295
297
298 try
299 {
300 f = new IKVM.Runtime.ClassFile(Context, Diagnostics, IKVM.ByteCode.Decoding.ClassFile.Read(itemRef.GetData()), name, ClassFileParseOptions, null);
301 }
302 catch (UnsupportedClassVersionException e)
303 {
304 Context.StaticCompiler.SuppressWarning(state, Diagnostic.ClassNotFound, name);
305 Diagnostics.ClassFormatError(name, e.Message);
306 return null;
307 }
308 catch (ByteCodeException e)
309 {
310 Context.StaticCompiler.SuppressWarning(state, Diagnostic.ClassNotFound, name);
311 Diagnostics.ClassFormatError(name, e.Message);
312 return null;
313 }
314 catch (ClassFormatError e)
315 {
316 Context.StaticCompiler.SuppressWarning(state, Diagnostic.ClassNotFound, name);
317 Diagnostics.ClassFormatError(name, e.Message);
318 return null;
319 }
320
321 if (f.Name != name)
322 {
323 Context.StaticCompiler.SuppressWarning(state, Diagnostic.ClassNotFound, name);
324 Diagnostics.WrongClassName(name, f.Name);
325 return null;
326 }
327
328 if (f.IsPublic && state.privatePackages != null)
329 {
330 foreach (string p in state.privatePackages)
331 {
332 if (f.Name.StartsWith(p))
333 {
334 f.SetInternal();
335 break;
336 }
337 }
338 }
339
340 if (f.IsPublic && state.publicPackages != null)
341 {
342 bool found = false;
343 foreach (string package in state.publicPackages)
344 {
345 if (f.Name.StartsWith(package))
346 {
347 found = true;
348 break;
349 }
350 }
351 if (!found)
352 {
353 f.SetInternal();
354 }
355 }
356
357 if (f.SourceFileAttribute != null)
358 {
359 var path = itemRef.Path;
360 if (path != null)
361 {
362 var sourceFile = Path.GetFullPath(Path.Combine(path.DirectoryName, f.SourceFileAttribute));
363 if (File.Exists(sourceFile))
364 f.SourcePath = sourceFile;
365 }
366
367 if (f.SourcePath == null)
368 {
369 if (state.sourcepath != null)
370 {
371 var package = f.Name;
372 var index = package.LastIndexOf('.');
373 package = index == -1 ? "" : package.Substring(0, index).Replace('.', '/');
374 f.SourcePath = Path.GetFullPath(Path.Combine(state.sourcepath + "/" + package, f.SourceFileAttribute));
375 }
376 else
377 {
378 f.SourcePath = f.SourceFileAttribute;
379 }
380 }
381 }
382
383 try
384 {
385 var tw = DefineClass(f, null);
386
387 // we successfully created the type, so we don't need to include the class as a resource
388 if (state.nojarstubs)
389 {
390 itemRef.Remove();
391 }
392 else
393 {
394 itemRef.MarkAsStub();
395 }
396
397 int pos = f.Name.LastIndexOf('.');
398 if (pos != -1)
399 {
400 string manifestJar = state.IsClassesJar(itemRef.Jar) ? null : itemRef.Jar.Name;
401 packages.DefinePackage(f.Name.Substring(0, pos), manifestJar);
402 }
403
404 return tw;
405 }
406 catch (ClassFormatError x)
407 {
408 Diagnostics.ClassFormatError(name, x.Message);
409 }
410 catch (IllegalAccessError x)
411 {
412 Diagnostics.IllegalAccessError(name, x.Message);
413 }
414 catch (VerifyError x)
415 {
416 Diagnostics.VerificationError(name, x.Message);
417 }
418 catch (NoClassDefFoundError x)
419 {
420 if ((state.codegenoptions & CodeGenOptions.DisableDynamicBinding) != 0)
421 {
422 Diagnostics.NoClassDefFoundError(name, x.Message);
423 }
424
425 Diagnostics.ClassNotFound(x.Message);
426 }
428 {
429 Diagnostics.GenericUnableToCompileError(name, x.GetType().Name, x.Message);
430 }
431
432 Context.StaticCompiler.SuppressWarning(state, Diagnostic.ClassNotFound, name);
433 return null;
434 }
435 else
436 {
437 return null;
438 }
439 }
440 }
441
442 // HACK when we're compiling multiple targets with -sharedclassloader, each target will have its own CompilerClassLoader,
443 // so we need to consider them equivalent (because they represent the same class loader).
444 internal bool IsEquivalentTo(RuntimeClassLoader other)
445 {
446 if (this == other)
447 {
448 return true;
449 }
451 if (ccl != null && state.sharedclassloader != null && state.sharedclassloader.Contains(ccl))
452 {
453 if (!internalsVisibleTo.Contains(ccl))
454 {
455 AddInternalsVisibleToAttribute(ccl);
456 }
457 return true;
458 }
459 return false;
460 }
461
462 internal override bool InternalsVisibleToImpl(RuntimeJavaType wrapper, RuntimeJavaType friend)
463 {
464 Debug.Assert(wrapper.ClassLoader == this);
465 RuntimeClassLoader other = friend.ClassLoader;
466 // TODO ideally we should also respect InternalsVisibleToAttribute.Annotation here
467 if (this == other || internalsVisibleTo.Contains(other))
468 {
469 return true;
470 }
472 if (ccl != null)
473 {
474 AddInternalsVisibleToAttribute(ccl);
475 return true;
476 }
477 return false;
478 }
479
480 private void AddInternalsVisibleToAttribute(ImportClassLoader ccl)
481 {
482 internalsVisibleTo.Add(ccl);
483 AssemblyBuilder asm = ccl.assemblyBuilder;
484 AssemblyName asmName = asm.GetName();
485 string name = asmName.Name;
486 byte[] pubkey = asmName.GetPublicKey();
487 if (pubkey == null && asmName.KeyPair != null)
488 {
489 pubkey = asmName.KeyPair.PublicKey;
490 }
491 if (pubkey != null && pubkey.Length != 0)
492 {
493 StringBuilder sb = new StringBuilder(name);
494 sb.Append(", PublicKey=");
495 foreach (byte b in pubkey)
496 {
497 sb.AppendFormat("{0:X2}", b);
498 }
499 name = sb.ToString();
500 }
501 Context.AttributeHelper.SetInternalsVisibleToAttribute(this.assemblyBuilder, name);
502 }
503
512 void SetMain(RuntimeJavaType type, PEFileKinds target, IDictionary<string, string> properties, bool noglobbing, Type apartmentAttributeType)
513 {
514 if (type is null)
515 throw new ArgumentNullException(nameof(type));
516 if (properties is null)
517 throw new ArgumentNullException(nameof(properties));
518
519 // global main method decorated with appropriate apartment type
520 var mainMethodProxy = GetTypeWrapperFactory().ModuleBuilder.DefineGlobalMethod("Main", MethodAttributes.Public | MethodAttributes.Static, Context.Types.Int32, new[] { Context.Types.String.MakeArrayType() });
521 if (apartmentAttributeType != null)
522 mainMethodProxy.SetCustomAttribute(new CustomAttributeBuilder(apartmentAttributeType.GetConstructor(Type.EmptyTypes), Array.Empty<object>()));
523
524 var ilgen = Context.CodeEmitterFactory.Create(mainMethodProxy);
525
526 // first argument to Launch (assembly)
527 ilgen.Emit(OpCodes.Ldtoken, type.TypeAsTBD);
528 ilgen.Emit(OpCodes.Call, Context.CompilerFactory.GetTypeFromHandleMethod);
529 ilgen.Emit(OpCodes.Callvirt, Context.Types.Type.GetProperty(nameof(System.Type.Assembly)).GetGetMethod());
530
531 // second argument to Launch (type name)
532 ilgen.Emit(OpCodes.Ldstr, type.Name);
533
534 // third argument: is this a jar
535 ilgen.Emit(OpCodes.Ldc_I4_0);
536
537 // fourth argument: args
538 ilgen.Emit(OpCodes.Ldarg_0);
539
540 // fifth argument, runtime prefix
541 ilgen.Emit(OpCodes.Ldstr, DEFAULT_RUNTIME_ARGS_PREFIX);
542
543 // sixth argument, property set to initialize JVM
544 if (properties.Count > 0)
545 {
546 var environmentType = Context.Resolver.ResolveCoreType(typeof(Environment).FullName).AsReflection();
547 var environmentExpandMethod = environmentType.GetMethod(nameof(Environment.ExpandEnvironmentVariables), [Context.Types.String]);
548 var dictionaryType = Context.Resolver.ResolveCoreType(typeof(Dictionary<,>).FullName).AsReflection().MakeGenericType(Context.Types.String, Context.Types.String);
549 var dictionaryAddMethod = dictionaryType.GetMethod("Add", [Context.Types.String, Context.Types.String]);
550
551 ilgen.EmitLdc_I4(properties.Count);
552 ilgen.Emit(OpCodes.Newobj, dictionaryType.GetConstructor([Context.Types.Int32]));
553
554 foreach (var kvp in properties)
555 {
556 ilgen.Emit(OpCodes.Dup);
557 ilgen.Emit(OpCodes.Ldstr, kvp.Key);
558 ilgen.Emit(OpCodes.Ldstr, kvp.Value);
559
560 // property value can reference an environmental variable (reassess the requirment for this)
561 if (kvp.Value.IndexOf('%') < kvp.Value.LastIndexOf('%'))
562 ilgen.Emit(OpCodes.Call, environmentExpandMethod);
563
564 // add to properties dictionary
565 ilgen.Emit(OpCodes.Callvirt, dictionaryAddMethod);
566 }
567 }
568 else
569 {
570 ilgen.Emit(OpCodes.Ldnull);
571 }
572
573 // invoke the launcher main method
574 var launchMethod = Context.Resolver.ResolveRuntimeType(typeof(IKVM.Runtime.Launcher).FullName).GetMethod(nameof(IKVM.Runtime.Launcher.Run)).AsReflection();
575 ilgen.Emit(OpCodes.Call, launchMethod);
576 ilgen.Emit(OpCodes.Ret);
577
578 // generate entry point
579 ilgen.DoEmit();
580 assemblyBuilder.SetEntryPoint(mainMethodProxy, target);
581 }
582
583 void PrepareSave()
584 {
585 ((DynamicClassLoader)this.GetTypeWrapperFactory()).FinishAll();
586 }
587
588 void Save()
589 {
590 ModuleBuilder mb = GetTypeWrapperFactory().ModuleBuilder;
591 if (targetIsModule)
592 {
593 // HACK force all referenced assemblies to end up as references in the assembly
594 // (even if they are otherwise unused), to make sure that the assembly class loader
595 // delegates to them at runtime.
596 // NOTE now we only do this for modules, when we're an assembly we store the exported
597 // assemblies in the ikvm.exports resource.
598 for (int i = 0; i < referencedAssemblies.Length; i++)
599 {
600 Type[] types = referencedAssemblies[i].MainAssembly.GetExportedTypes();
601 if (types.Length > 0)
602 {
603 mb.GetTypeToken(types[0]);
604 }
605 }
606 }
607 mb.CreateGlobalFunctions();
608
609 AddJavaModuleAttribute(mb);
610
611 // add a package list and export map
612 if (state.sharedclassloader == null || state.sharedclassloader[0] == this)
613 {
614 var packageListAttributeCtor = Context.Resolver.ResolveRuntimeType(typeof(PackageListAttribute).FullName).AsReflection().GetConstructor([Context.Types.String, Context.Types.String.MakeArrayType()]);
615 foreach (object[] args in packages.ToArray())
616 {
617 args[1] = UnicodeUtil.EscapeInvalidSurrogates((string[])args[1]);
618 mb.SetCustomAttribute(new CustomAttributeBuilder(packageListAttributeCtor, args));
619 }
620 // We can't add the resource when we're a module, because a multi-module assembly has a single resource namespace
621 // and since you cannot combine -target:module with -sharedclassloader we don't need an export map
622 // (the wildcard exports have already been added above, by making sure that we statically reference the assemblies).
623 if (!targetIsModule)
624 {
625 WriteExportMap();
626 }
627 }
628
629 if (targetIsModule)
630 {
631 Diagnostics.GenericCompilerInfo($"CompilerClassLoader saving {assemblyFile} in {assemblyDir}");
632
633 try
634 {
635 GetTypeWrapperFactory().ModuleBuilder.__Save(state.pekind, state.imageFileMachine);
636 }
637 catch (IOException x)
638 {
639 throw new FatalCompilerErrorException(DiagnosticEvent.ErrorWritingFile(GetTypeWrapperFactory().ModuleBuilder.FullyQualifiedName, x.Message));
640 }
641 catch (UnauthorizedAccessException x)
642 {
643 throw new FatalCompilerErrorException(DiagnosticEvent.ErrorWritingFile(GetTypeWrapperFactory().ModuleBuilder.FullyQualifiedName, x.Message));
644 }
645 }
646 else
647 {
648 Diagnostics.GenericCompilerInfo($"CompilerClassLoader saving {assemblyFile} in {assemblyDir}");
649
650 try
651 {
652 assemblyBuilder.Save(assemblyFile, state.pekind, state.imageFileMachine);
653 }
654 catch (IOException x)
655 {
656 throw new FatalCompilerErrorException(DiagnosticEvent.ErrorWritingFile(Path.Combine(assemblyDir, assemblyFile), x.Message));
657 }
658 catch (UnauthorizedAccessException x)
659 {
660 throw new FatalCompilerErrorException(DiagnosticEvent.ErrorWritingFile(Path.Combine(assemblyDir, assemblyFile), x.Message));
661 }
662 }
663 }
664
665 void AddJavaModuleAttribute(ModuleBuilder mb)
666 {
667 var typeofJavaModuleAttribute = Context.Resolver.ResolveRuntimeType(typeof(JavaModuleAttribute).FullName).AsReflection();
668 var propInfos = new[] { typeofJavaModuleAttribute.GetProperty("Jars") };
669 var propValues = new object[] { jarList.ToArray() };
670
671 if (nameMappings.Count > 0)
672 {
673 var list = new string[nameMappings.Count * 2];
674 int i = 0;
675 foreach (var kv in nameMappings)
676 {
677 list[i++] = kv.Key;
678 list[i++] = kv.Value;
679 }
680
681 list = UnicodeUtil.EscapeInvalidSurrogates(list);
682 var cab = new CustomAttributeBuilder(typeofJavaModuleAttribute.GetConstructor([Context.Resolver.ResolveCoreType(typeof(string).FullName).MakeArrayType().AsReflection()]), [list], propInfos, propValues);
683 mb.SetCustomAttribute(cab);
684 }
685 else
686 {
687 var cab = new CustomAttributeBuilder(typeofJavaModuleAttribute.GetConstructor([]), [], propInfos, propValues);
688 mb.SetCustomAttribute(cab);
689 }
690 }
691
692 static void AddExportMapEntry(Dictionary<string, List<string>> map, ImportClassLoader ccl, string name)
693 {
694 string assemblyName = ccl.assemblyBuilder.FullName;
695
696 if (map.TryGetValue(assemblyName, out var list) == false)
697 {
698 list = new List<string>();
699 map.Add(assemblyName, list);
700 }
701
702 if (list != null) // if list is null, we already have a wildcard export for this assembly
703 list.Add(name);
704 }
705
706 void AddWildcardExports(Dictionary<string, List<string>> exportedNamesPerAssembly)
707 {
708 foreach (var acl in referencedAssemblies)
709 exportedNamesPerAssembly[acl.MainAssembly.FullName] = null;
710 }
711
712 void WriteExportMap()
713 {
714 var exportedNamesPerAssembly = new Dictionary<string, List<string>>();
715
716 AddWildcardExports(exportedNamesPerAssembly);
717 foreach (var tw in dynamicallyImportedTypes)
718 AddExportMapEntry(exportedNamesPerAssembly, (ImportClassLoader)tw.ClassLoader, tw.Name);
719
720 if (state.sharedclassloader == null)
721 {
722 foreach (var ccl in peerReferences)
723 exportedNamesPerAssembly[ccl.assemblyBuilder.FullName] = null;
724 }
725 else
726 {
727 foreach (var ccl in state.sharedclassloader)
728 {
729 if (ccl != this)
730 {
731 ccl.AddWildcardExports(exportedNamesPerAssembly);
732 foreach (var jar in ccl.state.jars)
733 foreach (var item in jar)
734 if (item.IsStub == false)
735 AddExportMapEntry(exportedNamesPerAssembly, ccl, item.Name);
736
737 if (ccl.state.externalResources != null)
738 foreach (string name in ccl.state.externalResources.Keys)
739 AddExportMapEntry(exportedNamesPerAssembly, ccl, name);
740 }
741 }
742 }
743
744 var ms = new MemoryStream();
745 var bw = new BinaryWriter(ms);
746 bw.Write(exportedNamesPerAssembly.Count);
747
748 foreach (var kv in exportedNamesPerAssembly)
749 {
750 bw.Write(kv.Key);
751 if (kv.Value == null)
752 {
753 // wildcard export
754 bw.Write(0);
755 }
756 else
757 {
758 Debug.Assert(kv.Value.Count != 0);
759 bw.Write(kv.Value.Count);
760 foreach (var name in kv.Value)
761 bw.Write(JVM.PersistableHash(name));
762 }
763 }
764 ms.Position = 0;
765 GetTypeWrapperFactory().ModuleBuilder.DefineManifestResource("ikvm.exports", ms, ResourceAttributes.Public);
766 }
767
768 void WriteResources()
769 {
770 Diagnostics.GenericCompilerInfo("CompilerClassLoader adding resources...");
771
772 // BUG we need to call GetTypeWrapperFactory() to make sure that the assemblyBuilder is created (when building an empty target)
773 var moduleBuilder = GetTypeWrapperFactory().ModuleBuilder;
774
775 for (int i = 0; i < state.jars.Count; i++)
776 {
777 var hasEntries = false;
778 var mem = new MemoryStream();
779 using (var zip = new ZipArchive(mem, ZipArchiveMode.Create))
780 {
781 var stubs = new List<string>();
782 foreach (Jar.Item item in state.jars[i])
783 {
784 if (item.IsStub)
785 {
786 // we don't want stub class pseudo resources for classes loaded from the file system
787 if (i != state.classesJar)
788 stubs.Add(item.Name);
789
790 continue;
791 }
792 var zipEntry = zip.CreateEntry(item.Name, state.compressedResources ? CompressionLevel.Optimal : CompressionLevel.NoCompression);
793
794 byte[] data = item.GetData();
795
796 using Stream stream = zipEntry.Open();
797 stream.Write(data, 0, data.Length);
798
799 hasEntries = true;
800 }
801
802 if (stubs.Count != 0)
803 {
804 // generate the --ikvm-classes-- file in the jar
805 var zipEntry = zip.CreateEntry(JVM.Internal.JarClassList);
806
807 using Stream stream = zipEntry.Open();
808 using BinaryWriter bw = new BinaryWriter(stream);
809
810 bw.Write(stubs.Count);
811 foreach (string classFile in stubs)
812 bw.Write(classFile);
813
814 hasEntries = true;
815 }
816 }
817
818 // don't include empty classes.jar
819 if (i != state.classesJar || hasEntries)
820 {
821 mem = new MemoryStream(mem.ToArray());
822 var name = state.jars[i].Name;
823 if (state.targetIsModule)
824 name = Path.GetFileNameWithoutExtension(name) + "-" + moduleBuilder.ModuleVersionId.ToString("N") + Path.GetExtension(name);
825
826 jarList.Add(name);
827 moduleBuilder.DefineManifestResource(name, mem, ResourceAttributes.Public);
828 }
829 }
830 }
831
832 private static MethodAttributes MapMethodAccessModifiers(IKVM.Tools.Importer.MapXml.MapModifiers mod)
833 {
834 const IKVM.Tools.Importer.MapXml.MapModifiers access = IKVM.Tools.Importer.MapXml.MapModifiers.Public | IKVM.Tools.Importer.MapXml.MapModifiers.Protected | IKVM.Tools.Importer.MapXml.MapModifiers.Private;
835 switch (mod & access)
836 {
837 case IKVM.Tools.Importer.MapXml.MapModifiers.Public:
838 return MethodAttributes.Public;
839 case IKVM.Tools.Importer.MapXml.MapModifiers.Protected:
840 return MethodAttributes.FamORAssem;
841 case IKVM.Tools.Importer.MapXml.MapModifiers.Private:
842 return MethodAttributes.Private;
843 default:
844 return MethodAttributes.Assembly;
845 }
846 }
847
848 private static FieldAttributes MapFieldAccessModifiers(IKVM.Tools.Importer.MapXml.MapModifiers mod)
849 {
850 const IKVM.Tools.Importer.MapXml.MapModifiers access = IKVM.Tools.Importer.MapXml.MapModifiers.Public | IKVM.Tools.Importer.MapXml.MapModifiers.Protected | IKVM.Tools.Importer.MapXml.MapModifiers.Private;
851 switch (mod & access)
852 {
853 case IKVM.Tools.Importer.MapXml.MapModifiers.Public:
854 return FieldAttributes.Public;
855 case IKVM.Tools.Importer.MapXml.MapModifiers.Protected:
856 return FieldAttributes.FamORAssem;
857 case IKVM.Tools.Importer.MapXml.MapModifiers.Private:
858 return FieldAttributes.Private;
859 default:
860 return FieldAttributes.Assembly;
861 }
862 }
863
864 private sealed class RemapperTypeWrapper : RuntimeJavaType
865 {
866 private ImportClassLoader classLoader;
867 private TypeBuilder typeBuilder;
868 private TypeBuilder helperTypeBuilder;
869 private Type shadowType;
870 private IKVM.Tools.Importer.MapXml.Class classDef;
871 private RuntimeJavaType baseTypeWrapper;
872 private RuntimeJavaType[] interfaceWrappers;
873
874 internal override RuntimeClassLoader ClassLoader => classLoader;
875
876 internal override bool IsRemapped
877 {
878 get
879 {
880 return true;
881 }
882 }
883
884 private static RuntimeJavaType GetBaseWrapper(RuntimeContext context, IKVM.Tools.Importer.MapXml.Class c)
885 {
886 if ((c.Modifiers & IKVM.Tools.Importer.MapXml.MapModifiers.Interface) != 0)
887 {
888 return null;
889 }
890 if (c.Name == "java.lang.Object")
891 {
892 return null;
893 }
894
895 return context.JavaBase.TypeOfJavaLangObject;
896 }
897
898 internal RemapperTypeWrapper(RuntimeContext context, ImportClassLoader classLoader, IKVM.Tools.Importer.MapXml.Class c, IKVM.Tools.Importer.MapXml.Root map)
899 : base(context, TypeFlags.None, (Modifiers)c.Modifiers, c.Name)
900 {
901 this.classLoader = classLoader;
902 this.baseTypeWrapper = GetBaseWrapper(context, c);
903 classDef = c;
904 bool baseIsSealed = false;
905 shadowType = context.StaticCompiler.Universe.GetType(c.Shadows, true);
906 classLoader.SetRemappedType(shadowType, this);
907 Type baseType = shadowType;
908 Type baseInterface = null;
909 if (baseType.IsInterface)
910 {
911 baseInterface = baseType;
912 }
913 TypeAttributes attrs = TypeAttributes.Public;
914 if ((c.Modifiers & IKVM.Tools.Importer.MapXml.MapModifiers.Interface) == 0)
915 {
916 attrs |= TypeAttributes.Class;
917 if (baseType.IsSealed)
918 {
919 baseIsSealed = true;
920 attrs |= TypeAttributes.Abstract | TypeAttributes.Sealed;
921 }
922 }
923 else
924 {
925 attrs |= TypeAttributes.Interface | TypeAttributes.Abstract;
926 baseType = null;
927 }
928 if ((c.Modifiers & IKVM.Tools.Importer.MapXml.MapModifiers.Abstract) != 0)
929 {
930 attrs |= TypeAttributes.Abstract;
931 }
932 string name = c.Name.Replace('/', '.');
933 typeBuilder = classLoader.GetTypeWrapperFactory().ModuleBuilder.DefineType(name, attrs, baseIsSealed ? Context.Types.Object : baseType);
934 if (c.Attributes != null)
935 {
936 foreach (IKVM.Tools.Importer.MapXml.Attribute custattr in c.Attributes)
937 {
938 Context.AttributeHelper.SetCustomAttribute(classLoader, typeBuilder, custattr);
939 }
940 }
941 if (baseInterface != null)
942 {
943 typeBuilder.AddInterfaceImplementation(baseInterface);
944 }
945 if (classLoader.EmitStackTraceInfo)
946 {
947 Context.AttributeHelper.SetSourceFile(typeBuilder, classLoader.state.remapfile.Name);
948 }
949
950 if (baseIsSealed)
951 {
952 Context.AttributeHelper.SetModifiers(typeBuilder, (Modifiers)c.Modifiers, false);
953 }
954
955 if (c.Scope == MapXml.Scope.Public)
956 {
957 // FXBUG we would like to emit an attribute with a Type argument here, but that doesn't work because
958 // of a bug in SetCustomAttribute that causes type arguments to be serialized incorrectly (if the type
959 // is in the same assembly). Normally we use AttributeHelper.FreezeDry to get around this, but that doesn't
960 // work in this case (no attribute is emitted at all). So we work around by emitting a string instead
961 Context.AttributeHelper.SetRemappedClass(classLoader.assemblyBuilder, name, shadowType);
962
963 Context.AttributeHelper.SetRemappedType(typeBuilder, shadowType);
964 }
965
966 var methods = new List<RuntimeJavaMethod>();
967
968 if (c.Constructors != null)
969 {
970 foreach (IKVM.Tools.Importer.MapXml.Constructor m in c.Constructors)
971 {
972 methods.Add(new RemappedConstructorWrapper(this, m));
973 }
974 }
975
976 if (c.Methods != null)
977 {
978 foreach (IKVM.Tools.Importer.MapXml.Method m in c.Methods)
979 {
980 methods.Add(new RemappedMethodWrapper(this, m, map, false));
981 }
982 }
983 // add methods from our super classes (e.g. Throwable should have Object's methods)
984 if (!this.IsFinal && !this.IsInterface && this.BaseTypeWrapper != null)
985 {
986 foreach (var mw in BaseTypeWrapper.GetMethods())
987 {
988 var rmw = mw as RemappedMethodWrapper;
989 if (rmw != null && (rmw.IsPublic || rmw.IsProtected))
990 {
991 if (!FindMethod(methods, rmw.Name, rmw.Signature))
992 {
993 methods.Add(new RemappedMethodWrapper(this, rmw.XmlMethod, map, true));
994 }
995 }
996 }
997 }
998
999 SetMethods(methods.ToArray());
1000 }
1001
1002 internal sealed override RuntimeJavaType BaseTypeWrapper
1003 {
1004 get { return baseTypeWrapper; }
1005 }
1006
1007 internal void LoadInterfaces(IKVM.Tools.Importer.MapXml.Class c)
1008 {
1009 if (c.Interfaces != null)
1010 {
1011 interfaceWrappers = new RuntimeJavaType[c.Interfaces.Length];
1012 for (int i = 0; i < c.Interfaces.Length; i++)
1013 {
1014 var iface = classLoader.LoadClassByName(c.Interfaces[i].Class);
1015 interfaceWrappers[i] = iface;
1016 foreach (var mw in iface.GetMethods())
1017 {
1018 // make sure default interface methods are implemented (they currently have to be explicitly implemented in map.xml)
1019 if (mw.IsVirtual && !mw.IsAbstract)
1020 {
1021 if (GetMethod(mw.Name, mw.Signature, true) == null)
1022 {
1023 classLoader.Diagnostics.RemappedTypeMissingDefaultInterfaceMethod(Name, iface.Name + "." + mw.Name + mw.Signature);
1024 }
1025 }
1026 }
1027 }
1028 }
1029 else
1030 {
1031 interfaceWrappers = Array.Empty<RuntimeJavaType>();
1032 }
1033 }
1034
1035 private static bool FindMethod(List<RuntimeJavaMethod> methods, string name, string sig)
1036 {
1037 foreach (var mw in methods)
1038 {
1039 if (mw.Name == name && mw.Signature == sig)
1040 {
1041 return true;
1042 }
1043 }
1044 return false;
1045 }
1046
1047 abstract class RemappedMethodBaseWrapper : RuntimeJavaMethod
1048 {
1049
1050 internal RemappedMethodBaseWrapper(RemapperTypeWrapper typeWrapper, string name, string sig, Modifiers modifiers) :
1051 base(typeWrapper, name, sig, null, null, null, modifiers, MemberFlags.None)
1052 {
1053
1054 }
1055
1056 internal abstract MethodBase DoLink();
1057
1058 internal abstract void Finish();
1059
1060 }
1061
1062 sealed class RemappedConstructorWrapper : RemappedMethodBaseWrapper
1063 {
1064
1065 private IKVM.Tools.Importer.MapXml.Constructor m;
1066 private MethodBuilder mbHelper;
1067
1068 internal RemappedConstructorWrapper(RemapperTypeWrapper typeWrapper, IKVM.Tools.Importer.MapXml.Constructor m)
1069 : base(typeWrapper, "<init>", m.Sig, (Modifiers)m.Modifiers)
1070 {
1071 this.m = m;
1072 }
1073
1074 internal override void EmitCall(CodeEmitter ilgen)
1075 {
1076 ilgen.Emit(OpCodes.Call, GetMethod());
1077 }
1078
1079 internal override void EmitNewobj(CodeEmitter ilgen)
1080 {
1081 if (mbHelper != null)
1082 {
1083 ilgen.Emit(OpCodes.Call, mbHelper);
1084 }
1085 else
1086 {
1087 ilgen.Emit(OpCodes.Newobj, GetMethod());
1088 }
1089 }
1090
1091 internal override MethodBase DoLink()
1092 {
1093 MethodAttributes attr = MapMethodAccessModifiers(m.Modifiers);
1094 RemapperTypeWrapper typeWrapper = (RemapperTypeWrapper)DeclaringType;
1095 Type[] paramTypes = typeWrapper.ClassLoader.ArgTypeListFromSig(m.Sig);
1096
1097 MethodBuilder cbCore = null;
1098
1099 if (typeWrapper.shadowType.IsSealed)
1100 {
1101 mbHelper = typeWrapper.typeBuilder.DefineMethod("newhelper", attr | MethodAttributes.Static, CallingConventions.Standard, typeWrapper.shadowType, paramTypes);
1102 if (m.Attributes != null)
1103 {
1104 foreach (IKVM.Tools.Importer.MapXml.Attribute custattr in m.Attributes)
1105 {
1106 DeclaringType.Context.AttributeHelper.SetCustomAttribute(DeclaringType.ClassLoader, mbHelper, custattr);
1107 }
1108 }
1109 SetParameters(DeclaringType.ClassLoader, mbHelper, m.Parameters);
1110 DeclaringType.Context.AttributeHelper.SetModifiers(mbHelper, (Modifiers)m.Modifiers, false);
1111 DeclaringType.Context.AttributeHelper.SetNameSig(mbHelper, "<init>", m.Sig);
1112 AddDeclaredExceptions(DeclaringType.Context, mbHelper, m.Throws);
1113 }
1114 else
1115 {
1116 cbCore = ReflectUtil.DefineConstructor(typeWrapper.typeBuilder, attr, paramTypes);
1117 if (m.Attributes != null)
1118 {
1119 foreach (IKVM.Tools.Importer.MapXml.Attribute custattr in m.Attributes)
1120 {
1121 DeclaringType.Context.AttributeHelper.SetCustomAttribute(DeclaringType.ClassLoader, cbCore, custattr);
1122 }
1123 }
1124 SetParameters(DeclaringType.ClassLoader, cbCore, m.Parameters);
1125 AddDeclaredExceptions(DeclaringType.Context, cbCore, m.Throws);
1126 }
1127 return cbCore;
1128 }
1129
1130 internal override void Finish()
1131 {
1132 // TODO we should insert method tracing (if enabled)
1133
1134 Type[] paramTypes = this.GetParametersForDefineMethod();
1135
1136 MethodBuilder cbCore = GetMethod() as MethodBuilder;
1137
1138 if (cbCore != null)
1139 {
1140 CodeEmitter ilgen = DeclaringType.Context.CodeEmitterFactory.Create(cbCore);
1141 // TODO we need to support ghost (and other funky?) parameter types
1142 if (m.Body != null)
1143 {
1144 // TODO do we need return type conversion here?
1145 m.Body.Emit(DeclaringType.ClassLoader, ilgen);
1146 }
1147 else
1148 {
1149 ilgen.Emit(OpCodes.Ldarg_0);
1150 for (int i = 0; i < paramTypes.Length; i++)
1151 {
1152 ilgen.EmitLdarg(i + 1);
1153 }
1154 if (m.Redirect != null)
1155 {
1156 throw new NotImplementedException();
1157 }
1158 else
1159 {
1160 ConstructorInfo baseCon = DeclaringType.TypeAsTBD.GetConstructor(paramTypes);
1161 if (baseCon == null)
1162 {
1163 // TODO better error handling
1164 throw new InvalidOperationException("base class constructor not found: " + DeclaringType.Name + ".<init>" + m.Sig);
1165 }
1166 ilgen.Emit(OpCodes.Call, baseCon);
1167 }
1168 ilgen.Emit(OpCodes.Ret);
1169 }
1170 ilgen.DoEmit();
1171 if (this.DeclaringType.ClassLoader.EmitStackTraceInfo)
1172 {
1173 ilgen.EmitLineNumberTable(cbCore);
1174 }
1175 }
1176
1177 if (mbHelper != null)
1178 {
1179 CodeEmitter ilgen = DeclaringType.Context.CodeEmitterFactory.Create(mbHelper);
1180 if (m.Redirect != null)
1181 {
1182 m.Redirect.Emit(DeclaringType.ClassLoader, ilgen);
1183 }
1184 else if (m.AlternateBody != null)
1185 {
1186 m.AlternateBody.Emit(DeclaringType.ClassLoader, ilgen);
1187 }
1188 else if (m.Body != null)
1189 {
1190 // <body> doesn't make sense for helper constructors (which are actually factory methods)
1191 throw new InvalidOperationException();
1192 }
1193 else
1194 {
1195 ConstructorInfo baseCon = DeclaringType.TypeAsTBD.GetConstructor(paramTypes);
1196 if (baseCon == null)
1197 {
1198 // TODO better error handling
1199 throw new InvalidOperationException("constructor not found: " + DeclaringType.Name + ".<init>" + m.Sig);
1200 }
1201 for (int i = 0; i < paramTypes.Length; i++)
1202 {
1203 ilgen.EmitLdarg(i);
1204 }
1205 ilgen.Emit(OpCodes.Newobj, baseCon);
1206 ilgen.Emit(OpCodes.Ret);
1207 }
1208 ilgen.DoEmit();
1209 if (this.DeclaringType.ClassLoader.EmitStackTraceInfo)
1210 {
1211 ilgen.EmitLineNumberTable(mbHelper);
1212 }
1213 }
1214 }
1215 }
1216
1217 sealed class RemappedMethodWrapper : RemappedMethodBaseWrapper
1218 {
1219
1220 private IKVM.Tools.Importer.MapXml.Method m;
1221 private IKVM.Tools.Importer.MapXml.Root map;
1222 private MethodBuilder mbHelper;
1223 private List<RemapperTypeWrapper> overriders = new List<RemapperTypeWrapper>();
1224 private bool inherited;
1225
1226 internal RemappedMethodWrapper(RemapperTypeWrapper typeWrapper, IKVM.Tools.Importer.MapXml.Method m, IKVM.Tools.Importer.MapXml.Root map, bool inherited)
1227 : base(typeWrapper, m.Name, m.Sig, (Modifiers)m.Modifiers)
1228 {
1229 this.m = m;
1230 this.map = map;
1231 this.inherited = inherited;
1232 }
1233
1234 internal IKVM.Tools.Importer.MapXml.Method XmlMethod
1235 {
1236 get
1237 {
1238 return m;
1239 }
1240 }
1241
1242 internal override void EmitCall(CodeEmitter ilgen)
1243 {
1244 if (!IsStatic && IsFinal)
1245 {
1246 // When calling a final instance method on a remapped type from a class derived from a .NET class (i.e. a cli.System.Object or cli.System.Exception derived base class)
1247 // then we can't call the java.lang.Object or java.lang.Throwable methods and we have to go through the instancehelper_ method. Note that since the method
1248 // is final, this won't affect the semantics.
1249 EmitCallvirt(ilgen);
1250 }
1251 else
1252 {
1253 ilgen.Emit(OpCodes.Call, (MethodInfo)GetMethod());
1254 }
1255 }
1256
1257 internal override void EmitCallvirt(CodeEmitter ilgen)
1258 {
1259 EmitCallvirtImpl(ilgen, this.IsProtected && !mbHelper.IsPublic);
1260 }
1261
1262 private void EmitCallvirtImpl(CodeEmitter ilgen, bool cloneOrFinalizeHack)
1263 {
1264 if (mbHelper != null && !cloneOrFinalizeHack)
1265 {
1266 ilgen.Emit(OpCodes.Call, mbHelper);
1267 }
1268 else
1269 {
1270 ilgen.Emit(OpCodes.Callvirt, (MethodInfo)GetMethod());
1271 }
1272 }
1273
1274 internal override MethodBase DoLink()
1275 {
1276 RemapperTypeWrapper typeWrapper = (RemapperTypeWrapper)DeclaringType;
1277
1278 if (typeWrapper.IsInterface)
1279 {
1280 if (m.Override == null)
1281 {
1282 throw new InvalidOperationException(typeWrapper.Name + "." + m.Name + m.Sig);
1283 }
1284 MethodInfo interfaceMethod = typeWrapper.shadowType.GetMethod(m.Override.Name, typeWrapper.ClassLoader.ArgTypeListFromSig(m.Sig));
1285 if (interfaceMethod == null)
1286 {
1287 throw new InvalidOperationException(typeWrapper.Name + "." + m.Name + m.Sig);
1288 }
1289 // if any of the remapped types has a body for this interface method, we need a helper method
1290 // to special invocation through this interface for that type
1291 List<IKVM.Tools.Importer.MapXml.Class> specialCases = null;
1292 foreach (IKVM.Tools.Importer.MapXml.Class c in map.Assembly.Classes)
1293 {
1294 if (c.Methods != null)
1295 {
1296 foreach (IKVM.Tools.Importer.MapXml.Method mm in c.Methods)
1297 {
1298 if (mm.Name == m.Name && mm.Sig == m.Sig && mm.Body != null)
1299 {
1300 if (specialCases == null)
1301 {
1302 specialCases = new List<IKVM.Tools.Importer.MapXml.Class>();
1303 }
1304 specialCases.Add(c);
1305 break;
1306 }
1307 }
1308 }
1309 }
1310 string[] throws;
1311 if (m.Throws == null)
1312 {
1313 throws = new string[0];
1314 }
1315 else
1316 {
1317 throws = new string[m.Throws.Length];
1318 for (int i = 0; i < throws.Length; i++)
1319 {
1320 throws[i] = m.Throws[i].Class;
1321 }
1322 }
1323 DeclaringType.Context.AttributeHelper.SetRemappedInterfaceMethod(typeWrapper.typeBuilder, m.Name, m.Override.Name, throws);
1324 MethodBuilder helper = null;
1325 if (specialCases != null)
1326 {
1327 CodeEmitter ilgen;
1328 Type[] argTypes = ArrayUtil.Concat(typeWrapper.shadowType, typeWrapper.ClassLoader.ArgTypeListFromSig(m.Sig));
1329 if (typeWrapper.helperTypeBuilder == null)
1330 {
1331 typeWrapper.helperTypeBuilder = typeWrapper.typeBuilder.DefineNestedType("__Helper", TypeAttributes.NestedPublic | TypeAttributes.Class | TypeAttributes.Sealed | TypeAttributes.Abstract);
1332 DeclaringType.Context.AttributeHelper.HideFromJava(typeWrapper.helperTypeBuilder);
1333 }
1334 helper = typeWrapper.helperTypeBuilder.DefineMethod(m.Name, MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.Static, typeWrapper.ClassLoader.RetTypeWrapperFromSig(m.Sig, LoadMode.LoadOrThrow).TypeAsSignatureType, argTypes);
1335 if (m.Attributes != null)
1336 {
1337 foreach (IKVM.Tools.Importer.MapXml.Attribute custattr in m.Attributes)
1338 {
1339 DeclaringType.Context.AttributeHelper.SetCustomAttribute(DeclaringType.ClassLoader, helper, custattr);
1340 }
1341 }
1342 SetParameters(DeclaringType.ClassLoader, helper, m.Parameters);
1343 ilgen = DeclaringType.Context.CodeEmitterFactory.Create(helper);
1344 foreach (IKVM.Tools.Importer.MapXml.Class c in specialCases)
1345 {
1346 var tw = typeWrapper.ClassLoader.LoadClassByName(c.Name);
1347 ilgen.Emit(OpCodes.Ldarg_0);
1348 ilgen.Emit(OpCodes.Isinst, tw.TypeAsTBD);
1349 ilgen.Emit(OpCodes.Dup);
1350 CodeEmitterLabel label = ilgen.DefineLabel();
1351 ilgen.EmitBrfalse(label);
1352 for (int i = 1; i < argTypes.Length; i++)
1353 {
1354 ilgen.EmitLdarg(i);
1355 }
1356 var mw = tw.GetMethod(m.Name, m.Sig, false);
1357 mw.Link();
1358 mw.EmitCallvirt(ilgen);
1359 ilgen.Emit(OpCodes.Ret);
1360 ilgen.MarkLabel(label);
1361 ilgen.Emit(OpCodes.Pop);
1362 }
1363 for (int i = 0; i < argTypes.Length; i++)
1364 {
1365 ilgen.EmitLdarg(i);
1366 }
1367 ilgen.Emit(OpCodes.Callvirt, interfaceMethod);
1368 ilgen.Emit(OpCodes.Ret);
1369 ilgen.DoEmit();
1370 }
1371 mbHelper = helper;
1372 return interfaceMethod;
1373 }
1374 else
1375 {
1376 MethodBuilder mbCore = null;
1377 Type[] paramTypes = typeWrapper.ClassLoader.ArgTypeListFromSig(m.Sig);
1378 Type retType = typeWrapper.ClassLoader.RetTypeWrapperFromSig(m.Sig, LoadMode.LoadOrThrow).TypeAsSignatureType;
1379
1380 if (typeWrapper.shadowType.IsSealed && (m.Modifiers & IKVM.Tools.Importer.MapXml.MapModifiers.Static) == 0)
1381 {
1382 // skip instance methods in sealed types, but we do need to add them to the overriders
1383 if (typeWrapper.BaseTypeWrapper != null && (m.Modifiers & IKVM.Tools.Importer.MapXml.MapModifiers.Private) == 0)
1384 {
1385 RemappedMethodWrapper baseMethod = typeWrapper.BaseTypeWrapper.GetMethod(m.Name, m.Sig, true) as RemappedMethodWrapper;
1386 if (baseMethod != null &&
1387 !baseMethod.IsFinal &&
1388 !baseMethod.IsPrivate &&
1389 (baseMethod.m.Override != null ||
1390 baseMethod.m.Redirect != null ||
1391 baseMethod.m.Body != null ||
1392 baseMethod.m.AlternateBody != null))
1393 {
1394 baseMethod.overriders.Add(typeWrapper);
1395 }
1396 }
1397 }
1398 else
1399 {
1400 MethodInfo overrideMethod = null;
1401 MethodAttributes attr = m.MethodAttributes | MapMethodAccessModifiers(m.Modifiers) | MethodAttributes.HideBySig;
1402 if ((m.Modifiers & IKVM.Tools.Importer.MapXml.MapModifiers.Static) != 0)
1403 {
1404 attr |= MethodAttributes.Static;
1405 }
1406 else if ((m.Modifiers & IKVM.Tools.Importer.MapXml.MapModifiers.Private) == 0 && (m.Modifiers & IKVM.Tools.Importer.MapXml.MapModifiers.Final) == 0)
1407 {
1408 attr |= MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.CheckAccessOnOverride;
1409 if (!typeWrapper.shadowType.IsSealed)
1410 {
1411 MethodInfo autoOverride = typeWrapper.shadowType.GetMethod(m.Name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, paramTypes, null);
1412 if (autoOverride != null && autoOverride.ReturnType == retType && !autoOverride.IsFinal)
1413 {
1414 // the method we're processing is overriding a method in its shadowType (which is the actual base type)
1415 attr &= ~MethodAttributes.NewSlot;
1416 }
1417 }
1418 if (typeWrapper.BaseTypeWrapper != null)
1419 {
1420 RemappedMethodWrapper baseMethod = typeWrapper.BaseTypeWrapper.GetMethod(m.Name, m.Sig, true) as RemappedMethodWrapper;
1421 if (baseMethod != null)
1422 {
1423 baseMethod.overriders.Add(typeWrapper);
1424 if (baseMethod.m.Override != null)
1425 {
1426 overrideMethod = typeWrapper.BaseTypeWrapper.TypeAsTBD.GetMethod(baseMethod.m.Override.Name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, paramTypes, null);
1427 if (overrideMethod == null)
1428 {
1429 throw new InvalidOperationException();
1430 }
1431 }
1432 }
1433 }
1434 }
1435 mbCore = GetDefineMethodHelper().DefineMethod(DeclaringType.ClassLoader.GetTypeWrapperFactory(), typeWrapper.typeBuilder, m.Name, attr);
1436 if (m.Attributes != null)
1437 {
1438 foreach (IKVM.Tools.Importer.MapXml.Attribute custattr in m.Attributes)
1439 {
1440 DeclaringType.Context.AttributeHelper.SetCustomAttribute(DeclaringType.ClassLoader, mbCore, custattr);
1441 }
1442 }
1443 SetParameters(DeclaringType.ClassLoader, mbCore, m.Parameters);
1444 if (overrideMethod != null && !inherited)
1445 {
1446 typeWrapper.typeBuilder.DefineMethodOverride(mbCore, overrideMethod);
1447 }
1448 if (inherited)
1449 {
1450 DeclaringType.Context.AttributeHelper.HideFromReflection(mbCore);
1451 }
1452 AddDeclaredExceptions(DeclaringType.Context, mbCore, m.Throws);
1453 }
1454
1455 if ((m.Modifiers & IKVM.Tools.Importer.MapXml.MapModifiers.Static) == 0 && !IsHideFromJava(m))
1456 {
1457 // instance methods must have an instancehelper method
1458 MethodAttributes attr = MapMethodAccessModifiers(m.Modifiers) | MethodAttributes.HideBySig | MethodAttributes.Static;
1459 // NOTE instancehelpers for protected methods are made internal
1460 // and special cased in DotNetTypeWrapper.LazyPublishMembers
1461 if ((m.Modifiers & IKVM.Tools.Importer.MapXml.MapModifiers.Protected) != 0)
1462 {
1463 attr &= ~MethodAttributes.MemberAccessMask;
1464 attr |= MethodAttributes.Assembly;
1465 }
1466 mbHelper = typeWrapper.typeBuilder.DefineMethod("instancehelper_" + m.Name, attr, CallingConventions.Standard, retType, ArrayUtil.Concat(typeWrapper.shadowType, paramTypes));
1467 if (m.Attributes != null)
1468 {
1469 foreach (IKVM.Tools.Importer.MapXml.Attribute custattr in m.Attributes)
1470 {
1471 DeclaringType.Context.AttributeHelper.SetCustomAttribute(DeclaringType.ClassLoader, mbHelper, custattr);
1472 }
1473 }
1474 IKVM.Tools.Importer.MapXml.Parameter[] parameters;
1475 if (m.Parameters == null)
1476 {
1477 parameters = new IKVM.Tools.Importer.MapXml.Parameter[1];
1478 }
1479 else
1480 {
1481 parameters = new IKVM.Tools.Importer.MapXml.Parameter[m.Parameters.Length + 1];
1482 m.Parameters.CopyTo(parameters, 1);
1483 }
1484 parameters[0] = new IKVM.Tools.Importer.MapXml.Parameter();
1485 parameters[0].Name = "this";
1486 SetParameters(DeclaringType.ClassLoader, mbHelper, parameters);
1487 if (!typeWrapper.IsFinal)
1488 {
1489 DeclaringType.Context.AttributeHelper.SetEditorBrowsableNever(mbHelper);
1490 }
1491 DeclaringType.Context.AttributeHelper.SetModifiers(mbHelper, (Modifiers)m.Modifiers, false);
1492 DeclaringType.Context.AttributeHelper.SetNameSig(mbHelper, m.Name, m.Sig);
1493 AddDeclaredExceptions(DeclaringType.Context, mbHelper, m.Throws);
1494 mbHelper.SetCustomAttribute(new CustomAttributeBuilder(DeclaringType.Context.Resolver.ResolveCoreType(typeof(ObsoleteAttribute).FullName).AsReflection().GetConstructor([DeclaringType.Context.Types.String]), ["This function will be removed from future versions. Please use extension methods from ikvm.extensions namespace instead."]));
1495 }
1496 return mbCore;
1497 }
1498 }
1499
1500 private static bool IsHideFromJava(IKVM.Tools.Importer.MapXml.Method m)
1501 {
1502 if (m.Attributes != null)
1503 {
1504 foreach (MapXml.Attribute attr in m.Attributes)
1505 {
1506 if (attr.Type == "IKVM.Attributes.HideFromJavaAttribute")
1507 {
1508 return true;
1509 }
1510 }
1511 }
1512 return m.Name.StartsWith("__<", StringComparison.Ordinal);
1513 }
1514
1515 internal override void Finish()
1516 {
1517 // TODO we should insert method tracing (if enabled)
1518 Type[] paramTypes = this.GetParametersForDefineMethod();
1519
1520 MethodBuilder mbCore = GetMethod() as MethodBuilder;
1521
1522 // NOTE sealed types don't have instance methods (only instancehelpers)
1523 if (mbCore != null)
1524 {
1525 CodeEmitter ilgen = DeclaringType.Context.CodeEmitterFactory.Create(mbCore);
1526 MethodInfo baseMethod = null;
1527 if (m.Override != null)
1528 {
1529 baseMethod = DeclaringType.TypeAsTBD.GetMethod(m.Override.Name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, paramTypes, null);
1530 if (baseMethod == null)
1531 {
1532 throw new InvalidOperationException();
1533 }
1534 ((TypeBuilder)DeclaringType.TypeAsBaseType).DefineMethodOverride(mbCore, baseMethod);
1535 }
1536 // TODO we need to support ghost (and other funky?) parameter types
1537 if (m.Body != null)
1538 {
1539 // we manually walk the instruction list, because we need to special case the ret instructions
1540 IKVM.Tools.Importer.MapXml.CodeGenContext context = new IKVM.Tools.Importer.MapXml.CodeGenContext(DeclaringType.ClassLoader);
1541 foreach (IKVM.Tools.Importer.MapXml.Instruction instr in m.Body.Instructions)
1542 {
1543 if (instr is IKVM.Tools.Importer.MapXml.Ret)
1544 {
1545 this.ReturnType.EmitConvStackTypeToSignatureType(ilgen, null);
1546 }
1547 instr.Generate(context, ilgen);
1548 }
1549 }
1550 else
1551 {
1552 if (m.Redirect != null && m.Redirect.LineNumber != -1)
1553 ilgen.SetLineNumber((ushort)m.Redirect.LineNumber);
1554
1555 int thisOffset = 0;
1556 if ((m.Modifiers & IKVM.Tools.Importer.MapXml.MapModifiers.Static) == 0)
1557 {
1558 thisOffset = 1;
1559 ilgen.Emit(OpCodes.Ldarg_0);
1560 }
1561 for (int i = 0; i < paramTypes.Length; i++)
1562 {
1563 ilgen.EmitLdarg(i + thisOffset);
1564 }
1565 if (m.Redirect != null)
1566 {
1567 EmitRedirect(DeclaringType.TypeAsTBD, ilgen);
1568 }
1569 else
1570 {
1571 if (baseMethod == null)
1572 {
1573 throw new InvalidOperationException(DeclaringType.Name + "." + m.Name + m.Sig);
1574 }
1575 ilgen.Emit(OpCodes.Call, baseMethod);
1576 }
1577 this.ReturnType.EmitConvStackTypeToSignatureType(ilgen, null);
1578 ilgen.Emit(OpCodes.Ret);
1579 }
1580 ilgen.DoEmit();
1581 if (this.DeclaringType.ClassLoader.EmitStackTraceInfo)
1582 {
1583 ilgen.EmitLineNumberTable(mbCore);
1584 }
1585 }
1586
1587 // NOTE static methods don't have helpers
1588 // NOTE for interface helpers we don't have to do anything,
1589 // because they've already been generated in DoLink
1590 // (currently this only applies to Comparable.compareTo).
1591 if (mbHelper != null && !this.DeclaringType.IsInterface)
1592 {
1593 CodeEmitter ilgen = DeclaringType.Context.CodeEmitterFactory.Create(mbHelper);
1594 // check "this" for null
1595 if (m.Override != null && m.Redirect == null && m.Body == null && m.AlternateBody == null)
1596 {
1597 // we're going to be calling the overridden version, so we don't need the null check
1598 }
1599 else if (!m.NoNullCheck)
1600 {
1601 ilgen.Emit(OpCodes.Ldarg_0);
1602 ilgen.EmitNullCheck();
1603 }
1604 if (mbCore != null &&
1605 (m.Override == null || m.Redirect != null) &&
1606 (m.Modifiers & IKVM.Tools.Importer.MapXml.MapModifiers.Private) == 0 && (m.Modifiers & IKVM.Tools.Importer.MapXml.MapModifiers.Final) == 0)
1607 {
1608 // TODO we should have a way to supress this for overridden methods
1609 ilgen.Emit(OpCodes.Ldarg_0);
1610 ilgen.Emit(OpCodes.Isinst, DeclaringType.TypeAsBaseType);
1611 ilgen.Emit(OpCodes.Dup);
1612 CodeEmitterLabel skip = ilgen.DefineLabel();
1613 ilgen.EmitBrfalse(skip);
1614 for (int i = 0; i < paramTypes.Length; i++)
1615 {
1616 ilgen.EmitLdarg(i + 1);
1617 }
1618 ilgen.Emit(OpCodes.Callvirt, mbCore);
1619 this.ReturnType.EmitConvStackTypeToSignatureType(ilgen, null);
1620 ilgen.Emit(OpCodes.Ret);
1621 ilgen.MarkLabel(skip);
1622 ilgen.Emit(OpCodes.Pop);
1623 }
1624 foreach (RemapperTypeWrapper overrider in overriders)
1625 {
1626 RemappedMethodWrapper mw = (RemappedMethodWrapper)overrider.GetMethod(Name, Signature, false);
1627 if (mw.m.Redirect == null && mw.m.Body == null && mw.m.AlternateBody == null)
1628 {
1629 // the overridden method doesn't actually do anything special (that means it will end
1630 // up calling the .NET method it overrides), so we don't need to special case this
1631 }
1632 else
1633 {
1634 ilgen.Emit(OpCodes.Ldarg_0);
1635 ilgen.Emit(OpCodes.Isinst, overrider.TypeAsTBD);
1636 ilgen.Emit(OpCodes.Dup);
1637 CodeEmitterLabel skip = ilgen.DefineLabel();
1638 ilgen.EmitBrfalse(skip);
1639 for (int i = 0; i < paramTypes.Length; i++)
1640 {
1641 ilgen.EmitLdarg(i + 1);
1642 }
1643 mw.Link();
1644 mw.EmitCallvirtImpl(ilgen, false);
1645 this.ReturnType.EmitConvStackTypeToSignatureType(ilgen, null);
1646 ilgen.Emit(OpCodes.Ret);
1647 ilgen.MarkLabel(skip);
1648 ilgen.Emit(OpCodes.Pop);
1649 }
1650 }
1651 if (m.Body != null || m.AlternateBody != null)
1652 {
1653 IKVM.Tools.Importer.MapXml.InstructionList body = m.AlternateBody == null ? m.Body : m.AlternateBody;
1654 // we manually walk the instruction list, because we need to special case the ret instructions
1655 IKVM.Tools.Importer.MapXml.CodeGenContext context = new IKVM.Tools.Importer.MapXml.CodeGenContext(DeclaringType.ClassLoader);
1656 foreach (IKVM.Tools.Importer.MapXml.Instruction instr in body.Instructions)
1657 {
1658 if (instr is IKVM.Tools.Importer.MapXml.Ret)
1659 {
1660 this.ReturnType.EmitConvStackTypeToSignatureType(ilgen, null);
1661 }
1662 instr.Generate(context, ilgen);
1663 }
1664 }
1665 else
1666 {
1667 if (m.Redirect != null && m.Redirect.LineNumber != -1)
1668 {
1669 ilgen.SetLineNumber((ushort)m.Redirect.LineNumber);
1670 }
1671
1672 var shadowType = ((RemapperTypeWrapper)DeclaringType).shadowType;
1673 for (int i = 0; i < paramTypes.Length + 1; i++)
1674 ilgen.EmitLdarg(i);
1675
1676 if (m.Redirect != null)
1677 {
1678 EmitRedirect(shadowType, ilgen);
1679 }
1680 else if (m.Override != null)
1681 {
1682 var baseMethod = shadowType.GetMethod(m.Override.Name, BindingFlags.Instance | BindingFlags.Public, null, paramTypes, null);
1683 if (baseMethod == null)
1684 throw new InvalidOperationException(DeclaringType.Name + "." + m.Name + m.Sig);
1685
1686 ilgen.Emit(OpCodes.Callvirt, baseMethod);
1687 }
1688 else
1689 {
1690 var baseMethod = DeclaringType.BaseTypeWrapper.GetMethod(Name, Signature, true) as RemappedMethodWrapper;
1691 if (baseMethod == null || baseMethod.m.Override == null)
1692 throw new InvalidOperationException(DeclaringType.Name + "." + m.Name + m.Sig);
1693
1694 var overrideMethod = shadowType.GetMethod(baseMethod.m.Override.Name, BindingFlags.Instance | BindingFlags.Public, null, paramTypes, null);
1695 if (overrideMethod == null)
1696 throw new InvalidOperationException(DeclaringType.Name + "." + m.Name + m.Sig);
1697
1698 ilgen.Emit(OpCodes.Callvirt, overrideMethod);
1699 }
1700
1701 ReturnType.EmitConvStackTypeToSignatureType(ilgen, null);
1702 ilgen.Emit(OpCodes.Ret);
1703 }
1704
1705 ilgen.DoEmit();
1706
1707 if (DeclaringType.ClassLoader.EmitStackTraceInfo)
1708 ilgen.EmitLineNumberTable(mbHelper);
1709 }
1710
1711 // do we need a helper for non-virtual reflection invocation?
1712 if (m.NonVirtualAlternateBody != null || (m.Override != null && overriders.Count > 0))
1713 {
1714 var tw = (RemapperTypeWrapper)DeclaringType;
1715 var mb = tw.typeBuilder.DefineMethod("nonvirtualhelper/" + Name, MethodAttributes.Private | MethodAttributes.Static, ReturnTypeForDefineMethod, ArrayUtil.Concat(tw.TypeAsSignatureType, GetParametersForDefineMethod()));
1716
1717 // apply custom attributes from map XML
1718 if (m.Attributes != null)
1719 foreach (var custattr in m.Attributes)
1720 DeclaringType.Context.AttributeHelper.SetCustomAttribute(DeclaringType.ClassLoader, mb, custattr);
1721
1722 SetParameters(DeclaringType.ClassLoader, mb, m.Parameters);
1723 DeclaringType.Context.AttributeHelper.HideFromJava(mb);
1724
1725 var ilgen = DeclaringType.Context.CodeEmitterFactory.Create(mb);
1726 if (m.NonVirtualAlternateBody != null)
1727 {
1728 m.NonVirtualAlternateBody.Emit(DeclaringType.ClassLoader, ilgen);
1729 }
1730 else
1731 {
1732 var shadowType = ((RemapperTypeWrapper)DeclaringType).shadowType;
1733 var baseMethod = shadowType.GetMethod(m.Override.Name, BindingFlags.Instance | BindingFlags.Public, null, paramTypes, null);
1734 if (baseMethod == null)
1735 throw new InvalidOperationException(DeclaringType.Name + "." + m.Name + m.Sig);
1736
1737 ilgen.Emit(OpCodes.Ldarg_0);
1738 for (int i = 0; i < paramTypes.Length; i++)
1739 ilgen.EmitLdarg(i + 1);
1740
1741 ilgen.Emit(OpCodes.Call, baseMethod);
1742 ilgen.Emit(OpCodes.Ret);
1743 }
1744
1745 ilgen.DoEmit();
1746 }
1747 }
1748
1749 private void EmitRedirect(Type baseType, CodeEmitter ilgen)
1750 {
1751 var redirName = m.Redirect.Name ?? m.Name;
1752 var redirSig = m.Redirect.Sig ?? m.Sig;
1753 var classLoader = DeclaringType.ClassLoader;
1754
1755 // type specified, or class missing, assume loading .NET type
1756 if (m.Redirect.Type != null || m.Redirect.Class == null)
1757 {
1758 var type = m.Redirect.Type != null ? DeclaringType.Context.StaticCompiler.Universe.GetType(m.Redirect.Type, true) : baseType;
1759 var redirParamTypes = classLoader.ArgTypeListFromSig(redirSig);
1760 var mi = type.GetMethod(m.Redirect.Name, redirParamTypes) ?? throw new InvalidOperationException();
1761 ilgen.Emit(OpCodes.Call, mi);
1762 }
1763 else
1764 {
1765 var tw = classLoader.LoadClassByName(m.Redirect.Class);
1766 var mw = tw.GetMethod(redirName, redirSig, false) ?? throw new InvalidOperationException("Missing redirect method: " + tw.Name + "." + redirName + redirSig);
1767 mw.Link();
1768 mw.EmitCall(ilgen);
1769 }
1770 }
1771 }
1772
1773 private static void SetParameters(RuntimeClassLoader loader, MethodBuilder mb, IKVM.Tools.Importer.MapXml.Parameter[] parameters)
1774 {
1775 if (parameters != null)
1776 {
1777 for (int i = 0; i < parameters.Length; i++)
1778 {
1779 ParameterBuilder pb = mb.DefineParameter(i + 1, ParameterAttributes.None, parameters[i].Name);
1780 if (parameters[i].Attributes != null)
1781 {
1782 for (int j = 0; j < parameters[i].Attributes.Length; j++)
1783 {
1784 loader.Context.AttributeHelper.SetCustomAttribute(loader, pb, parameters[i].Attributes[j]);
1785 }
1786 }
1787 }
1788 }
1789 }
1790
1791 internal void Process2ndPassStep1()
1792 {
1793 if (!shadowType.IsSealed)
1794 {
1795 foreach (var ifaceTypeWrapper in interfaceWrappers)
1796 {
1797 typeBuilder.AddInterfaceImplementation(ifaceTypeWrapper.TypeAsBaseType);
1798 }
1799 }
1800 Context.AttributeHelper.SetImplementsAttribute(typeBuilder, interfaceWrappers);
1801 }
1802
1803 internal void Process2ndPassStep2(IKVM.Tools.Importer.MapXml.Root map)
1804 {
1805 var c = classDef;
1806 var tb = typeBuilder;
1807
1808 var fields = new List<RuntimeJavaField>();
1809
1810 // TODO fields should be moved to the RemapperTypeWrapper constructor as well
1811 if (c.Fields != null)
1812 {
1813 foreach (IKVM.Tools.Importer.MapXml.Field f in c.Fields)
1814 {
1815 {
1816 FieldAttributes attr = MapFieldAccessModifiers(f.Modifiers);
1817 if (f.Constant != null)
1818 {
1819 attr |= FieldAttributes.Literal;
1820 }
1821 else if ((f.Modifiers & IKVM.Tools.Importer.MapXml.MapModifiers.Final) != 0)
1822 {
1823 attr |= FieldAttributes.InitOnly;
1824 }
1825 if ((f.Modifiers & IKVM.Tools.Importer.MapXml.MapModifiers.Static) != 0)
1826 {
1827 attr |= FieldAttributes.Static;
1828 }
1829 FieldBuilder fb = tb.DefineField(f.Name, ClassLoader.FieldTypeWrapperFromSig(f.Sig, LoadMode.LoadOrThrow).TypeAsSignatureType, attr);
1830 if (f.Attributes != null)
1831 {
1832 foreach (IKVM.Tools.Importer.MapXml.Attribute custattr in f.Attributes)
1833 {
1834 Context.AttributeHelper.SetCustomAttribute(classLoader, fb, custattr);
1835 }
1836 }
1837 object constant;
1838 if (f.Constant != null)
1839 {
1840 switch (f.Sig[0])
1841 {
1842 case 'J':
1843 constant = long.Parse(f.Constant);
1844 break;
1845 default:
1846 // TODO support other types
1847 throw new NotImplementedException("remapped constant field of type: " + f.Sig);
1848 }
1849 fb.SetConstant(constant);
1850 fields.Add(new RuntimeConstantJavaField(this, ClassLoader.FieldTypeWrapperFromSig(f.Sig, LoadMode.LoadOrThrow), f.Name, f.Sig, (Modifiers)f.Modifiers, fb, constant, MemberFlags.None));
1851 }
1852 else
1853 {
1854 fields.Add(RuntimeJavaField.Create(this, ClassLoader.FieldTypeWrapperFromSig(f.Sig, LoadMode.LoadOrThrow), fb, f.Name, f.Sig, new ExModifiers((Modifiers)f.Modifiers, false)));
1855 }
1856 }
1857 }
1858 }
1859 SetFields(fields.ToArray());
1860 }
1861
1862 internal void Process3rdPass()
1863 {
1864 foreach (RemappedMethodBaseWrapper m in GetMethods())
1865 {
1866 m.Link();
1867 }
1868 }
1869
1870 internal void Process4thPass(ICollection<RemapperTypeWrapper> remappedTypes)
1871 {
1872 foreach (RemappedMethodBaseWrapper m in GetMethods())
1873 {
1874 m.Finish();
1875 }
1876
1877 if (classDef.Clinit != null)
1878 {
1879 MethodBuilder cb = ReflectUtil.DefineTypeInitializer(typeBuilder, classLoader);
1880 CodeEmitter ilgen = Context.CodeEmitterFactory.Create(cb);
1881 // TODO emit code to make sure super class is initialized
1882 classDef.Clinit.Body.Emit(classLoader, ilgen);
1883 ilgen.DoEmit();
1884 }
1885
1886 // FXBUG because the AppDomain.TypeResolve event doesn't work correctly for inner classes,
1887 // we need to explicitly finish the interface we implement (if they are ghosts, we need the nested __Interface type)
1888 if (classDef.Interfaces != null)
1889 {
1890 foreach (IKVM.Tools.Importer.MapXml.Implements iface in classDef.Interfaces)
1891 {
1892 ClassLoader.LoadClassByName(iface.Class).Finish();
1893 }
1894 }
1895
1896 CreateShadowInstanceOf(remappedTypes);
1897 CreateShadowCheckCast(remappedTypes);
1898
1899 if (!shadowType.IsInterface)
1900 {
1901 // For all inherited methods, we emit a method that hides the inherited method and
1902 // annotate it with EditorBrowsableAttribute(EditorBrowsableState.Never) to make
1903 // sure the inherited methods don't show up in Intellisense.
1904 var methods = new Dictionary<string, MethodBuilder>();
1905 foreach (var mw in GetMethods())
1906 {
1907 var mb = mw.GetMethod() as MethodBuilder;
1908 if (mb != null)
1909 methods.Add(MakeMethodKey(mb), mb);
1910 }
1911
1912 foreach (var mi in typeBuilder.BaseType.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy))
1913 {
1914 string key = MakeMethodKey(mi);
1915 if (!methods.ContainsKey(key))
1916 {
1917 ParameterInfo[] paramInfo = mi.GetParameters();
1918 Type[] paramTypes = new Type[paramInfo.Length];
1919 for (int i = 0; i < paramInfo.Length; i++)
1920 {
1921 paramTypes[i] = paramInfo[i].ParameterType;
1922 }
1923 MethodBuilder mb = typeBuilder.DefineMethod(mi.Name, mi.Attributes & (MethodAttributes.MemberAccessMask | MethodAttributes.SpecialName | MethodAttributes.Static), mi.ReturnType, paramTypes);
1924 Context.AttributeHelper.HideFromJava(mb);
1925 Context.AttributeHelper.SetEditorBrowsableNever(mb);
1926 CodeEmitter ilgen = Context.CodeEmitterFactory.Create(mb);
1927 for (int i = 0; i < paramTypes.Length; i++)
1928 {
1929 ilgen.EmitLdarg(i);
1930 }
1931 if (!mi.IsStatic)
1932 {
1933 ilgen.EmitLdarg(paramTypes.Length);
1934 ilgen.Emit(OpCodes.Callvirt, mi);
1935 }
1936 else
1937 {
1938 ilgen.Emit(OpCodes.Call, mi);
1939 }
1940 ilgen.Emit(OpCodes.Ret);
1941 ilgen.DoEmit();
1942 methods[key] = mb;
1943 }
1944 }
1945
1946 foreach (var pi in typeBuilder.BaseType.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static))
1947 {
1948 ParameterInfo[] paramInfo = pi.GetIndexParameters();
1949 Type[] paramTypes = new Type[paramInfo.Length];
1950 for (int i = 0; i < paramInfo.Length; i++)
1951 {
1952 paramTypes[i] = paramInfo[i].ParameterType;
1953 }
1954 PropertyBuilder pb = typeBuilder.DefineProperty(pi.Name, PropertyAttributes.None, pi.PropertyType, paramTypes);
1955 if (pi.GetGetMethod() != null)
1956 {
1957 pb.SetGetMethod(methods[MakeMethodKey(pi.GetGetMethod())]);
1958 }
1959 if (pi.GetSetMethod() != null)
1960 {
1961 pb.SetSetMethod(methods[MakeMethodKey(pi.GetSetMethod())]);
1962 }
1963 Context.AttributeHelper.SetEditorBrowsableNever(pb);
1964 }
1965 }
1966
1967 typeBuilder.CreateType();
1968 if (helperTypeBuilder != null)
1969 helperTypeBuilder.CreateType();
1970 }
1971
1972 static string MakeMethodKey(MethodInfo method)
1973 {
1974 var sb = new ValueStringBuilder(method.ReturnType.AssemblyQualifiedName.Length + 1 + method.Name.Length);
1975 sb.Append(method.ReturnType.AssemblyQualifiedName);
1976 sb.Append(":");
1977 sb.Append(method.Name);
1978
1979 var paramInfo = method.GetParameters();
1980 for (int i = 0; i < paramInfo.Length; i++)
1981 {
1982 sb.Append(":");
1983 sb.Append(paramInfo[i].ParameterType.AssemblyQualifiedName);
1984 }
1985
1986 return sb.ToString();
1987 }
1988
1989 void CreateShadowInstanceOf(ICollection<RemapperTypeWrapper> remappedTypes)
1990 {
1991 // FXBUG .NET 1.1 doesn't allow static methods on interfaces
1992 if (typeBuilder.IsInterface)
1993 return;
1994
1995 var attr = MethodAttributes.SpecialName | MethodAttributes.Public | MethodAttributes.Static;
1996 var mb = typeBuilder.DefineMethod("__<instanceof>", attr, Context.Types.Boolean, [Context.Types.Object]);
1997 Context.AttributeHelper.HideFromJava(mb);
1998 Context.AttributeHelper.SetEditorBrowsableNever(mb);
1999 var ilgen = Context.CodeEmitterFactory.Create(mb);
2000
2001 ilgen.Emit(OpCodes.Ldarg_0);
2002 ilgen.Emit(OpCodes.Isinst, shadowType);
2003 var retFalse = ilgen.DefineLabel();
2004 ilgen.EmitBrfalse(retFalse);
2005
2006 if (!shadowType.IsSealed)
2007 {
2008 ilgen.Emit(OpCodes.Ldarg_0);
2009 ilgen.Emit(OpCodes.Isinst, typeBuilder);
2010 ilgen.EmitBrtrue(retFalse);
2011 }
2012
2013 if (shadowType == Context.Types.Object)
2014 {
2015 ilgen.Emit(OpCodes.Ldarg_0);
2016 ilgen.Emit(OpCodes.Isinst, Context.Types.Array);
2017 ilgen.EmitBrtrue(retFalse);
2018 }
2019
2020 foreach (RemapperTypeWrapper r in remappedTypes)
2021 {
2022 if (!r.shadowType.IsInterface && r.shadowType.IsSubclassOf(shadowType))
2023 {
2024 ilgen.Emit(OpCodes.Ldarg_0);
2025 ilgen.Emit(OpCodes.Isinst, r.shadowType);
2026 ilgen.EmitBrtrue(retFalse);
2027 }
2028 }
2029 ilgen.Emit(OpCodes.Ldc_I4_1);
2030 ilgen.Emit(OpCodes.Ret);
2031
2032 ilgen.MarkLabel(retFalse);
2033 ilgen.Emit(OpCodes.Ldc_I4_0);
2034 ilgen.Emit(OpCodes.Ret);
2035
2036 ilgen.DoEmit();
2037 }
2038
2039 private void CreateShadowCheckCast(ICollection<RemapperTypeWrapper> remappedTypes)
2040 {
2041 // FXBUG .NET 1.1 doesn't allow static methods on interfaces
2042 if (typeBuilder.IsInterface)
2043 {
2044 return;
2045 }
2046 MethodAttributes attr = MethodAttributes.SpecialName | MethodAttributes.Public | MethodAttributes.Static;
2047 MethodBuilder mb = typeBuilder.DefineMethod("__<checkcast>", attr, shadowType, new Type[] { Context.Types.Object });
2048 Context.AttributeHelper.HideFromJava(mb);
2049 Context.AttributeHelper.SetEditorBrowsableNever(mb);
2050 CodeEmitter ilgen = Context.CodeEmitterFactory.Create(mb);
2051
2052 CodeEmitterLabel fail = ilgen.DefineLabel();
2053 bool hasfail = false;
2054
2055 if (!shadowType.IsSealed)
2056 {
2057 ilgen.Emit(OpCodes.Ldarg_0);
2058 ilgen.Emit(OpCodes.Isinst, typeBuilder);
2059 ilgen.EmitBrtrue(fail);
2060 hasfail = true;
2061 }
2062
2063 if (shadowType == Context.Types.Object)
2064 {
2065 ilgen.Emit(OpCodes.Ldarg_0);
2066 ilgen.Emit(OpCodes.Isinst, Context.Types.Array);
2067 ilgen.EmitBrtrue(fail);
2068 hasfail = true;
2069 }
2070
2071 foreach (RemapperTypeWrapper r in remappedTypes)
2072 {
2073 if (!r.shadowType.IsInterface && r.shadowType.IsSubclassOf(shadowType))
2074 {
2075 ilgen.Emit(OpCodes.Ldarg_0);
2076 ilgen.Emit(OpCodes.Isinst, r.shadowType);
2077 ilgen.EmitBrtrue(fail);
2078 hasfail = true;
2079 }
2080 }
2081 ilgen.Emit(OpCodes.Ldarg_0);
2082 ilgen.EmitCastclass(shadowType);
2083 ilgen.Emit(OpCodes.Ret);
2084
2085 if (hasfail)
2086 {
2087 ilgen.MarkLabel(fail);
2088 ilgen.ThrowException(Context.Resolver.ResolveCoreType(typeof(InvalidCastException).FullName).AsReflection());
2089 }
2090
2091 ilgen.DoEmit();
2092 }
2093
2094 internal override MethodBase LinkMethod(RuntimeJavaMethod mw)
2095 {
2096 return ((RemappedMethodBaseWrapper)mw).DoLink();
2097 }
2098
2099 internal override RuntimeJavaType[] Interfaces
2100 {
2101 get
2102 {
2103 return interfaceWrappers;
2104 }
2105 }
2106
2107 internal override Type TypeAsTBD
2108 {
2109 get
2110 {
2111 return shadowType;
2112 }
2113 }
2114
2115 internal override Type TypeAsBaseType
2116 {
2117 get
2118 {
2119 return typeBuilder;
2120 }
2121 }
2122
2123 internal override bool IsMapUnsafeException
2124 {
2125 get
2126 {
2127 // any remapped exceptions are automatically unsafe
2128 return shadowType == Context.Types.Exception || shadowType.IsSubclassOf(Context.Types.Exception);
2129 }
2130 }
2131
2132 internal override bool IsFastClassLiteralSafe
2133 {
2134 get { return true; }
2135 }
2136 }
2137
2138 internal static void AddDeclaredExceptions(RuntimeContext context, MethodBuilder mb, IKVM.Tools.Importer.MapXml.Throws[] throws)
2139 {
2140 if (throws != null)
2141 {
2142 string[] exceptions = new string[throws.Length];
2143 for (int i = 0; i < exceptions.Length; i++)
2144 {
2145 exceptions[i] = throws[i].Class;
2146 }
2147 context.AttributeHelper.SetThrowsAttribute(mb, exceptions);
2148 }
2149 }
2150
2151 internal void EmitRemappedTypes()
2152 {
2153 Diagnostics.GenericCompilerInfo("Emit remapped types");
2154
2155 assemblyAttributes = map.Assembly.Attributes;
2156
2157 if (map.Assembly.Classes != null)
2158 {
2159 // 1st pass, put all types in remapped to make them loadable
2160 bool hasRemappedTypes = false;
2161 foreach (IKVM.Tools.Importer.MapXml.Class c in map.Assembly.Classes)
2162 {
2163 if (c.Shadows != null)
2164 {
2165 if (classes.ContainsKey(c.Name))
2166 {
2167 Diagnostics.DuplicateClassName(c.Name);
2168 }
2169
2170 remapped.Add(c.Name, new RemapperTypeWrapper(Context, this, c, map));
2171 hasRemappedTypes = true;
2172 }
2173 }
2174
2175 if (hasRemappedTypes)
2176 {
2177 SetupGhosts(map);
2178 foreach (IKVM.Tools.Importer.MapXml.Class c in map.Assembly.Classes)
2179 {
2180 if (c.Shadows != null)
2181 {
2182 remapped[c.Name].LoadInterfaces(c);
2183 }
2184 }
2185 }
2186 }
2187 }
2188
2189 internal void EmitRemappedTypes2ndPass()
2190 {
2191 if (map != null && map.Assembly != null && map.Assembly.Classes != null)
2192 {
2193 // 2nd pass, resolve interfaces, publish methods/fields
2194 foreach (IKVM.Tools.Importer.MapXml.Class c in map.Assembly.Classes)
2195 {
2196 if (c.Shadows != null)
2197 {
2198 RemapperTypeWrapper typeWrapper = remapped[c.Name];
2199 typeWrapper.Process2ndPassStep1();
2200 }
2201 }
2202 foreach (IKVM.Tools.Importer.MapXml.Class c in map.Assembly.Classes)
2203 {
2204 if (c.Shadows != null)
2205 {
2206 RemapperTypeWrapper typeWrapper = remapped[c.Name];
2207 typeWrapper.Process2ndPassStep2(map);
2208 }
2209 }
2210 }
2211 }
2212
2213 internal bool IsMapUnsafeException(RuntimeJavaType tw)
2214 {
2215 if (mappedExceptions != null)
2216 for (int i = 0; i < mappedExceptions.Length; i++)
2217 if (mappedExceptions[i].IsSubTypeOf(tw) || (mappedExceptionsAllSubClasses[i] && tw.IsSubTypeOf(mappedExceptions[i])))
2218 return true;
2219
2220 return false;
2221 }
2222
2223 internal void LoadMappedExceptions(MapXml.Root map)
2224 {
2225 if (map.ExceptionMappings.Length > 0)
2226 {
2227 mappedExceptionsAllSubClasses = new bool[map.ExceptionMappings.Length];
2228 mappedExceptions = new RuntimeJavaType[map.ExceptionMappings.Length];
2229 for (int i = 0; i < mappedExceptions.Length; i++)
2230 {
2231 var dst = map.ExceptionMappings[i].Destination;
2232 if (dst[0] == '*')
2233 {
2234 mappedExceptionsAllSubClasses[i] = true;
2235 dst = dst.Substring(1);
2236 }
2237
2238 mappedExceptions[i] = LoadClassByName(dst);
2239 }
2240
2241 // HACK we need to find the <exceptionMapping /> element and bind it
2242 foreach (var c in map.Assembly.Classes)
2243 foreach (var m in c.Methods)
2244 if (m.Body != null)
2245 foreach (var instr in m.Body.Instructions)
2246 if (instr is MapXml.EmitExceptionMapping eem)
2247 eem.mapping = map.ExceptionMappings;
2248 }
2249 }
2250
2251 internal sealed class ExceptionMapEmitter
2252 {
2253
2254 readonly RuntimeContext rcontext;
2255 readonly MapXml.ExceptionMapping[] map;
2256
2262 internal ExceptionMapEmitter(RuntimeContext context, MapXml.ExceptionMapping[] map)
2263 {
2264 this.rcontext = context;
2265 this.map = map;
2266 }
2267
2268 internal void Emit(MapXml.CodeGenContext context, CodeEmitter ilgen)
2269 {
2270 var mwSuppressFillInStackTrace = rcontext.JavaBase.TypeOfjavaLangThrowable.GetMethod("__<suppressFillInStackTrace>", "()V", false);
2271 mwSuppressFillInStackTrace.Link();
2272 ilgen.Emit(OpCodes.Ldarg_0);
2273 ilgen.Emit(OpCodes.Callvirt, rcontext.CompilerFactory.GetTypeMethod);
2274
2275 for (int i = 0; i < map.Length; i++)
2276 {
2277 ilgen.Emit(OpCodes.Dup);
2278 ilgen.Emit(OpCodes.Ldtoken, rcontext.Resolver.ResolveCoreType(map[i].Source).AsReflection());
2279 ilgen.Emit(OpCodes.Call, rcontext.CompilerFactory.GetTypeFromHandleMethod);
2280 ilgen.Emit(OpCodes.Ceq);
2281 var label = ilgen.DefineLabel();
2282 ilgen.EmitBrfalse(label);
2283 ilgen.Emit(OpCodes.Pop);
2284 if (map[i].Code != null)
2285 {
2286 ilgen.Emit(OpCodes.Ldarg_0);
2287
2288 if (map[i].Code.Instructions.Length > 0)
2289 {
2290 foreach (var instr in map[i].Code.Instructions)
2291 {
2292 var newobj = instr as MapXml.NewObj;
2293 if (newobj != null && newobj.Class != null && context.ClassLoader.LoadClassByName(newobj.Class).IsSubTypeOf(rcontext.JavaBase.TypeOfjavaLangThrowable))
2294 mwSuppressFillInStackTrace.EmitCall(ilgen);
2295
2296 instr.Generate(context, ilgen);
2297 }
2298 }
2299
2300 ilgen.Emit(OpCodes.Ret);
2301 }
2302 else
2303 {
2304 var tw = context.ClassLoader.LoadClassByName(map[i].Destination);
2305 var mw = tw.GetMethod("<init>", "()V", false);
2306 mw.Link();
2307 mwSuppressFillInStackTrace.EmitCall(ilgen);
2308 mw.EmitNewobj(ilgen);
2309 ilgen.Emit(OpCodes.Ret);
2310 }
2311
2312 ilgen.MarkLabel(label);
2313 }
2314
2315 ilgen.Emit(OpCodes.Pop);
2316 ilgen.Emit(OpCodes.Ldarg_0);
2317 ilgen.Emit(OpCodes.Ret);
2318 }
2319 }
2320
2321 internal void LoadMapXml()
2322 {
2323 if (map.Assembly.Classes.Length > 0)
2324 {
2325 mapxml_Classes = new Dictionary<string, MapXml.Class>();
2326 mapxml_MethodBodies = new Dictionary<MethodKey, MapXml.InstructionList>();
2327 mapxml_ReplacedMethods = new Dictionary<MethodKey, MapXml.ReplaceMethodCall[]>();
2328 mapxml_MethodPrologues = new Dictionary<MethodKey, MapXml.InstructionList>();
2329
2330 foreach (var c in map.Assembly.Classes)
2331 {
2332 // if it is not a remapped type, it must be a container for native, patched or augmented methods
2333 if (c.Shadows == null)
2334 {
2335 string className = c.Name;
2336 mapxml_Classes.Add(className, c);
2337 AddMapXmlMethods(className, c.Constructors);
2338 AddMapXmlMethods(className, c.Methods);
2339 if (c.Clinit != null)
2340 AddMapXmlMethod(className, c.Clinit);
2341 }
2342 }
2343 }
2344 }
2345
2346 private void AddMapXmlMethods(string className, MapXml.MethodBase[] methods)
2347 {
2348 if (methods != null)
2349 foreach (var method in methods)
2350 AddMapXmlMethod(className, method);
2351 }
2352
2353 private void AddMapXmlMethod(string className, MapXml.MethodBase method)
2354 {
2355 if (method.Body != null)
2356 mapxml_MethodBodies.Add(method.ToMethodKey(className), method.Body);
2357
2358 if (method.ReplaceMethodCalls.Length > 0)
2359 mapxml_ReplacedMethods.Add(method.ToMethodKey(className), method.ReplaceMethodCalls);
2360
2361 if (method.Prologue != null)
2362 mapxml_MethodPrologues.Add(method.ToMethodKey(className), method.Prologue);
2363 }
2364
2365 internal MapXml.InstructionList GetMethodPrologue(MethodKey method)
2366 {
2367 if (mapxml_MethodPrologues == null)
2368 return null;
2369
2370 mapxml_MethodPrologues.TryGetValue(method, out var prologue);
2371 return prologue;
2372 }
2373
2374 internal MapXml.ReplaceMethodCall[] GetReplacedMethodsFor(RuntimeJavaMethod mw)
2375 {
2376 if (mapxml_ReplacedMethods == null)
2377 return null;
2378
2379 mapxml_ReplacedMethods.TryGetValue(new MethodKey(mw.DeclaringType.Name, mw.Name, mw.Signature), out var rmc);
2380 return rmc;
2381 }
2382
2383 internal Dictionary<string, MapXml.Class> GetMapXmlClasses()
2384 {
2385 return mapxml_Classes;
2386 }
2387
2388 internal Dictionary<MethodKey, MapXml.InstructionList> GetMapXmlMethodBodies()
2389 {
2390 return mapxml_MethodBodies;
2391 }
2392
2393 internal MapXml.Parameter[] GetXmlMapParameters(string classname, string method, string sig)
2394 {
2395 if (mapxml_Classes != null)
2396 {
2397 if (mapxml_Classes.TryGetValue(classname, out var clazz))
2398 {
2399 if (method == "<init>" && clazz.Constructors.Length > 0)
2400 {
2401 for (int i = 0; i < clazz.Constructors.Length; i++)
2402 if (clazz.Constructors[i].Sig == sig)
2403 return clazz.Constructors[i].Parameters;
2404 }
2405 else if (clazz.Methods.Length > 0)
2406 {
2407 for (int i = 0; i < clazz.Methods.Length; i++)
2408 if (clazz.Methods[i].Name == method && clazz.Methods[i].Sig == sig)
2409 return clazz.Methods[i].Parameters;
2410 }
2411 }
2412 }
2413
2414 return null;
2415 }
2416
2417 internal bool IsGhost(RuntimeJavaType tw)
2418 {
2419 return ghosts != null && tw.IsInterface && ghosts.ContainsKey(tw.Name);
2420 }
2421
2422 void SetupGhosts(MapXml.Root map)
2423 {
2424 ghosts = new Dictionary<string, List<RuntimeJavaType>>();
2425
2426 // find the ghost interfaces
2427 foreach (var c in map.Assembly.Classes)
2428 {
2429 if (c.Shadows != null && c.Interfaces.Length > 0)
2430 {
2431 // NOTE we don't support interfaces that inherit from other interfaces
2432 // (actually, if they are explicitly listed it would probably work)
2433 var typeWrapper = FindLoadedClass(c.Name);
2434
2435 foreach (var iface in c.Interfaces)
2436 {
2437 var ifaceWrapper = FindLoadedClass(iface.Class);
2438 if (ifaceWrapper == null || !ifaceWrapper.TypeAsTBD.IsAssignableFrom(typeWrapper.TypeAsTBD))
2439 AddGhost(iface.Class, typeWrapper);
2440 }
2441 }
2442 }
2443
2444 // we manually add the array ghost interfaces
2445 var array = Context.ClassLoaderFactory.GetJavaTypeFromType(Context.Types.Array);
2446 AddGhost("java.io.Serializable", array);
2447 AddGhost("java.lang.Cloneable", array);
2448 }
2449
2450 private void AddGhost(string interfaceName, RuntimeJavaType implementer)
2451 {
2452 if (!ghosts.TryGetValue(interfaceName, out var list))
2453 {
2454 list = new List<RuntimeJavaType>();
2455 ghosts[interfaceName] = list;
2456 }
2457
2458 list.Add(implementer);
2459 }
2460
2461 internal RuntimeJavaType[] GetGhostImplementers(RuntimeJavaType wrapper)
2462 {
2463 return ghosts.TryGetValue(wrapper.Name, out var list) ? list.ToArray() : Array.Empty<RuntimeJavaType>();
2464 }
2465
2466 internal void FinishRemappedTypes()
2467 {
2468 // 3rd pass, link the methods. Note that a side effect of the linking is the
2469 // twiddling with the overriders array in the base methods, so we need to do this
2470 // as a separate pass before we compile the methods
2471 foreach (var typeWrapper in remapped.Values)
2472 typeWrapper.Process3rdPass();
2473
2474 // 4th pass, implement methods/fields and bake the type
2475 foreach (var typeWrapper in remapped.Values)
2476 typeWrapper.Process4thPass(remapped.Values);
2477
2478 if (assemblyAttributes != null)
2479 foreach (MapXml.Attribute attr in assemblyAttributes)
2480 Context.AttributeHelper.SetCustomAttribute(this, assemblyBuilder, attr);
2481 }
2482
2483 private static bool IsSigned(Assembly asm)
2484 {
2485 byte[] key = asm.GetName().GetPublicKey();
2486 return key != null && key.Length != 0;
2487 }
2488
2489 internal static int Compile(ImportContext importer, RuntimeContext context, StaticCompiler compiler, IDiagnosticHandler diagnostics, string runtimeAssembly, List<ImportState> optionsList)
2490 {
2491 try
2492 {
2493 compiler.runtimeAssembly = compiler.LoadFile(runtimeAssembly ?? Path.Combine(Path.GetDirectoryName(typeof(ImportClassLoader).Assembly.Location), "IKVM.Runtime.dll"));
2494 }
2495 catch (FileNotFoundException)
2496 {
2497 // runtime assembly is required
2498 if (compiler.runtimeAssembly == null)
2499 throw new FatalCompilerErrorException(DiagnosticEvent.RuntimeNotFound());
2500
2501 // some unknown error
2502 throw new FatalCompilerErrorException(DiagnosticEvent.FileNotFound(compiler.runtimeAssembly.FullName));
2503 }
2504
2505 diagnostics.GenericCompilerInfo($"Loaded runtime assembly: {compiler.runtimeAssembly.FullName}");
2506
2507 var loaders = new List<ImportClassLoader>();
2508 foreach (var options in optionsList)
2509 {
2510 int rc = CreateCompiler(context, compiler, diagnostics, options, out var loader);
2511 if (rc != 0)
2512 return rc;
2513
2514 loaders.Add(loader);
2515 options.sharedclassloader?.Add(loader);
2516 }
2517
2518 foreach (var loader1 in loaders)
2519 foreach (var loader2 in loaders)
2520 if (loader1 != loader2 && (loader1.state.crossReferenceAllPeers || (loader1.state.peerReferences != null && Array.IndexOf(loader1.state.peerReferences, loader2.state.assembly) != -1)))
2521 loader1.AddReference(loader2);
2522
2523 foreach (var loader in loaders)
2524 loader.CompilePass0();
2525
2526 var mainAssemblyTypes = new Dictionary<ImportClassLoader, Type>();
2527 foreach (var loader in loaders)
2528 {
2529 if (loader.state.sharedclassloader != null)
2530 {
2531 if (!mainAssemblyTypes.TryGetValue(loader.state.sharedclassloader[0], out var mainAssemblyType))
2532 {
2533 var tb = loader.state.sharedclassloader[0].GetTypeWrapperFactory().ModuleBuilder.DefineType("__<MainAssembly>", TypeAttributes.NotPublic | TypeAttributes.Abstract | TypeAttributes.SpecialName);
2534 loader.Context.AttributeHelper.HideFromJava(tb);
2535 mainAssemblyType = tb.CreateType();
2536 mainAssemblyTypes.Add(loader.state.sharedclassloader[0], mainAssemblyType);
2537 }
2538 if (loader.state.sharedclassloader[0] != loader)
2539 {
2540 ((AssemblyBuilder)loader.GetTypeWrapperFactory().ModuleBuilder.Assembly).__AddTypeForwarder(mainAssemblyType);
2541 }
2542 }
2543
2544 loader.CompilePass1();
2545 }
2546
2547 foreach (var loader in loaders)
2548 {
2549 loader.CompilePass2();
2550 }
2551
2552 if (context.Bootstrap)
2553 foreach (var loader in loaders)
2554 loader.EmitRemappedTypes2ndPass();
2555
2556 foreach (var loader in loaders)
2557 {
2558 int rc = loader.CompilePass3();
2559 if (rc != 0)
2560 return rc;
2561 }
2562
2563 diagnostics.GenericCompilerInfo("CompilerClassLoader.Save...");
2564
2565 foreach (var loader in loaders)
2566 loader.PrepareSave();
2567
2568 if (compiler.errorCount > 0)
2569 return 1;
2570
2571 foreach (ImportClassLoader loader in loaders)
2572 loader.Save();
2573
2574 return compiler.errorCount == 0 ? 0 : 1;
2575 }
2576
2577 static int CreateCompiler(RuntimeContext context, StaticCompiler compiler, IDiagnosticHandler diagnostics, ImportState options, out ImportClassLoader loader)
2578 {
2579 diagnostics.GenericCompilerInfo($"JVM.Compile path: {options.path}, assembly: {options.assembly}");
2580
2581 AssemblyName runtimeAssemblyName = compiler.runtimeAssembly.GetName();
2582 bool allReferencesAreStrongNamed = IsSigned(compiler.runtimeAssembly);
2583 List<Assembly> references = new List<Assembly>();
2584 foreach (Assembly reference in options.references ?? new Assembly[0])
2585 {
2586 references.Add(reference);
2587 allReferencesAreStrongNamed &= IsSigned(reference);
2588 diagnostics.GenericCompilerInfo($"Loaded reference assembly: {reference.FullName}");
2589
2590 // if it's an IKVM compiled assembly, make sure that it was compiled
2591 // against same version of the runtime
2592 foreach (AssemblyName asmref in reference.GetReferencedAssemblies())
2593 {
2594 if (asmref.Name == runtimeAssemblyName.Name)
2595 {
2596 if (IsSigned(compiler.runtimeAssembly))
2597 {
2598 // TODO we really should support binding redirects here to allow different revisions to be mixed
2599 if (asmref.FullName != runtimeAssemblyName.FullName)
2600 {
2601 throw new FatalCompilerErrorException(DiagnosticEvent.RuntimeMismatch(reference.Location, runtimeAssemblyName.FullName, asmref.FullName));
2602 }
2603 }
2604 else
2605 {
2606 if (asmref.GetPublicKeyToken() != null && asmref.GetPublicKeyToken().Length != 0)
2607 {
2608 throw new FatalCompilerErrorException(DiagnosticEvent.RuntimeMismatch(reference.Location, runtimeAssemblyName.FullName, asmref.FullName));
2609 }
2610 }
2611 }
2612 }
2613 }
2614
2615 diagnostics.GenericCompilerInfo("Parsing class files");
2616
2617 // map the class names to jar entries
2618 Dictionary<string, Jar.Item> h = new Dictionary<string, Jar.Item>();
2619 List<string> classNames = new List<string>();
2620 foreach (Jar jar in options.jars)
2621 {
2622 if (options.IsResourcesJar(jar))
2623 {
2624 continue;
2625 }
2626 foreach (Jar.Item item in jar)
2627 {
2628 string name = item.Name;
2629 if (name.EndsWith(".class", StringComparison.Ordinal)
2630 && name.Length > 6
2631 && name.IndexOf('.') == name.Length - 6)
2632 {
2633 string className = name.Substring(0, name.Length - 6).Replace('/', '.');
2634 if (h.ContainsKey(className))
2635 {
2636 diagnostics.DuplicateClassName(className);
2637 Jar.Item itemRef = h[className];
2638 if ((options.classesJar != -1 && itemRef.Jar == options.jars[options.classesJar]) || jar != itemRef.Jar)
2639 {
2640 // the previous class stays, because it was either in an earlier jar or we're processing the classes.jar
2641 // which contains the classes loaded from the file system (where the first encountered class wins)
2642 continue;
2643 }
2644 else
2645 {
2646 // we have a jar that contains multiple entries with the same name, the last one wins
2647 h.Remove(className);
2648 classNames.Remove(className);
2649 }
2650 }
2651 h.Add(className, item);
2652 classNames.Add(className);
2653 }
2654 }
2655 }
2656
2657 if (options.assemblyAttributeAnnotations == null)
2658 {
2659 // look for "assembly" type that acts as a placeholder for assembly attributes
2660 if (h.TryGetValue("assembly", out var assemblyType))
2661 {
2662 try
2663 {
2664 using var f = new IKVM.Runtime.ClassFile(context, diagnostics, IKVM.ByteCode.Decoding.ClassFile.Read(assemblyType.GetData()), null, ClassFileParseOptions.StaticImport, null);
2665
2666 // NOTE the "assembly" type in the unnamed package is a magic type
2667 // that acts as the placeholder for assembly attributes
2668 if (f.Name == "assembly" && f.Annotations != null)
2669 {
2670 options.assemblyAttributeAnnotations = f.Annotations;
2671 // HACK remove "assembly" type that exists only as a placeholder for assembly attributes
2672 h.Remove(f.Name);
2673 assemblyType.Remove();
2674 diagnostics.LegacyAssemblyAttributesFound();
2675 }
2676 }
2677 catch (ByteCodeException)
2678 {
2679
2680 }
2681 catch (ClassFormatError)
2682 {
2683
2684 }
2685 }
2686 }
2687
2688 // now look for a main method
2689 if (options.mainClass == null && (options.guessFileKind || options.target != PEFileKinds.Dll))
2690 {
2691 foreach (string className in classNames)
2692 {
2693 try
2694 {
2695 using var f = new IKVM.Runtime.ClassFile(context, diagnostics, IKVM.ByteCode.Decoding.ClassFile.Read(h[className].GetData()), null, ClassFileParseOptions.StaticImport, null);
2696 if (f.Name == className)
2697 {
2698 foreach (var m in f.Methods)
2699 {
2700 if (m.IsPublic && m.IsStatic && m.Name == "main" && m.Signature == "([Ljava.lang.String;)V")
2701 {
2702 diagnostics.MainMethodFound(f.Name);
2703 options.mainClass = f.Name;
2704 goto break_outer;
2705 }
2706 }
2707 }
2708 }
2709 catch (ClassFormatError)
2710 {
2711
2712 }
2713 }
2714 break_outer:;
2715 }
2716
2717 if (options.guessFileKind && options.mainClass == null)
2718 {
2719 options.target = PEFileKinds.Dll;
2720 }
2721
2722 if (options.target != PEFileKinds.Dll && options.mainClass == null)
2723 {
2724 throw new FatalCompilerErrorException(DiagnosticEvent.ExeRequiresMainClass());
2725 }
2726
2727 if (options.target == PEFileKinds.Dll && options.props.Count != 0)
2728 {
2729 throw new FatalCompilerErrorException(DiagnosticEvent.PropertiesRequireExe());
2730 }
2731
2732 if (options.path == null)
2733 {
2734 if (options.target == PEFileKinds.Dll)
2735 {
2736 if (options.targetIsModule)
2737 {
2738 options.path = ImportContext.GetFileInfo(options.assembly + ".netmodule");
2739 }
2740 else
2741 {
2742 options.path = ImportContext.GetFileInfo(options.assembly + ".dll");
2743 }
2744 }
2745 else
2746 {
2747 options.path = ImportContext.GetFileInfo(options.assembly + ".exe");
2748 }
2749
2750 diagnostics.OutputFileIs(options.path.ToString());
2751 }
2752
2753 if (options.targetIsModule)
2754 {
2755 if (options.classLoader != null)
2756 {
2757 throw new FatalCompilerErrorException(DiagnosticEvent.ModuleCannotHaveClassLoader());
2758 }
2759 // TODO if we're overwriting a user specified assembly name, we need to emit a warning
2760 options.assembly = options.path.Name;
2761 }
2762
2763 diagnostics.GenericCompilerInfo("Constructing compiler");
2764 var referencedAssemblies = new List<RuntimeAssemblyClassLoader>(references.Count);
2765 for (int i = 0; i < references.Count; i++)
2766 {
2767 // if reference is to base assembly, set it explicitly for resolution
2768 if (compiler.baseAssembly == null && options.bootstrap == false && IsBaseAssembly(context, references[i]))
2769 compiler.baseAssembly = references[i];
2770
2771 var acl = context.AssemblyClassLoaderFactory.FromAssembly(references[i]);
2772 if (referencedAssemblies.Contains(acl))
2773 diagnostics.DuplicateAssemblyReference(acl.MainAssembly.FullName);
2774
2775 referencedAssemblies.Add(acl);
2776 }
2777
2778 loader = new ImportClassLoader(context, compiler, diagnostics, referencedAssemblies.ToArray(), options, options.path, options.targetIsModule, options.assembly, h);
2779 loader.classesToCompile = new List<string>(h.Keys);
2780 if (options.remapfile != null)
2781 {
2782 diagnostics.GenericCompilerInfo($"Loading remapped types (1) from {options.remapfile}");
2783
2784 FileStream fs;
2785 try
2786 {
2787 // NOTE: Using FileShare.ReadWrite ensures other FileStreams (from other processes) can be opened
2788 // simultaneously on this file while we are reading it.
2789 fs = new FileStream(options.remapfile.FullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
2790 }
2791 catch (Exception e)
2792 {
2793 throw new FatalCompilerErrorException(DiagnosticEvent.ErrorReadingFile(options.remapfile.FullName, e.Message));
2794 }
2795
2796 try
2797 {
2798 MapXml.Root map;
2799
2800 try
2801 {
2802 map = new MapXml.MapXmlSerializer().Read(XDocument.Load(fs, LoadOptions.SetLineInfo));
2803 }
2804 catch (MapXml.MapXmlException x)
2805 {
2806 throw new FatalCompilerErrorException(DiagnosticEvent.ErrorParsingMapFile(options.remapfile.FullName, x.Message));
2807 }
2808
2809 if (loader.ValidateAndSetMap(map) == false)
2810 return 1;
2811 }
2812 finally
2813 {
2814 fs.Close();
2815 }
2816
2817 if (options.bootstrap)
2818 context.ClassLoaderFactory.SetBootstrapClassLoader(loader);
2819 }
2820
2821 // If we do not yet have a reference to the base assembly and we are not compiling the base assembly,
2822 // try to find the base assembly by looking at the assemblies that the runtime references
2823 if (compiler.baseAssembly == null && options.bootstrap == false)
2824 {
2825 foreach (var name in compiler.runtimeAssembly.GetReferencedAssemblies())
2826 {
2827 Assembly asm = null;
2828
2829 try
2830 {
2831 var path = Path.Combine(Path.GetDirectoryName(compiler.runtimeAssembly.Location), name.Name + ".dll");
2832 if (File.Exists(path))
2833 asm = LoadReferencedAssembly(compiler, path);
2834 }
2835 catch (FileNotFoundException)
2836 {
2837
2838 }
2839
2840 if (asm != null && IsBaseAssembly(context, asm))
2841 {
2842 RuntimeAssemblyClassLoader.PreloadExportedAssemblies(context.StaticCompiler, asm);
2843 compiler.baseAssembly = asm;
2844 break;
2845 }
2846 }
2847
2848 if (compiler.baseAssembly == null)
2849 {
2850 throw new FatalCompilerErrorException(DiagnosticEvent.BootstrapClassesMissing());
2851 }
2852
2853 // we need to scan again for remapped types, now that we've loaded the core library
2854 context.ClassLoaderFactory.LoadRemappedTypes();
2855 }
2856
2857 if (options.bootstrap == false)
2858 {
2859 allReferencesAreStrongNamed &= IsSigned(context.Resolver.ResolveBaseAssembly().AsReflection());
2860 loader.AddReference(context.AssemblyClassLoaderFactory.FromAssembly(context.Resolver.ResolveBaseAssembly().AsReflection()));
2861 }
2862
2863 if ((options.keyPair != null || options.publicKey != null) && !allReferencesAreStrongNamed)
2864 {
2865 throw new FatalCompilerErrorException(DiagnosticEvent.StrongNameRequiresStrongNamedRefs());
2866 }
2867
2868 if (loader.map != null)
2869 {
2870 loader.LoadMapXml();
2871 }
2872
2873 if (options.bootstrap == false)
2874 {
2875 loader.fakeTypes = context.FakeTypes;
2876 loader.fakeTypes.Load(context.Resolver.ResolveBaseAssembly().AsReflection());
2877 }
2878
2879 return 0;
2880 }
2881
2882 static bool IsBaseAssembly(RuntimeContext context, Assembly asm)
2883 {
2884 return asm.IsDefined(context.Resolver.ResolveRuntimeType(typeof(IKVM.Attributes.RemappedClassAttribute).FullName).AsReflection(), false);
2885 }
2886
2887 private static Assembly LoadReferencedAssembly(StaticCompiler compiler, string r)
2888 {
2889 Assembly asm = compiler.LoadFile(r);
2890 return asm;
2891 }
2892
2893 private void CompilePass0()
2894 {
2895 if (state.sharedclassloader != null && state.sharedclassloader[0] != this)
2896 {
2897 packages = state.sharedclassloader[0].packages;
2898 }
2899 else
2900 {
2901 packages = new Packages();
2902 }
2903 }
2904
2905 void CompilePass1()
2906 {
2907 Diagnostics.GenericCompilerInfo("Compiling class files (1)");
2908 if (state.bootstrap)
2909 EmitRemappedTypes();
2910
2911 // if we're compiling the core class library, generate the "fake" generic types
2912 // that represent the not-really existing types (i.e. the Java enums that represent .NET enums,
2913 // the Method interface for delegates and the Annotation annotation for custom attributes)
2914 if (state.bootstrap)
2915 {
2916 fakeTypes = Context.FakeTypes;
2917 fakeTypes.Create(GetTypeWrapperFactory().ModuleBuilder, this);
2918 }
2919
2920 javaTypes = new List<RuntimeJavaType>();
2921
2922 foreach (var s in classesToCompile)
2923 {
2924 var javaType = TryLoadClassByName(s);
2925 if (javaType != null)
2926 {
2927 var loader = javaType.ClassLoader;
2928 if (loader != this)
2929 {
2930 if (loader is RuntimeAssemblyClassLoader)
2931 Diagnostics.SkippingReferencedClass(s, ((RuntimeAssemblyClassLoader)loader).GetAssembly(javaType).FullName);
2932
2933 continue;
2934 }
2935
2936 if (state.sharedclassloader != null && state.sharedclassloader[0] != this)
2937 state.sharedclassloader[0].dynamicallyImportedTypes.Add(javaType);
2938
2939 javaTypes.Add(javaType);
2940 }
2941 }
2942 }
2943
2944 void CompilePass2()
2945 {
2946 Diagnostics.GenericCompilerInfo("Compiling class files (2)");
2947
2948 foreach (var javaTypes in javaTypes)
2949 {
2950 var dtw = javaTypes as RuntimeByteCodeJavaType;
2951 if (dtw != null)
2952 dtw.CreateStep2();
2953 }
2954 }
2955
2956 int CompilePass3()
2957 {
2958 Diagnostics.GenericCompilerInfo("Compiling class files (3)");
2959
2960 // emits the IL required for module initialization
2961 var moduleInitBuilders = new List<Action<MethodBuilder, CodeEmitter>>();
2962
2963 // bootstrap mode introduces fake types
2964 if (map != null && state.bootstrap)
2965 {
2966 fakeTypes.Finish(this);
2967 }
2968
2969 // generate configured proxies
2970 foreach (string proxy in state.proxies)
2971 {
2972 Context.ProxyGenerator.Create(this, proxy);
2973 }
2974
2975 // set the main entry point to the main method of the specified class
2976 if (state.mainClass != null)
2977 {
2978 RuntimeJavaType wrapper = null;
2979
2980 try
2981 {
2982 wrapper = TryLoadClassByName(state.mainClass);
2983 }
2985 {
2986 }
2987 if (wrapper == null)
2988 {
2989 throw new FatalCompilerErrorException(DiagnosticEvent.MainClassNotFound());
2990 }
2991
2992 var mw = wrapper.GetMethod("main", "([Ljava.lang.String;)V", false);
2993 if (mw == null || !mw.IsStatic)
2994 throw new FatalCompilerErrorException(DiagnosticEvent.MainMethodNotFound());
2995
2996 mw.Link();
2997
2998 var method = mw.GetMethod() as MethodInfo;
2999 if (method == null)
3000 throw new FatalCompilerErrorException(DiagnosticEvent.UnsupportedMainMethod());
3001
3002 if (!ReflectUtil.IsFromAssembly(method.DeclaringType, assemblyBuilder) && (!method.IsPublic || !method.DeclaringType.IsPublic))
3003 {
3004 throw new FatalCompilerErrorException(DiagnosticEvent.ExternalMainNotAccessible());
3005 }
3006
3007 var apartmentAttributeType = state.apartment switch
3008 {
3009 ApartmentState.STA => Context.Resolver.ResolveCoreType(typeof(STAThreadAttribute).FullName),
3010 ApartmentState.MTA => Context.Resolver.ResolveCoreType(typeof(MTAThreadAttribute).FullName),
3011 ApartmentState.Unknown => null,
3012 _ => throw new NotImplementedException(),
3013 };
3014
3015 SetMain(wrapper, state.target, state.props, state.noglobbing, apartmentAttributeType.AsReflection());
3016 }
3017
3018 // complete map
3019 if (map != null)
3020 {
3021 LoadMappedExceptions(map);
3022 Diagnostics.GenericCompilerInfo("Loading remapped types (2)");
3023
3024 try
3025 {
3026 FinishRemappedTypes();
3027 }
3029 {
3030 Context.StaticCompiler.IssueMissingTypeMessage((Type)x.MemberInfo);
3031 return 1;
3032 }
3033 }
3034
3035 Diagnostics.GenericCompilerInfo("Compiling class files (2)");
3036 WriteResources();
3037
3038 // add external resources
3039 if (state.externalResources != null)
3040 foreach (KeyValuePair<string, string> kv in state.externalResources)
3041 assemblyBuilder.AddResourceFile(JVM.MangleResourceName(kv.Key), kv.Value);
3042
3043 // configure Win32 file version
3044 if (state.fileversion != null)
3045 {
3046 var filever = new CustomAttributeBuilder(Context.Resolver.ResolveCoreType(typeof(System.Reflection.AssemblyFileVersionAttribute).FullName).AsReflection().GetConstructor([Context.Types.String]), [state.fileversion]);
3047 assemblyBuilder.SetCustomAttribute(filever);
3048 }
3049
3050 // apply assembly annotations
3051 if (state.assemblyAttributeAnnotations != null)
3052 {
3053 foreach (object[] def in state.assemblyAttributeAnnotations)
3054 {
3055 var annotation = IKVM.Runtime.Annotation.LoadAssemblyCustomAttribute(this, def);
3056 if (annotation != null)
3057 annotation.Apply(this, assemblyBuilder, def);
3058 }
3059 }
3060
3061 // custom class loader specified
3062 if (state.classLoader != null)
3063 {
3064 RuntimeJavaType classLoaderType = null;
3065 try
3066 {
3067 classLoaderType = TryLoadClassByName(state.classLoader);
3068 }
3070 {
3071
3072 }
3073
3074 if (classLoaderType == null)
3075 throw new FatalCompilerErrorException(DiagnosticEvent.ClassLoaderNotFound());
3076
3077 if (classLoaderType.IsPublic == false && ReflectUtil.IsFromAssembly(classLoaderType.TypeAsBaseType, assemblyBuilder) == false)
3078 throw new FatalCompilerErrorException(DiagnosticEvent.ClassLoaderNotAccessible());
3079
3080 if (classLoaderType.IsAbstract)
3081 throw new FatalCompilerErrorException(DiagnosticEvent.ClassLoaderIsAbstract());
3082
3083 if (classLoaderType.IsAssignableTo(Context.ClassLoaderFactory.LoadClassCritical("java.lang.ClassLoader")) == false)
3084 throw new FatalCompilerErrorException(DiagnosticEvent.ClassLoaderNotClassLoader());
3085
3086 var classLoaderInitMethod = classLoaderType.GetMethod("<init>", "(Lcli.System.Reflection.Assembly;)V", false);
3087 if (classLoaderInitMethod == null)
3088 throw new FatalCompilerErrorException(DiagnosticEvent.ClassLoaderConstructorMissing());
3089
3090 // apply custom attribute specifying custom class loader
3091 var ci = Context.Resolver.ResolveRuntimeType(typeof(CustomAssemblyClassLoaderAttribute).FullName).AsReflection().GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new[] { Context.Types.Type }, null);
3092 assemblyBuilder.SetCustomAttribute(new CustomAttributeBuilder(ci, new object[] { classLoaderType.TypeAsTBD }));
3093
3094 // the class loader type defines a module initialize method, ensure we call it upon module load
3095 var mwModuleInit = classLoaderType.GetMethod("InitializeModule", "(Lcli.System.Reflection.Module;)V", false);
3096 if (mwModuleInit != null && mwModuleInit.IsStatic == false)
3097 {
3098 moduleInitBuilders.Add((mb, il) =>
3099 {
3100 il.Emit(OpCodes.Ldtoken, mb);
3101 il.Emit(OpCodes.Call, Context.Resolver.ResolveCoreType(typeof(System.Reflection.MethodBase).FullName).GetMethod("GetMethodFromHandle", new[] { Context.Resolver.ResolveCoreType(typeof(RuntimeMethodHandle).FullName) }).AsReflection());
3102 il.Emit(OpCodes.Callvirt, Context.Resolver.ResolveCoreType(typeof(System.Reflection.MemberInfo).FullName).GetProperty("Module").GetGetMethod().AsReflection());
3103 il.Emit(OpCodes.Call, Context.Resolver.ResolveRuntimeType("IKVM.Runtime.ByteCodeHelper").GetMethod("InitializeModule").AsReflection());
3104 });
3105 }
3106 }
3107
3108 if (state.iconfile != null)
3109 {
3110 assemblyBuilder.__DefineIconResource(ImportContext.ReadAllBytes(state.iconfile));
3111 }
3112
3113 if (state.manifestFile != null)
3114 {
3115 assemblyBuilder.__DefineManifestResource(ImportContext.ReadAllBytes(state.manifestFile));
3116 }
3117
3118 assemblyBuilder.DefineVersionInfoResource();
3119
3120 // find methods marked as module initializers and append calls
3121 foreach (var modInitMethod in javaTypes.SelectMany(i => i.GetMethods()).Where(i => i.IsModuleInitializer))
3122 {
3123 var modInitMethod_ = modInitMethod;
3124 moduleInitBuilders.Add((mb, il) => { modInitMethod_.Link(); modInitMethod_.EmitCall(il); });
3125 }
3126
3127 // apply module initializer if any instructions added
3128 if (moduleInitBuilders.Count > 0)
3129 {
3130 // begin a module initializer
3131 var moduleInit = GetTypeWrapperFactory().ModuleBuilder.DefineGlobalMethod(".cctor", MethodAttributes.Private | MethodAttributes.Static | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, null, Type.EmptyTypes);
3132 var moduleInitIL = Context.CodeEmitterFactory.Create(moduleInit);
3133
3134 // allow builders to append IL
3135 foreach (var moduleInitBuilder in moduleInitBuilders)
3136 moduleInitBuilder(moduleInit, moduleInitIL);
3137
3138 // finish method
3139 moduleInitIL.Emit(OpCodes.Ret);
3140 moduleInitIL.DoEmit();
3141 }
3142
3143 return 0;
3144 }
3145
3146 private bool ValidateAndSetMap(IKVM.Tools.Importer.MapXml.Root map)
3147 {
3148 bool valid = true;
3149 if (map.Assembly != null)
3150 {
3151 if (map.Assembly.Classes != null)
3152 {
3153 foreach (IKVM.Tools.Importer.MapXml.Class c in map.Assembly.Classes)
3154 {
3155 if (c.Fields != null)
3156 {
3157 foreach (IKVM.Tools.Importer.MapXml.Field f in c.Fields)
3158 {
3159 ValidateNameSig("field", c.Name, f.Name, f.Sig, ref valid, true);
3160 }
3161 }
3162 if (c.Methods != null)
3163 {
3164 foreach (IKVM.Tools.Importer.MapXml.Method m in c.Methods)
3165 {
3166 ValidateNameSig("method", c.Name, m.Name, m.Sig, ref valid, false);
3167 }
3168 }
3169 if (c.Constructors != null)
3170 {
3171 foreach (IKVM.Tools.Importer.MapXml.Constructor ctor in c.Constructors)
3172 {
3173 ValidateNameSig("constructor", c.Name, "<init>", ctor.Sig, ref valid, false);
3174 }
3175 }
3176 if (c.Properties != null)
3177 {
3178 foreach (IKVM.Tools.Importer.MapXml.Property prop in c.Properties)
3179 {
3180 ValidateNameSig("property", c.Name, prop.Name, prop.Sig, ref valid, false);
3181 ValidatePropertyGetterSetter("getter", c.Name, prop.Name, prop.Getter, ref valid);
3182 ValidatePropertyGetterSetter("setter", c.Name, prop.Name, prop.Setter, ref valid);
3183 }
3184 }
3185 }
3186 }
3187 }
3188 this.map = map;
3189 return valid;
3190 }
3191
3192 private void ValidateNameSig(string member, string clazz, string name, string sig, ref bool valid, bool field)
3193 {
3194 if (!IsValidName(name))
3195 {
3196 valid = false;
3197 Diagnostics.InvalidMemberNameInMapFile(member, name, clazz);
3198 }
3199 if (!IsValidSig(sig, field))
3200 {
3201 valid = false;
3202 Diagnostics.InvalidMemberSignatureInMapFile(member, clazz, name, sig);
3203 }
3204 }
3205
3206 void ValidatePropertyGetterSetter(string getterOrSetter, string clazz, string property, IKVM.Tools.Importer.MapXml.Method method, ref bool valid)
3207 {
3208 if (method != null)
3209 {
3210 if (!IsValidName(method.Name))
3211 {
3212 valid = false;
3213 Diagnostics.InvalidPropertyNameInMapFile(getterOrSetter, clazz, property, method.Name);
3214 }
3216 {
3217 valid = false;
3218 Diagnostics.InvalidPropertySignatureInMapFile(getterOrSetter, clazz, property, method.Sig);
3219 }
3220 }
3221 }
3222
3223 static bool IsValidName(string name)
3224 {
3225 return name != null && name.Length != 0;
3226 }
3227
3228 static bool IsValidSig(string sig, bool field)
3229 {
3230 return sig != null && (field ? IKVM.Runtime.ClassFile.IsValidFieldDescriptor(sig) : IKVM.Runtime.ClassFile.IsValidMethodDescriptor(sig));
3231 }
3232
3233 internal Type GetTypeFromReferencedAssembly(string name)
3234 {
3235 foreach (RuntimeAssemblyClassLoader acl in referencedAssemblies)
3236 {
3237 Type type = acl.MainAssembly.GetType(name, false);
3238 if (type != null)
3239 {
3240 return type;
3241 }
3242 }
3243 return null;
3244 }
3245
3246 internal override bool WarningLevelHigh
3247 {
3248 get { return state.warningLevelHigh; }
3249 }
3250
3251 internal override bool NoParameterReflection
3252 {
3253 get { return state.noParameterReflection; }
3254 }
3255
3256 protected override void CheckProhibitedPackage(string className)
3257 {
3258 if (!state.bootstrap)
3259 {
3260 base.CheckProhibitedPackage(className);
3261 }
3262 }
3263 }
3264
3265}
IKVM.Reflection.AssemblyName AssemblyName
IKVM.Reflection.MethodInfo MethodInfo
global::java.lang.invoke.LambdaForm.Name Name
Marks an assembly such that it's types are considered to be loaded by the specified class loader type...
static readonly Diagnostic ClassNotFound
The 'ClassNotFound' diagnostic.
static bool IsValidMethodDescriptor(string descriptor)
Returns true if the specified descriptor is a valid method descriptor.
Definition ClassFile.cs:220
bool IsPublic
Gets whether this class file represents a public class.
Definition ClassFile.cs:995
static bool IsValidFieldDescriptor(string descriptor)
Returns true if the specified descriptor is a valid field descriptor.
Definition ClassFile.cs:152
Modifiers Modifiers
Gets the modifiers of the class.
Definition ClassFile.cs:980
CodeEmitter Create(MethodBuilder mb)
Creates a new instance.
RuntimeContext Context
Gets the RuntimeContext that hosts this code emitter.
Main state of the running JVM.
Utility for launching a Java class from a main entry point. Parses JVM command line options,...
Definition Launcher.cs:29
static int Run(Assembly assembly, string main, bool jar, string[] args, string rarg, IDictionary< string, string > properties)
Services as the managed entry point jump for a Java executable.
Definition Launcher.cs:263
.NET exception that corresponds to a Java exception.
RuntimeAssemblyClassLoader FromAssembly(Assembly assembly)
Obtains the RuntimeAssemblyClassLoader for the given Assembly. This method should not be used with dy...
Runtime support for a class loader.
RuntimeContext Context
Gets a reference to the RuntimeContext that this RuntimeClassLoader is hosted within.
Maintains services relevant to an instane of the IKVM runtime.
AttributeHelper AttributeHelper
Gets the AttributeHelper associated with this instance of the runtime.
CodeEmitterFactory CodeEmitterFactory
Gets the CodeEmitterFactory associated with this instance of the runtime.
Types Types
Gets the Types associated with this instance of the runtime.
bool Bootstrap
Gets whether or not the runtime is running in bootstrap mode; that is, we are compiling the Java base...
RuntimeContextOptions Options
Gets the RuntimeContextOptions associated with this instance of the runtime.
CompilerFactory CompilerFactory
Gets the CompilerFactory associated with this instance of the runtime.
RuntimeAssemblyClassLoaderFactory AssemblyClassLoaderFactory
Gets the RuntimeAssemblyClassLoaderFactory associated with this instance of the runtime.
ISymbolResolver Resolver
Gets the ISymbolResolver associated with this instance of the runtime.
CoreClasses JavaBase
Gets the CoreClasses associated with this instance of the runtime.
RuntimeClassLoaderFactory ClassLoaderFactory
Gets the RuntimeClassLoaderFactory associated with this instance of the runtime.
FakeTypes(RuntimeContext context)
Initializes a new instance.
Definition FakeTypes.cs:50
Implementation of RuntimeClassLoader that emits loaded Java types to an AssemblyBuilder.
ImportClassLoader(RuntimeContext context, StaticCompiler compiler, IDiagnosticHandler diagnostics, RuntimeAssemblyClassLoader[] referencedAssemblies, ImportState options, FileInfo assemblyPath, bool targetIsModule, string assemblyName, Dictionary< string, Jar.Item > classes)
Initializes a new instance.
override RuntimeJavaType LoadClassImpl(string name, LoadMode mode)
override void CheckProhibitedPackage(string className)
Holds some state for an instance of ImportContext.
int LineNumber
Gets the line number in the source file where this element occurs.
Exposes methods to accept diagnostic invocations.
void DuplicateAssemblyReference(string arg0)
The 'DuplicateAssemblyReference' diagnostic.
void MainMethodFound(string arg0)
The 'MainMethodFound' diagnostic.
void LegacyAssemblyAttributesFound()
The 'LegacyAssemblyAttributesFound' diagnostic.
void DuplicateClassName(string arg0)
The 'DuplicateClassName' diagnostic.
void GenericCompilerInfo(string arg0)
The 'GenericCompilerInfo' diagnostic.
void OutputFileIs(string arg0)
The 'OutputFileIs' diagnostic.
MemberFlags
Describes various options applied to a member.
record struct MethodKey(string ClassName, string MethodName, string MethodSig)
@ None
Emit no debugging information.
static DiagnosticEvent MainClassNotFound(Exception? exception=null, DiagnosticLocation location=default)
The 'MainClassNotFound' diagnostic.
static DiagnosticEvent StrongNameRequiresStrongNamedRefs(Exception? exception=null, DiagnosticLocation location=default)
The 'StrongNameRequiresStrongNamedRefs' diagnostic.
static DiagnosticEvent BootstrapClassesMissing(Exception? exception=null, DiagnosticLocation location=default)
The 'BootstrapClassesMissing' diagnostic.
static DiagnosticEvent ExeRequiresMainClass(Exception? exception=null, DiagnosticLocation location=default)
The 'ExeRequiresMainClass' diagnostic.
static DiagnosticEvent PropertiesRequireExe(Exception? exception=null, DiagnosticLocation location=default)
The 'PropertiesRequireExe' diagnostic.
static DiagnosticEvent ErrorReadingFile(string arg0, string arg1, Exception? exception=null, DiagnosticLocation location=default)
The 'ErrorReadingFile' diagnostic.
static DiagnosticEvent ClassLoaderIsAbstract(Exception? exception=null, DiagnosticLocation location=default)
The 'ClassLoaderIsAbstract' diagnostic.
static DiagnosticEvent ClassLoaderNotClassLoader(Exception? exception=null, DiagnosticLocation location=default)
The 'ClassLoaderNotClassLoader' diagnostic.
static DiagnosticEvent ModuleCannotHaveClassLoader(Exception? exception=null, DiagnosticLocation location=default)
The 'ModuleCannotHaveClassLoader' diagnostic.
static DiagnosticEvent ClassLoaderConstructorMissing(Exception? exception=null, DiagnosticLocation location=default)
The 'ClassLoaderConstructorMissing' diagnostic.
static DiagnosticEvent ClassLoaderNotFound(Exception? exception=null, DiagnosticLocation location=default)
The 'ClassLoaderNotFound' diagnostic.
static DiagnosticEvent FileNotFound(string arg0, Exception? exception=null, DiagnosticLocation location=default)
The 'FileNotFound' diagnostic.
static DiagnosticEvent UnsupportedMainMethod(Exception? exception=null, DiagnosticLocation location=default)
The 'UnsupportedMainMethod' diagnostic.
static DiagnosticEvent ClassLoaderNotAccessible(Exception? exception=null, DiagnosticLocation location=default)
The 'ClassLoaderNotAccessible' diagnostic.
static DiagnosticEvent ExternalMainNotAccessible(Exception? exception=null, DiagnosticLocation location=default)
The 'ExternalMainNotAccessible' diagnostic.
static DiagnosticEvent RuntimeMismatch(string referencedAssemblyPath, string runtimeAssemblyName, string referencedAssemblyName, Exception? exception=null, DiagnosticLocation location=default)
The 'RuntimeMismatch' diagnostic.
static DiagnosticEvent ErrorWritingFile(string arg0, string arg1, Exception? exception=null, DiagnosticLocation location=default)
The 'ErrorWritingFile' diagnostic.
static DiagnosticEvent MainMethodNotFound(Exception? exception=null, DiagnosticLocation location=default)
The 'MainMethodNotFound' diagnostic.
static DiagnosticEvent RuntimeNotFound(Exception? exception=null, DiagnosticLocation location=default)
The 'RuntimeNotFound' diagnostic.
static DiagnosticEvent ErrorParsingMapFile(string arg0, string arg1, Exception? exception=null, DiagnosticLocation location=default)
The 'ErrorParsingMapFile' diagnostic.