Less static is good for my health
[mono.git] / mcs / mcs / ikvm.cs
1 //
2 // ikvm.cs: IKVM.Reflection and IKVM.Reflection.Emit specific implementations
3 //
4 // Author: Marek Safar (marek.safar@gmail.com)
5 //
6 // Dual licensed under the terms of the MIT X11 or GNU GPL
7 //
8 // Copyright 2009-2010 Novell, Inc. 
9 //
10 //
11
12 using System;
13 using System.Collections.Generic;
14 using MetaType = IKVM.Reflection.Type;
15 using IKVM.Reflection;
16 using IKVM.Reflection.Emit;
17 using System.IO;
18 using System.Configuration.Assemblies;
19
20 namespace Mono.CSharp
21 {
22 #if !STATIC
23         public class StaticImporter
24         {
25                 public StaticImporter (BuildinTypes buildin)
26                 {
27                         throw new NotSupportedException ();
28                 }
29
30                 public void ImportAssembly (Assembly assembly, RootNamespace targetNamespace)
31                 {
32                         throw new NotSupportedException ();
33                 }
34
35                 public void ImportModule (Module module, RootNamespace targetNamespace)
36                 {
37                         throw new NotSupportedException ();
38                 }
39
40                 public TypeSpec ImportType (System.Type type)
41                 {
42                         throw new NotSupportedException ();
43                 }
44         }
45
46 #else
47
48         sealed class StaticImporter : MetadataImporter
49         {
50                 public StaticImporter ()
51                 {
52                 }
53
54                 public void AddCompiledAssembly (AssemblyDefinitionStatic assembly)
55                 {
56                         assembly_2_definition.Add (assembly.Builder, assembly);
57                 }
58
59                 public override void AddCompiledType (TypeBuilder type, TypeSpec spec)
60                 {
61                         compiled_types.Add (type, spec);
62                 }
63
64                 protected override MemberKind DetermineKindFromBaseType (MetaType baseType)
65                 {
66                         string name = baseType.Name;
67
68                         if (name == "ValueType" && baseType.Namespace == "System")
69                                 return MemberKind.Struct;
70
71                         if (name == "Enum" && baseType.Namespace == "System")
72                                 return MemberKind.Enum;
73
74                         if (name == "MulticastDelegate" && baseType.Namespace == "System")
75                                 return MemberKind.Delegate;
76
77                         return MemberKind.Class;
78                 }
79
80                 protected override bool HasVolatileModifier (MetaType[] modifiers)
81                 {
82                         foreach (var t in modifiers) {
83                                 if (t.Name == "IsVolatile" && t.Namespace == CompilerServicesNamespace)
84                                         return true;
85                         }
86
87                         return false;
88                 }
89
90                 public override void GetCustomAttributeTypeName (CustomAttributeData cad, out string typeNamespace, out string typeName)
91                 {
92                         cad.__TryReadTypeName (out typeNamespace, out typeName);
93                 }
94
95                 public void ImportAssembly (Assembly assembly, RootNamespace targetNamespace)
96                 {
97                         // It can be used more than once when importing same assembly
98                         // into 2 or more global aliases
99                         var definition = GetAssemblyDefinition (assembly);
100
101                         var all_types = assembly.GetTypes ();
102                         ImportTypes (all_types, targetNamespace, definition.HasExtensionMethod);
103                 }
104
105                 public ImportedModuleDefinition ImportModule (Module module, RootNamespace targetNamespace)
106                 {
107                         var module_definition = new ImportedModuleDefinition (module, this);
108                         module_definition.ReadAttributes ();
109
110                         var all_types = module.GetTypes ();
111                         ImportTypes (all_types, targetNamespace, false);
112
113                         return module_definition;
114                 }
115
116                 public void InitializeBuildinTypes (BuildinTypes buildin, Assembly corlib)
117                 {
118                         //
119                         // Setup mapping for build-in types to avoid duplication of their definition
120                         //
121                         foreach (var type in buildin.AllTypes) {
122                                 compiled_types.Add (corlib.GetType (type.FullName), type);
123                         }
124                 }
125         }
126 #endif
127
128         class AssemblyDefinitionStatic : AssemblyDefinition
129         {
130                 readonly StaticLoader loader;
131
132                 //
133                 // Assembly container with file output
134                 //
135                 public AssemblyDefinitionStatic (ModuleContainer module, StaticLoader loader, string name, string fileName)
136                         : base (module, name, fileName)
137                 {
138                         this.loader = loader;
139                         Importer = loader.MetadataImporter;
140                 }
141
142                 //
143                 // Initializes the assembly SRE domain
144                 //
145                 public void Create (Universe domain)
146                 {
147                         ResolveAssemblySecurityAttributes ();
148                         var an = CreateAssemblyName ();
149
150                         Builder = domain.DefineDynamicAssembly (an, AssemblyBuilderAccess.Save, Path.GetDirectoryName (file_name));
151                         module.Create (this, CreateModuleBuilder ());
152                 }
153
154                 public override void Emit ()
155                 {
156                         if (loader.Corlib != null && !(loader.Corlib is AssemblyBuilder)) {
157                                 Builder.__SetImageRuntimeVersion (loader.Corlib.ImageRuntimeVersion, 0x20000);
158                         } else {
159                                 // Sets output file metadata version when there is no mscorlib
160                                 switch (module.Compiler.Settings.StdLibRuntimeVersion) {
161                                 case RuntimeVersion.v4:
162                                         Builder.__SetImageRuntimeVersion ("v4.0.30319", 0x20000);
163                                         break;
164                                 case RuntimeVersion.v2:
165                                         Builder.__SetImageRuntimeVersion ("v2.0.50727", 0x20000);
166                                         break;
167                                 case RuntimeVersion.v1:
168                                         // Compiler does not do any checks whether the produced metadata
169                                         // are valid in the context of 1.0 stream version
170                                         Builder.__SetImageRuntimeVersion ("v1.1.4322", 0x10000);
171                                         break;
172                                 default:
173                                         throw new NotImplementedException ();
174                                 }
175                         }
176
177                         builder_extra = new AssemblyBuilderIKVM (Builder, Compiler);
178
179                         base.Emit ();
180                 }
181
182                 public Module IncludeModule (RawModule moduleFile)
183                 {
184                         return Builder.__AddModule (moduleFile);
185                 }
186
187                 protected override void SaveModule (PortableExecutableKinds pekind, ImageFileMachine machine)
188                 {
189                         module.Builder.__Save (pekind, machine);
190                 }
191         }
192
193         class StaticLoader : AssemblyReferencesLoader<Assembly>, IDisposable
194         {
195                 readonly StaticImporter importer;
196                 readonly Universe domain;
197                 Assembly corlib;
198                 List<Tuple<AssemblyName, string, Assembly>> loaded_names;
199                 static readonly Dictionary<string, string[]> sdk_directory;
200
201                 static StaticLoader ()
202                 {
203                         sdk_directory = new Dictionary<string, string[]> ();
204                         sdk_directory.Add ("2", new string[] { "2.0", "net_2_0", "v2.0.50727" });
205                         sdk_directory.Add ("4", new string[] { "4.0", "net_4_0", "v4.0.30319" });
206                 }
207
208                 public StaticLoader (StaticImporter importer, CompilerContext compiler)
209                         : base (compiler)
210                 {
211                         this.importer = importer;
212                         domain = new Universe ();
213                         domain.EnableMissingMemberResolution ();
214                         domain.AssemblyResolve += AssemblyReferenceResolver;
215                         loaded_names = new List<Tuple<AssemblyName, string, Assembly>> ();
216
217                         var corlib_path = Path.GetDirectoryName (typeof (object).Assembly.Location);
218                         string fx_path = corlib_path.Substring (0, corlib_path.LastIndexOf (Path.DirectorySeparatorChar));
219                         string sdk_path = null;
220
221                         string sdk_version = compiler.Settings.SdkVersion ?? "4";
222                         string[] sdk_sub_dirs;
223
224                         if (!sdk_directory.TryGetValue (sdk_version, out sdk_sub_dirs))
225                                 sdk_sub_dirs = new string[] { sdk_version };
226
227                         foreach (var dir in sdk_sub_dirs) {
228                                 sdk_path = Path.Combine (fx_path, dir);
229                                 if (File.Exists (Path.Combine (sdk_path, "mscorlib.dll")))
230                                         break;
231
232                                 sdk_path = null;
233                         }
234
235                         if (sdk_path == null) {
236                                 compiler.Report.Warning (-1, 1, "SDK path could not be resolved");
237                                 sdk_path = corlib_path;
238                         }
239
240                         paths.Add (sdk_path);
241                 }
242
243                 #region Properties
244
245                 public Assembly Corlib {
246                         get {
247                                 return corlib;
248                         }
249                         set {
250                                 corlib = value;
251                         }
252                 }
253
254                 public Universe Domain {
255                         get {
256                                 return domain;
257                         }
258                 }
259
260                 public StaticImporter MetadataImporter {
261                         get {
262                                 return importer;
263                         }
264                 }
265
266                 #endregion
267
268                 Assembly AssemblyReferenceResolver (object sender, IKVM.Reflection.ResolveEventArgs args)
269                 {
270                         var refname = args.Name;
271                         if (refname == "mscorlib")
272                                 return corlib;
273
274                         Assembly version_mismatch = null;
275                         bool is_fx_assembly = false;
276
277                         foreach (var assembly in domain.GetAssemblies ()) {
278                                 AssemblyComparisonResult result;
279                                 if (!Fusion.CompareAssemblyIdentityPure (refname, false, assembly.FullName, false, out result)) {
280                                         if ((result == AssemblyComparisonResult.NonEquivalentVersion || result == AssemblyComparisonResult.NonEquivalentPartialVersion) &&
281                                                 (version_mismatch == null || version_mismatch.GetName ().Version < assembly.GetName ().Version) &&
282                                                 !is_fx_assembly) {
283                                                 version_mismatch = assembly;
284                                         }
285
286                                         continue;
287                                 }
288
289                                 if (result == AssemblyComparisonResult.EquivalentFullMatch ||
290                                         result == AssemblyComparisonResult.EquivalentWeakNamed ||
291                                         result == AssemblyComparisonResult.EquivalentPartialMatch) {
292                                         return assembly;
293                                 }
294
295                                 if (result == AssemblyComparisonResult.EquivalentFXUnified) {
296                                         is_fx_assembly = true;
297
298                                         if (version_mismatch == null || version_mismatch.GetName ().Version < assembly.GetName ().Version)
299                                                 version_mismatch = assembly;
300
301                                         continue;
302                                 }
303
304                                 throw new NotImplementedException ("Assembly equality = " + result.ToString ());
305                         }
306
307                         if (version_mismatch != null) {
308                                 if (version_mismatch is AssemblyBuilder)
309                                         return version_mismatch;
310
311                                 var v1 = new AssemblyName (refname).Version;
312                                 var v2 = version_mismatch.GetName ().Version;
313
314                                 if (v1 > v2) {
315 //                                      compiler.Report.SymbolRelatedToPreviousError (args.RequestingAssembly.Location);
316                                         compiler.Report.Error (1705, "Assembly `{0}' references `{1}' which has a higher version number than imported assembly `{2}'",
317                                                 args.RequestingAssembly.FullName, refname, version_mismatch.GetName ().FullName);
318
319                                         return domain.CreateMissingAssembly (args.Name);
320                                 }
321
322                                 if (!is_fx_assembly) {
323                                         if (v1.Major != v2.Major || v1.Minor != v2.Minor) {
324                                                 compiler.Report.Warning (1701, 2,
325                                                         "Assuming assembly reference `{0}' matches assembly `{1}'. You may need to supply runtime policy",
326                                                         refname, version_mismatch.GetName ().FullName);
327                                         } else {
328                                                 compiler.Report.Warning (1702, 3,
329                                                         "Assuming assembly reference `{0}' matches assembly `{1}'. You may need to supply runtime policy",
330                                                         refname, version_mismatch.GetName ().FullName);
331                                         }
332                                 }
333
334                                 return version_mismatch;
335                         }
336
337                         // AssemblyReference has not been found in the domain
338                         // create missing reference and continue
339                         return domain.CreateMissingAssembly (args.Name);
340                 }
341
342                 public void Dispose ()
343                 {
344                         domain.Dispose ();
345                 }
346
347                 protected override string[] GetDefaultReferences ()
348                 {
349                         //
350                         // For now the "default config" is harcoded into the compiler
351                         // we can move this outside later
352                         //
353                         var default_references = new List<string> (4);
354
355                         default_references.Add ("System.dll");
356                         default_references.Add ("System.Xml.dll");
357
358                         if (compiler.Settings.Version > LanguageVersion.ISO_2)
359                                 default_references.Add ("System.Core.dll");
360                         if (compiler.Settings.Version > LanguageVersion.V_3)
361                                 default_references.Add ("Microsoft.CSharp.dll");
362
363                         return default_references.ToArray ();
364                 }
365
366                 public override bool HasObjectType (Assembly assembly)
367                 {
368                         return assembly.GetType (compiler.BuildinTypes.Object.FullName) != null;
369                 }
370
371                 public override Assembly LoadAssemblyFile (string fileName)
372                 {
373                         bool? has_extension = null;
374                         foreach (var path in paths) {
375                                 var file = Path.Combine (path, fileName);
376                                 if (Report.DebugFlags > 0)
377                                         Console.WriteLine ("Probing assembly location `{0}'", file);
378
379                                 if (!File.Exists (file)) {
380                                         if (!has_extension.HasValue)
381                                                 has_extension = fileName.EndsWith (".dll", StringComparison.Ordinal) || fileName.EndsWith (".exe", StringComparison.Ordinal);
382
383                                         if (has_extension.Value)
384                                                 continue;
385
386                                         file += ".dll";
387                                         if (!File.Exists (file))
388                                                 continue;
389                                 }
390
391                                 try {
392                                         using (RawModule module = domain.OpenRawModule (file)) {
393                                                 if (!module.IsManifestModule) {
394                                                         Error_AssemblyIsModule (fileName);
395                                                         return null;
396                                                 }
397
398                                                 //
399                                                 // check whether the assembly can be actually imported without
400                                                 // collision
401                                                 //
402                                                 var an = module.GetAssemblyName ();
403                                                 foreach (var entry in loaded_names) {
404                                                         var loaded_name = entry.Item1;
405                                                         if (an.Name != loaded_name.Name)
406                                                                 continue;
407
408                                                         if (an.CodeBase == loaded_name.CodeBase)
409                                                                 return entry.Item3;
410                                                         
411                                                         if (((an.Flags | loaded_name.Flags) & AssemblyNameFlags.PublicKey) == 0) {
412                                                                 compiler.Report.SymbolRelatedToPreviousError (entry.Item2);
413                                                                 compiler.Report.SymbolRelatedToPreviousError (fileName);
414                                                                 compiler.Report.Error (1704,
415                                                                         "An assembly with the same name `{0}' has already been imported. Consider removing one of the references or sign the assembly",
416                                                                         an.Name);
417                                                                 return null;
418                                                         }
419
420                                                         if ((an.Flags & AssemblyNameFlags.PublicKey) == (loaded_name.Flags & AssemblyNameFlags.PublicKey) && an.Version.Equals (loaded_name.Version)) {
421                                                                 compiler.Report.SymbolRelatedToPreviousError (entry.Item2);
422                                                                 compiler.Report.SymbolRelatedToPreviousError (fileName);
423                                                                 compiler.Report.Error (1703,
424                                                                         "An assembly with the same identity `{0}' has already been imported. Consider removing one of the references",
425                                                                         an.FullName);
426                                                                 return null;
427                                                         }
428                                                 }
429
430                                                 if (Report.DebugFlags > 0)
431                                                         Console.WriteLine ("Loading assembly `{0}'", fileName);
432
433                                                 var assembly = domain.LoadAssembly (module);
434                                                 if (assembly != null)
435                                                         loaded_names.Add (Tuple.Create (an, fileName, assembly));
436
437                                                 return assembly;
438                                         }
439                                 } catch {
440                                         Error_FileCorrupted (file);
441                                         return null;
442                                 }
443                         }
444
445                         Error_FileNotFound (fileName);
446                         return null;
447                 }
448
449                 public RawModule LoadModuleFile (string moduleName)
450                 {
451                         foreach (var path in paths) {
452                                 var file = Path.Combine (path, moduleName);
453                                 if (!File.Exists (file)) {
454                                         if (moduleName.EndsWith (".netmodule", StringComparison.Ordinal))
455                                                 continue;
456
457                                         file += ".netmodule";
458                                         if (!File.Exists (file))
459                                                 continue;
460                                 }
461
462                                 try {
463                                         return domain.OpenRawModule (file);
464                                 } catch {
465                                         Error_FileCorrupted (file);
466                                         return null;
467                                 }
468                         }
469
470                         Error_FileNotFound (moduleName);
471                         return null;                            
472                 }
473
474                 //
475                 // Optimized default assembly loader version
476                 //
477                 public override Assembly LoadAssemblyDefault (string assembly)
478                 {
479                         foreach (var path in paths) {
480                                 var file = Path.Combine (path, assembly);
481
482                                 if (Report.DebugFlags > 0)
483                                         Console.WriteLine ("Probing default assembly location `{0}'", file);
484
485                                 if (!File.Exists (file))
486                                         continue;
487
488                                 try {
489                                         if (Report.DebugFlags > 0)
490                                                 Console.WriteLine ("Loading default assembly `{0}'", file);
491
492                                         var a = domain.LoadFile (file);
493                                         if (a != null) {
494                                                 loaded_names.Add (Tuple.Create (a.GetName (), file, a));
495                                         }
496
497                                         return a;
498                                 } catch {
499                                         // Default assemblies can fail to load without error
500                                         return null;
501                                 }
502                         }
503
504                         return null;
505                 }
506
507                 public override void LoadReferences (ModuleContainer module)
508                 {
509                         List<Tuple<RootNamespace, Assembly>> loaded;
510                         base.LoadReferencesCore (module, out corlib, out loaded);
511
512                         compiler.TimeReporter.Start (TimeReporter.TimerType.ReferencesImporting);
513
514                         if (corlib == null) {
515                                 // System.Object was not found in any referenced assembly, use compiled assembly as corlib
516                                 corlib = module.DeclaringAssembly.Builder;
517                         } else {
518                                 importer.InitializeBuildinTypes (compiler.BuildinTypes, corlib);
519                                 importer.ImportAssembly (corlib, module.GlobalRootNamespace);
520                         }
521
522                         foreach (var entry in loaded) {
523                                 importer.ImportAssembly (entry.Item2, entry.Item1);
524                         }
525
526                         compiler.TimeReporter.Stop (TimeReporter.TimerType.ReferencesImporting);
527                 }
528
529                 public void LoadModules (AssemblyDefinitionStatic assembly, RootNamespace targetNamespace)
530                 {
531                         foreach (var moduleName in compiler.Settings.Modules) {
532                                 var m = LoadModuleFile (moduleName);
533                                 if (m == null)
534                                         continue;
535
536                                 if (m.IsManifestModule) {
537                                         Error_FileCorrupted (moduleName);
538                                         continue;
539                                 }
540
541                                 var md = importer.ImportModule (assembly.IncludeModule (m), targetNamespace);
542                                 assembly.AddModule (md);
543                         }
544                 }
545         }
546
547         class AssemblyBuilderIKVM : AssemblyBuilderExtension
548         {
549                 readonly AssemblyBuilder builder;
550
551                 public AssemblyBuilderIKVM (AssemblyBuilder builder, CompilerContext ctx)
552                         : base (ctx)
553                 {
554                         this.builder = builder;
555                 }
556
557                 public override void AddTypeForwarder (TypeSpec type, Location loc)
558                 {
559                         builder.__AddTypeForwarder (type.GetMetaInfo ());
560                 }
561
562                 public override void DefineWin32IconResource (string fileName)
563                 {
564                         builder.__DefineIconResource (File.ReadAllBytes (fileName));
565                 }
566
567                 public override void SetAlgorithmId (uint value, Location loc)
568                 {
569                         builder.__SetAssemblyAlgorithmId ((AssemblyHashAlgorithm) value);
570                 }
571
572                 public override void SetCulture (string culture, Location loc)
573                 {
574                         builder.__SetAssemblyCulture (culture);
575                 }
576
577                 public override void SetFlags (uint flags, Location loc)
578                 {
579                         builder.__SetAssemblyFlags ((AssemblyNameFlags) flags);
580                 }
581
582                 public override void SetVersion (Version version, Location loc)
583                 {
584                         builder.__SetAssemblyVersion (version);
585                 }
586         }
587 }