2 // module.cs: keeps a tree representation of the generated code
4 // Authors: Miguel de Icaza (miguel@gnu.org)
5 // Marek Safar (marek.safar@gmail.com)
7 // Dual licensed under the terms of the MIT X11 or GNU GPL
9 // Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
10 // Copyright 2003-2008 Novell, Inc.
11 // Copyright 2011 Xamarin Inc
15 using System.Collections.Generic;
16 using System.Runtime.InteropServices;
17 using Mono.CompilerServices.SymbolWriter;
20 using System.Security.Cryptography;
21 using Mono.Security.Cryptography;
24 using IKVM.Reflection;
25 using IKVM.Reflection.Emit;
27 using System.Reflection;
28 using System.Reflection.Emit;
34 // Module (top-level type) container
36 public sealed class ModuleContainer : TypeContainer
40 // Compiler generated container for static data
42 sealed class StaticDataContainer : CompilerGeneratedContainer
44 readonly Dictionary<int, Struct> size_types;
45 readonly Dictionary<string, FieldSpec> data_hashes;
47 public StaticDataContainer (ModuleContainer module)
48 : base (module, new MemberName ("<PrivateImplementationDetails>", Location.Null),
49 Modifiers.STATIC | Modifiers.INTERNAL)
51 size_types = new Dictionary<int, Struct> ();
52 data_hashes = new Dictionary<string, FieldSpec> (StringComparer.Ordinal);
55 public override void CloseContainer ()
57 base.CloseContainer ();
59 foreach (var entry in size_types) {
60 entry.Value.CloseContainer ();
64 public FieldSpec DefineInitializedData (byte[] data, Location loc)
67 if (!size_types.TryGetValue (data.Length, out size_type)) {
69 // Build common type for this data length. We cannot use
70 // DefineInitializedData because it creates public type,
71 // and its name is not unique among modules
73 size_type = new Struct (this, new MemberName ("$ArrayType=" + data.Length, loc), Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED, null);
74 size_type.CreateContainer ();
75 size_type.DefineContainer ();
77 size_types.Add (data.Length, size_type);
79 // It has to work even if StructLayoutAttribute does not exist
80 size_type.TypeBuilder.__SetLayout (1, data.Length);
84 var data_hash = GenerateDataFieldName (data);
85 if (!data_hashes.TryGetValue (data_hash, out fs)) {
86 var name = "$field-" + data_hash;
87 const Modifiers fmod = Modifiers.STATIC | Modifiers.INTERNAL | Modifiers.READONLY;
88 var fbuilder = TypeBuilder.DefineField (name, size_type.CurrentType.GetMetaInfo (), ModifiersExtensions.FieldAttr (fmod) | FieldAttributes.HasFieldRVA);
89 fbuilder.__SetDataAndRVA (data);
91 fs = new FieldSpec (CurrentType, null, size_type.CurrentType, fbuilder, fmod);
92 data_hashes.Add (data_hash, fs);
98 static string GenerateDataFieldName (byte[] bytes)
100 using (var hashProvider = new SHA1CryptoServiceProvider ())
102 return CryptoConvert.ToHex (hashProvider.ComputeHash (bytes));
107 StaticDataContainer static_data;
110 // Makes const data field inside internal type container
112 public FieldSpec MakeStaticData (byte[] data, Location loc)
114 if (static_data == null) {
115 static_data = new StaticDataContainer (this);
116 static_data.CreateContainer ();
117 static_data.DefineContainer ();
119 AddCompilerGeneratedClass (static_data);
122 return static_data.DefineInitializedData (data, loc);
126 public sealed class PatternMatchingHelper : CompilerGeneratedContainer
128 public PatternMatchingHelper (ModuleContainer module)
129 : base (module, new MemberName ("<PatternMatchingHelper>", Location.Null),
130 Modifiers.STATIC | Modifiers.INTERNAL | Modifiers.DEBUGGER_HIDDEN)
134 public Method NumberMatcher { get; private set; }
136 protected override bool DoDefineMembers ()
138 if (!base.DoDefineMembers ())
141 NumberMatcher = GenerateNumberMatcher ();
145 Method GenerateNumberMatcher ()
148 var parameters = ParametersCompiled.CreateFullyResolved (
150 new Parameter (new TypeExpression (Compiler.BuiltinTypes.Object, loc), "obj", 0, null, loc),
151 new Parameter (new TypeExpression (Compiler.BuiltinTypes.Object, loc), "value", 0, null, loc),
152 new Parameter (new TypeExpression (Compiler.BuiltinTypes.Bool, loc), "enumType", 0, null, loc),
155 Compiler.BuiltinTypes.Object,
156 Compiler.BuiltinTypes.Object,
157 Compiler.BuiltinTypes.Bool
160 var m = new Method (this, new TypeExpression (Compiler.BuiltinTypes.Bool, loc),
161 Modifiers.PUBLIC | Modifiers.STATIC | Modifiers.DEBUGGER_HIDDEN, new MemberName ("NumberMatcher", loc),
164 parameters [0].Resolve (m, 0);
165 parameters [1].Resolve (m, 1);
166 parameters [2].Resolve (m, 2);
168 ToplevelBlock top_block = new ToplevelBlock (Compiler, parameters, loc);
173 // return Equals (obj, value);
175 var equals_args = new Arguments (2);
176 equals_args.Add (new Argument (top_block.GetParameterReference (0, loc)));
177 equals_args.Add (new Argument (top_block.GetParameterReference (1, loc)));
179 var if_type = new If (
180 top_block.GetParameterReference (2, loc),
181 new Return (new Invocation (new SimpleName ("Equals", loc), equals_args), loc),
184 top_block.AddStatement (if_type);
187 // if (obj is Enum || obj == null)
191 var if_enum = new If (
192 new Binary (Binary.Operator.LogicalOr,
193 new Is (top_block.GetParameterReference (0, loc), new TypeExpression (Compiler.BuiltinTypes.Enum, loc), loc),
194 new Binary (Binary.Operator.Equality, top_block.GetParameterReference (0, loc), new NullLiteral (loc))),
195 new Return (new BoolLiteral (Compiler.BuiltinTypes, false, loc), loc),
198 top_block.AddStatement (if_enum);
201 var system_convert = new MemberAccess (new QualifiedAliasMember ("global", "System", loc), "Convert", loc);
202 var expl_block = new ExplicitBlock (top_block, loc, loc);
205 // var converted = System.Convert.ChangeType (obj, System.Convert.GetTypeCode (value));
207 var lv_converted = LocalVariable.CreateCompilerGenerated (Compiler.BuiltinTypes.Object, top_block, loc);
209 var arguments_gettypecode = new Arguments (1);
210 arguments_gettypecode.Add (new Argument (top_block.GetParameterReference (1, loc)));
212 var gettypecode = new Invocation (new MemberAccess (system_convert, "GetTypeCode", loc), arguments_gettypecode);
214 var arguments_changetype = new Arguments (1);
215 arguments_changetype.Add (new Argument (top_block.GetParameterReference (0, loc)));
216 arguments_changetype.Add (new Argument (gettypecode));
218 var changetype = new Invocation (new MemberAccess (system_convert, "ChangeType", loc), arguments_changetype);
220 expl_block.AddStatement (new StatementExpression (new SimpleAssign (new LocalVariableReference (lv_converted, loc), changetype, loc)));
224 // return converted.Equals (value)
226 var equals_arguments = new Arguments (1);
227 equals_arguments.Add (new Argument (top_block.GetParameterReference (1, loc)));
228 var equals_invocation = new Invocation (new MemberAccess (new LocalVariableReference (lv_converted, loc), "Equals"), equals_arguments);
229 expl_block.AddStatement (new Return (equals_invocation, loc));
231 var catch_block = new ExplicitBlock (top_block, loc, loc);
232 catch_block.AddStatement (new Return (new BoolLiteral (Compiler.BuiltinTypes, false, loc), loc));
233 top_block.AddStatement (new TryCatch (expl_block, new List<Catch> () {
234 new Catch (catch_block, loc)
245 PatternMatchingHelper pmh;
247 public PatternMatchingHelper CreatePatterMatchingHelper ()
250 pmh = new PatternMatchingHelper (this);
252 pmh.CreateContainer ();
253 pmh.DefineContainer ();
255 AddCompilerGeneratedClass (pmh);
261 public CharSet? DefaultCharSet;
262 public TypeAttributes DefaultCharSetType = TypeAttributes.AnsiClass;
264 readonly Dictionary<int, List<AnonymousTypeClass>> anonymous_types;
265 readonly Dictionary<ArrayContainer.TypeRankPair, ArrayContainer> array_types;
266 readonly Dictionary<TypeSpec, PointerContainer> pointer_types;
267 readonly Dictionary<TypeSpec, ReferenceContainer> reference_types;
268 readonly Dictionary<TypeSpec, MethodSpec> attrs_cache;
269 readonly Dictionary<TypeSpec, AwaiterDefinition> awaiters;
270 readonly Dictionary<TypeSpec, TypeInfo> type_info_cache;
272 AssemblyDefinition assembly;
273 readonly CompilerContext context;
274 readonly RootNamespace global_ns;
275 readonly Dictionary<string, RootNamespace> alias_ns;
277 ModuleBuilder builder;
279 bool has_extenstion_method;
281 PredefinedAttributes predefined_attributes;
282 PredefinedTypes predefined_types;
283 PredefinedMembers predefined_members;
285 public Binary.PredefinedOperator[] OperatorsBinaryEqualityLifted;
286 public Binary.PredefinedOperator[] OperatorsBinaryLifted;
288 static readonly string[] attribute_targets = new string[] { "assembly", "module" };
290 public ModuleContainer (CompilerContext context)
291 : base (null, MemberName.Null, null, 0)
293 this.context = context;
295 caching_flags &= ~(Flags.Obsolete_Undetected | Flags.Excluded_Undetected);
297 containers = new List<TypeContainer> ();
298 anonymous_types = new Dictionary<int, List<AnonymousTypeClass>> ();
299 global_ns = new GlobalRootNamespace ();
300 alias_ns = new Dictionary<string, RootNamespace> ();
301 array_types = new Dictionary<ArrayContainer.TypeRankPair, ArrayContainer> ();
302 pointer_types = new Dictionary<TypeSpec, PointerContainer> ();
303 reference_types = new Dictionary<TypeSpec, ReferenceContainer> ();
304 attrs_cache = new Dictionary<TypeSpec, MethodSpec> ();
305 awaiters = new Dictionary<TypeSpec, AwaiterDefinition> ();
306 type_info_cache = new Dictionary<TypeSpec, TypeInfo> ();
311 internal Dictionary<ArrayContainer.TypeRankPair, ArrayContainer> ArrayTypesCache {
318 // Cache for parameter-less attributes
320 internal Dictionary<TypeSpec, MethodSpec> AttributeConstructorCache {
326 public override AttributeTargets AttributeTargets {
328 return AttributeTargets.Assembly;
332 public ModuleBuilder Builder {
338 public override CompilerContext Compiler {
344 public int CounterAnonymousTypes { get; set; }
346 public AssemblyDefinition DeclaringAssembly {
352 internal DocumentationBuilder DocumentationBuilder {
356 public override string DocCommentHeader {
358 throw new NotSupportedException ();
362 public Evaluator Evaluator {
366 public bool HasDefaultCharSet {
368 return DefaultCharSet.HasValue;
372 public bool HasExtensionMethod {
374 return has_extenstion_method;
377 has_extenstion_method = value;
381 public bool HasTypesFullyDefined {
386 // Returns module global:: namespace
388 public RootNamespace GlobalRootNamespace {
394 public override ModuleContainer Module {
400 internal Dictionary<TypeSpec, PointerContainer> PointerTypesCache {
402 return pointer_types;
406 internal PredefinedAttributes PredefinedAttributes {
408 return predefined_attributes;
412 internal PredefinedMembers PredefinedMembers {
414 return predefined_members;
418 internal PredefinedTypes PredefinedTypes {
420 return predefined_types;
424 internal Dictionary<TypeSpec, ReferenceContainer> ReferenceTypesCache {
426 return reference_types;
430 internal Dictionary<TypeSpec, TypeInfo> TypeInfoCache {
432 return type_info_cache;
436 public override string[] ValidAttributeTargets {
438 return attribute_targets;
442 public Dictionary<string, string> GetResourceStrings { get; private set; }
446 public override void Accept (StructuralVisitor visitor)
448 visitor.Visit (this);
451 public void AddAnonymousType (AnonymousTypeClass type)
453 List<AnonymousTypeClass> existing;
454 if (!anonymous_types.TryGetValue (type.Parameters.Count, out existing))
455 if (existing == null) {
456 existing = new List<AnonymousTypeClass> ();
457 anonymous_types.Add (type.Parameters.Count, existing);
463 public void AddAttribute (Attribute attr, IMemberContext context)
465 attr.AttachTo (this, context);
467 if (attributes == null) {
468 attributes = new Attributes (attr);
472 attributes.AddAttribute (attr);
475 public override void AddTypeContainer (TypeContainer tc)
477 AddTypeContainerMember (tc);
480 public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa)
482 if (a.Target == AttributeTargets.Assembly) {
483 assembly.ApplyAttributeBuilder (a, ctor, cdata, pa);
487 if (a.Type == pa.DefaultCharset) {
488 switch (a.GetCharSetValue ()) {
493 DefaultCharSet = CharSet.Auto;
494 DefaultCharSetType = TypeAttributes.AutoClass;
496 case CharSet.Unicode:
497 DefaultCharSet = CharSet.Unicode;
498 DefaultCharSetType = TypeAttributes.UnicodeClass;
501 Report.Error (1724, a.Location, "Value specified for the argument to `{0}' is not valid",
502 a.GetSignatureForError ());
505 } else if (a.Type == pa.CLSCompliant) {
506 Attribute cls = DeclaringAssembly.CLSCompliantAttribute;
508 Report.Warning (3012, 1, a.Location,
509 "You must specify the CLSCompliant attribute on the assembly, not the module, to enable CLS compliance checking");
510 } else if (DeclaringAssembly.IsCLSCompliant != a.GetBoolean ()) {
511 Report.SymbolRelatedToPreviousError (cls.Location, cls.GetSignatureForError ());
512 Report.Warning (3017, 1, a.Location,
513 "You cannot specify the CLSCompliant attribute on a module that differs from the CLSCompliant attribute on the assembly");
518 builder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), cdata);
521 public override void CloseContainer ()
523 if (anonymous_types != null) {
524 foreach (var atypes in anonymous_types)
525 foreach (var at in atypes.Value)
526 at.CloseContainer ();
529 base.CloseContainer ();
532 public TypeBuilder CreateBuilder (string name, TypeAttributes attr, int typeSize)
534 return builder.DefineType (name, attr, null, typeSize);
538 // Creates alias global namespace
540 public RootNamespace CreateRootNamespace (string alias)
542 if (alias == global_ns.Alias) {
543 RootNamespace.Error_GlobalNamespaceRedefined (Report, Location.Null);
548 if (!alias_ns.TryGetValue (alias, out rn)) {
549 rn = new RootNamespace (alias);
550 alias_ns.Add (alias, rn);
556 public void Create (AssemblyDefinition assembly, ModuleBuilder moduleBuilder)
558 this.assembly = assembly;
559 builder = moduleBuilder;
562 public override bool Define ()
566 ExpandBaseInterfaces ();
570 HasTypesFullyDefined = true;
575 public override bool DefineContainer ()
579 return base.DefineContainer ();
582 public void EnableRedefinition ()
587 public override void EmitContainer ()
589 if (OptAttributes != null)
590 OptAttributes.Emit ();
592 if (Compiler.Settings.Unsafe && !assembly.IsSatelliteAssembly) {
593 var pa = PredefinedAttributes.UnverifiableCode;
595 pa.EmitAttribute (builder);
598 foreach (var tc in containers) {
602 base.EmitContainer ();
604 if (Compiler.Report.Errors == 0 && !Compiler.Settings.WriteMetadataOnly)
607 if (anonymous_types != null) {
608 foreach (var atypes in anonymous_types)
609 foreach (var at in atypes.Value)
614 internal override void GenerateDocComment (DocumentationBuilder builder)
616 foreach (var tc in containers)
617 tc.GenerateDocComment (builder);
620 public AnonymousTypeClass GetAnonymousType (IList<AnonymousTypeParameter> parameters)
622 List<AnonymousTypeClass> candidates;
623 if (!anonymous_types.TryGetValue (parameters.Count, out candidates))
627 foreach (AnonymousTypeClass at in candidates) {
628 for (i = 0; i < parameters.Count; ++i) {
629 if (!parameters [i].Equals (at.Parameters [i]))
633 if (i == parameters.Count)
641 // Return container with awaiter definition. It never returns null
642 // but all container member can be null for easier error reporting
644 public AwaiterDefinition GetAwaiter (TypeSpec type)
646 AwaiterDefinition awaiter;
647 if (awaiters.TryGetValue (type, out awaiter))
650 awaiter = new AwaiterDefinition ();
653 // Predefined: bool IsCompleted { get; }
655 awaiter.IsCompleted = MemberCache.FindMember (type, MemberFilter.Property ("IsCompleted", Compiler.BuiltinTypes.Bool),
656 BindingRestriction.InstanceOnly) as PropertySpec;
659 // Predefined: GetResult ()
661 // The method return type is also result type of await expression
663 awaiter.GetResult = MemberCache.FindMember (type, MemberFilter.Method ("GetResult", 0,
664 ParametersCompiled.EmptyReadOnlyParameters, null),
665 BindingRestriction.InstanceOnly) as MethodSpec;
668 // Predefined: INotifyCompletion.OnCompleted (System.Action)
670 var nc = PredefinedTypes.INotifyCompletion;
671 awaiter.INotifyCompletion = !nc.Define () || type.ImplementsInterface (nc.TypeSpec, false);
673 awaiters.Add (type, awaiter);
677 public override void GetCompletionStartingWith (string prefix, List<string> results)
679 var names = Evaluator.GetVarNames ();
680 results.AddRange (names.Where (l => l.StartsWith (prefix)));
683 public RootNamespace GetRootNamespace (string name)
686 alias_ns.TryGetValue (name, out rn);
690 public override string GetSignatureForError ()
695 public Binary.PredefinedOperator[] GetPredefinedEnumAritmeticOperators (TypeSpec enumType, bool nullable)
698 Binary.Operator mask = 0;
701 underlying = Nullable.NullableInfo.GetEnumUnderlyingType (this, enumType);
702 mask = Binary.Operator.NullableMask;
704 underlying = EnumSpec.GetUnderlyingType (enumType);
707 var operators = new[] {
708 new Binary.PredefinedOperator (enumType, underlying,
709 mask | Binary.Operator.AdditionMask | Binary.Operator.SubtractionMask | Binary.Operator.DecomposedMask, enumType),
710 new Binary.PredefinedOperator (underlying, enumType,
711 mask | Binary.Operator.AdditionMask | Binary.Operator.SubtractionMask | Binary.Operator.DecomposedMask, enumType),
712 new Binary.PredefinedOperator (enumType, mask | Binary.Operator.SubtractionMask, underlying)
718 public void InitializePredefinedTypes ()
720 predefined_attributes = new PredefinedAttributes (this);
721 predefined_types = new PredefinedTypes (this);
722 predefined_members = new PredefinedMembers (this);
724 OperatorsBinaryEqualityLifted = Binary.CreateEqualityLiftedOperatorsTable (this);
725 OperatorsBinaryLifted = Binary.CreateStandardLiftedOperatorsTable (this);
728 public override bool IsClsComplianceRequired ()
730 return DeclaringAssembly.IsCLSCompliant;
733 public Attribute ResolveAssemblyAttribute (PredefinedAttribute a_type)
735 Attribute a = OptAttributes.Search ("assembly", a_type);
742 public void SetDeclaringAssembly (AssemblyDefinition assembly)
744 // TODO: This setter is quite ugly but I have not found a way around it yet
745 this.assembly = assembly;
748 public void LoadGetResourceStrings (List<string> fileNames)
750 foreach (var fileName in fileNames) {
751 if (!File.Exists (fileName)) {
752 Report.Error (1566, "Error reading resource file `{0}'", fileName);
756 foreach (var l in File.ReadLines (fileName)) {
757 if (GetResourceStrings == null)
758 GetResourceStrings = new Dictionary<string, string> ();
760 var line = l.Trim ();
761 if (line.Length == 0 || line [0] == '#' || line [0] == ';')
764 var epos = line.IndexOf ('=');
768 var key = line.Substring (0, epos).Trim ();
769 var value = line.Substring (epos + 1).Trim ();
771 GetResourceStrings [key] = value;