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;
271 AssemblyDefinition assembly;
272 readonly CompilerContext context;
273 readonly RootNamespace global_ns;
274 readonly Dictionary<string, RootNamespace> alias_ns;
276 ModuleBuilder builder;
278 bool has_extenstion_method;
280 PredefinedAttributes predefined_attributes;
281 PredefinedTypes predefined_types;
282 PredefinedMembers predefined_members;
284 public Binary.PredefinedOperator[] OperatorsBinaryEqualityLifted;
285 public Binary.PredefinedOperator[] OperatorsBinaryLifted;
287 static readonly string[] attribute_targets = new string[] { "assembly", "module" };
289 public ModuleContainer (CompilerContext context)
290 : base (null, MemberName.Null, null, 0)
292 this.context = context;
294 caching_flags &= ~(Flags.Obsolete_Undetected | Flags.Excluded_Undetected);
296 containers = new List<TypeContainer> ();
297 anonymous_types = new Dictionary<int, List<AnonymousTypeClass>> ();
298 global_ns = new GlobalRootNamespace ();
299 alias_ns = new Dictionary<string, RootNamespace> ();
300 array_types = new Dictionary<ArrayContainer.TypeRankPair, ArrayContainer> ();
301 pointer_types = new Dictionary<TypeSpec, PointerContainer> ();
302 reference_types = new Dictionary<TypeSpec, ReferenceContainer> ();
303 attrs_cache = new Dictionary<TypeSpec, MethodSpec> ();
304 awaiters = new Dictionary<TypeSpec, AwaiterDefinition> ();
309 internal Dictionary<ArrayContainer.TypeRankPair, ArrayContainer> ArrayTypesCache {
316 // Cache for parameter-less attributes
318 internal Dictionary<TypeSpec, MethodSpec> AttributeConstructorCache {
324 public override AttributeTargets AttributeTargets {
326 return AttributeTargets.Assembly;
330 public ModuleBuilder Builder {
336 public override CompilerContext Compiler {
342 public int CounterAnonymousTypes { get; set; }
344 public AssemblyDefinition DeclaringAssembly {
350 internal DocumentationBuilder DocumentationBuilder {
354 public override string DocCommentHeader {
356 throw new NotSupportedException ();
360 public Evaluator Evaluator {
364 public bool HasDefaultCharSet {
366 return DefaultCharSet.HasValue;
370 public bool HasExtensionMethod {
372 return has_extenstion_method;
375 has_extenstion_method = value;
379 public bool HasTypesFullyDefined {
384 // Returns module global:: namespace
386 public RootNamespace GlobalRootNamespace {
392 public override ModuleContainer Module {
398 internal Dictionary<TypeSpec, PointerContainer> PointerTypesCache {
400 return pointer_types;
404 internal PredefinedAttributes PredefinedAttributes {
406 return predefined_attributes;
410 internal PredefinedMembers PredefinedMembers {
412 return predefined_members;
416 internal PredefinedTypes PredefinedTypes {
418 return predefined_types;
422 internal Dictionary<TypeSpec, ReferenceContainer> ReferenceTypesCache {
424 return reference_types;
428 public override string[] ValidAttributeTargets {
430 return attribute_targets;
434 public Dictionary<string, string> GetResourceStrings { get; private set; }
438 public override void Accept (StructuralVisitor visitor)
440 visitor.Visit (this);
443 public void AddAnonymousType (AnonymousTypeClass type)
445 List<AnonymousTypeClass> existing;
446 if (!anonymous_types.TryGetValue (type.Parameters.Count, out existing))
447 if (existing == null) {
448 existing = new List<AnonymousTypeClass> ();
449 anonymous_types.Add (type.Parameters.Count, existing);
455 public void AddAttribute (Attribute attr, IMemberContext context)
457 attr.AttachTo (this, context);
459 if (attributes == null) {
460 attributes = new Attributes (attr);
464 attributes.AddAttribute (attr);
467 public override void AddTypeContainer (TypeContainer tc)
469 AddTypeContainerMember (tc);
472 public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa)
474 if (a.Target == AttributeTargets.Assembly) {
475 assembly.ApplyAttributeBuilder (a, ctor, cdata, pa);
479 if (a.Type == pa.DefaultCharset) {
480 switch (a.GetCharSetValue ()) {
485 DefaultCharSet = CharSet.Auto;
486 DefaultCharSetType = TypeAttributes.AutoClass;
488 case CharSet.Unicode:
489 DefaultCharSet = CharSet.Unicode;
490 DefaultCharSetType = TypeAttributes.UnicodeClass;
493 Report.Error (1724, a.Location, "Value specified for the argument to `{0}' is not valid",
494 a.GetSignatureForError ());
497 } else if (a.Type == pa.CLSCompliant) {
498 Attribute cls = DeclaringAssembly.CLSCompliantAttribute;
500 Report.Warning (3012, 1, a.Location,
501 "You must specify the CLSCompliant attribute on the assembly, not the module, to enable CLS compliance checking");
502 } else if (DeclaringAssembly.IsCLSCompliant != a.GetBoolean ()) {
503 Report.SymbolRelatedToPreviousError (cls.Location, cls.GetSignatureForError ());
504 Report.Warning (3017, 1, a.Location,
505 "You cannot specify the CLSCompliant attribute on a module that differs from the CLSCompliant attribute on the assembly");
510 builder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), cdata);
513 public override void CloseContainer ()
515 if (anonymous_types != null) {
516 foreach (var atypes in anonymous_types)
517 foreach (var at in atypes.Value)
518 at.CloseContainer ();
521 base.CloseContainer ();
524 public TypeBuilder CreateBuilder (string name, TypeAttributes attr, int typeSize)
526 return builder.DefineType (name, attr, null, typeSize);
530 // Creates alias global namespace
532 public RootNamespace CreateRootNamespace (string alias)
534 if (alias == global_ns.Alias) {
535 RootNamespace.Error_GlobalNamespaceRedefined (Report, Location.Null);
540 if (!alias_ns.TryGetValue (alias, out rn)) {
541 rn = new RootNamespace (alias);
542 alias_ns.Add (alias, rn);
548 public void Create (AssemblyDefinition assembly, ModuleBuilder moduleBuilder)
550 this.assembly = assembly;
551 builder = moduleBuilder;
554 public override bool Define ()
558 ExpandBaseInterfaces ();
562 HasTypesFullyDefined = true;
567 public override bool DefineContainer ()
571 return base.DefineContainer ();
574 public void EnableRedefinition ()
579 public override void EmitContainer ()
581 if (OptAttributes != null)
582 OptAttributes.Emit ();
584 if (Compiler.Settings.Unsafe && !assembly.IsSatelliteAssembly) {
585 var pa = PredefinedAttributes.UnverifiableCode;
587 pa.EmitAttribute (builder);
590 foreach (var tc in containers) {
594 base.EmitContainer ();
596 if (Compiler.Report.Errors == 0 && !Compiler.Settings.WriteMetadataOnly)
599 if (anonymous_types != null) {
600 foreach (var atypes in anonymous_types)
601 foreach (var at in atypes.Value)
606 internal override void GenerateDocComment (DocumentationBuilder builder)
608 foreach (var tc in containers)
609 tc.GenerateDocComment (builder);
612 public AnonymousTypeClass GetAnonymousType (IList<AnonymousTypeParameter> parameters)
614 List<AnonymousTypeClass> candidates;
615 if (!anonymous_types.TryGetValue (parameters.Count, out candidates))
619 foreach (AnonymousTypeClass at in candidates) {
620 for (i = 0; i < parameters.Count; ++i) {
621 if (!parameters [i].Equals (at.Parameters [i]))
625 if (i == parameters.Count)
633 // Return container with awaiter definition. It never returns null
634 // but all container member can be null for easier error reporting
636 public AwaiterDefinition GetAwaiter (TypeSpec type)
638 AwaiterDefinition awaiter;
639 if (awaiters.TryGetValue (type, out awaiter))
642 awaiter = new AwaiterDefinition ();
645 // Predefined: bool IsCompleted { get; }
647 awaiter.IsCompleted = MemberCache.FindMember (type, MemberFilter.Property ("IsCompleted", Compiler.BuiltinTypes.Bool),
648 BindingRestriction.InstanceOnly) as PropertySpec;
651 // Predefined: GetResult ()
653 // The method return type is also result type of await expression
655 awaiter.GetResult = MemberCache.FindMember (type, MemberFilter.Method ("GetResult", 0,
656 ParametersCompiled.EmptyReadOnlyParameters, null),
657 BindingRestriction.InstanceOnly) as MethodSpec;
660 // Predefined: INotifyCompletion.OnCompleted (System.Action)
662 var nc = PredefinedTypes.INotifyCompletion;
663 awaiter.INotifyCompletion = !nc.Define () || type.ImplementsInterface (nc.TypeSpec, false);
665 awaiters.Add (type, awaiter);
669 public override void GetCompletionStartingWith (string prefix, List<string> results)
671 var names = Evaluator.GetVarNames ();
672 results.AddRange (names.Where (l => l.StartsWith (prefix)));
675 public RootNamespace GetRootNamespace (string name)
678 alias_ns.TryGetValue (name, out rn);
682 public override string GetSignatureForError ()
687 public Binary.PredefinedOperator[] GetPredefinedEnumAritmeticOperators (TypeSpec enumType, bool nullable)
690 Binary.Operator mask = 0;
693 underlying = Nullable.NullableInfo.GetEnumUnderlyingType (this, enumType);
694 mask = Binary.Operator.NullableMask;
696 underlying = EnumSpec.GetUnderlyingType (enumType);
699 var operators = new[] {
700 new Binary.PredefinedOperator (enumType, underlying,
701 mask | Binary.Operator.AdditionMask | Binary.Operator.SubtractionMask | Binary.Operator.DecomposedMask, enumType),
702 new Binary.PredefinedOperator (underlying, enumType,
703 mask | Binary.Operator.AdditionMask | Binary.Operator.SubtractionMask | Binary.Operator.DecomposedMask, enumType),
704 new Binary.PredefinedOperator (enumType, mask | Binary.Operator.SubtractionMask, underlying)
710 public void InitializePredefinedTypes ()
712 predefined_attributes = new PredefinedAttributes (this);
713 predefined_types = new PredefinedTypes (this);
714 predefined_members = new PredefinedMembers (this);
716 OperatorsBinaryEqualityLifted = Binary.CreateEqualityLiftedOperatorsTable (this);
717 OperatorsBinaryLifted = Binary.CreateStandardLiftedOperatorsTable (this);
720 public override bool IsClsComplianceRequired ()
722 return DeclaringAssembly.IsCLSCompliant;
725 public Attribute ResolveAssemblyAttribute (PredefinedAttribute a_type)
727 Attribute a = OptAttributes.Search ("assembly", a_type);
734 public void SetDeclaringAssembly (AssemblyDefinition assembly)
736 // TODO: This setter is quite ugly but I have not found a way around it yet
737 this.assembly = assembly;
740 public void LoadGetResourceStrings (string fileName)
742 if (!File.Exists (fileName)) {
743 Report.Error (1566, "Error reading resource file `{0}'", fileName);
747 foreach (var l in File.ReadLines (fileName)) {
748 if (GetResourceStrings == null)
749 GetResourceStrings = new Dictionary<string, string> ();
751 var line = l.Trim ();
752 if (line.Length == 0 || line [0] == '#' || line [0] == ';')
755 var epos = line.IndexOf ('=');
759 var key = line.Substring (0, epos).Trim ();
760 var value = line.Substring (epos + 1).Trim ();
762 GetResourceStrings [key] = value;