2 // codegen.cs: The code generator
5 // Miguel de Icaza (miguel@ximian.com)
7 // (C) 2001, 2002, 2003 Ximian, Inc.
8 // (C) 2004 Novell, Inc.
17 using System.Collections;
18 using System.Collections.Specialized;
19 using System.Reflection;
20 using System.Reflection.Emit;
21 using System.Runtime.InteropServices;
22 using System.Security;
23 using System.Security.Cryptography;
24 using System.Security.Permissions;
26 using Mono.Security.Cryptography;
28 namespace Mono.CSharp {
31 /// Code generator class.
33 public class CodeGen {
34 static AppDomain current_domain;
35 static public SymbolWriter SymbolWriter;
37 public static AssemblyClass Assembly;
38 public static ModuleClass Module;
45 public static void Reset ()
47 Assembly = new AssemblyClass ();
48 Module = new ModuleClass (RootContext.Unsafe);
51 public static string Basename (string name)
53 int pos = name.LastIndexOf ('/');
56 return name.Substring (pos + 1);
58 pos = name.LastIndexOf ('\\');
60 return name.Substring (pos + 1);
65 public static string Dirname (string name)
67 int pos = name.LastIndexOf ('/');
70 return name.Substring (0, pos);
72 pos = name.LastIndexOf ('\\');
74 return name.Substring (0, pos);
79 static public string FileName;
82 // Initializes the symbol writer
84 static void InitializeSymbolWriter (string filename)
86 SymbolWriter = SymbolWriter.GetSymbolWriter (Module.Builder, filename);
89 // If we got an ISymbolWriter instance, initialize it.
91 if (SymbolWriter == null) {
93 -18, 1, "Could not find the symbol writer assembly (Mono.CompilerServices.SymbolWriter.dll). This is normally an installation problem. Please make sure to compile and install the mcs/class/Mono.CompilerServices.SymbolWriter directory.");
99 // Initializes the code generator variables
101 static public bool Init (string name, string output, bool want_debugging_support)
104 AssemblyName an = Assembly.GetAssemblyName (name, output);
108 if (an.KeyPair != null) {
109 // If we are going to strong name our assembly make
110 // sure all its refs are strong named
111 foreach (Assembly a in RootNamespace.Global.Assemblies) {
112 AssemblyName ref_name = a.GetName ();
113 byte [] b = ref_name.GetPublicKeyToken ();
114 if (b == null || b.Length == 0) {
115 Report.Error (1577, "Assembly generation failed " +
116 "-- Referenced assembly '" +
118 "' does not have a strong name.");
119 //Environment.Exit (1);
124 current_domain = AppDomain.CurrentDomain;
127 Assembly.Builder = current_domain.DefineDynamicAssembly (an,
128 AssemblyBuilderAccess.Save, Dirname (name));
130 catch (ArgumentException) {
131 // specified key may not be exportable outside it's container
132 if (RootContext.StrongNameKeyContainer != null) {
133 Report.Error (1548, "Could not access the key inside the container `" +
134 RootContext.StrongNameKeyContainer + "'.");
135 Environment.Exit (1);
139 catch (CryptographicException) {
140 if ((RootContext.StrongNameKeyContainer != null) || (RootContext.StrongNameKeyFile != null)) {
141 Report.Error (1548, "Could not use the specified key to strongname the assembly.");
142 Environment.Exit (1);
148 // Pass a path-less name to DefineDynamicModule. Wonder how
149 // this copes with output in different directories then.
150 // FIXME: figure out how this copes with --output /tmp/blah
152 // If the third argument is true, the ModuleBuilder will dynamically
153 // load the default symbol writer.
155 Module.Builder = Assembly.Builder.DefineDynamicModule (
156 Basename (name), Basename (output), false);
158 if (want_debugging_support)
159 InitializeSymbolWriter (output);
164 static public void Save (string name)
167 Assembly.Builder.Save (Basename (name));
169 if (SymbolWriter != null)
170 SymbolWriter.WriteSymbolFile ();
172 catch (COMException) {
173 if ((RootContext.StrongNameKeyFile == null) || (!RootContext.StrongNameDelaySign))
176 // FIXME: it seems Microsoft AssemblyBuilder doesn't like to delay sign assemblies
177 Report.Error (1548, "Couldn't delay-sign the assembly with the '" +
178 RootContext.StrongNameKeyFile +
179 "', Use MCS with the Mono runtime or CSC to compile this assembly.");
181 catch (System.IO.IOException io) {
182 Report.Error (16, "Could not write to file `"+name+"', cause: " + io.Message);
184 catch (System.UnauthorizedAccessException ua) {
185 Report.Error (16, "Could not write to file `"+name+"', cause: " + ua.Message);
191 public interface IResolveContext
193 DeclSpace DeclContainer { get; }
194 bool IsInObsoleteScope { get; }
195 bool IsInUnsafeScope { get; }
199 /// An Emit Context is created for each body of code (from methods,
200 /// properties bodies, indexer bodies or constructor bodies)
202 public class EmitContext : IResolveContext {
205 public DeclSpace TypeContainer;
206 public ILGenerator ig;
209 public enum Flags : byte {
211 /// This flag tracks the `checked' state of the compilation,
212 /// it controls whether we should generate code that does overflow
213 /// checking, or if we generate code that ignores overflows.
215 /// The default setting comes from the command line option to generate
216 /// checked or unchecked code plus any source code changes using the
217 /// checked/unchecked statements or expressions. Contrast this with
218 /// the ConstantCheckState flag.
223 /// The constant check state is always set to `true' and cant be changed
224 /// from the command line. The source code can change this setting with
225 /// the `checked' and `unchecked' statements and expressions.
227 ConstantCheckState = 1 << 1,
229 AllCheckStateFlags = CheckState | ConstantCheckState,
232 /// Whether we are inside an unsafe block
240 /// Whether control flow analysis is enabled
242 DoFlowAnalysis = 1 << 5,
245 /// Whether control flow analysis is disabled on structs
246 /// (only meaningful when DoFlowAnalysis is set)
248 OmitStructFlowAnalysis = 1 << 6
254 /// Whether we are emitting code inside a static or instance method
256 public bool IsStatic;
259 /// Whether the actual created method is static or instance method.
260 /// Althoug the method might be declared as `static', if an anonymous
261 /// method is involved, we might turn this into an instance method.
263 /// So this reflects the low-level staticness of the method, while
264 /// IsStatic represents the semantic, high-level staticness.
266 public bool MethodIsStatic;
269 /// Whether we are emitting a field initializer
271 public bool IsFieldInitializer;
274 /// The value that is allowed to be returned or NULL if there is no
277 public Type ReturnType;
280 /// Points to the Type (extracted from the TypeContainer) that
281 /// declares this body of code
283 public Type ContainerType;
286 /// Whether this is generating code for a constructor
288 public bool IsConstructor;
291 /// Keeps track of the Type to LocalBuilder temporary storage created
292 /// to store structures (used to compute the address of the structure
293 /// value on structure method invocations)
295 public Hashtable temporary_storage;
297 public Block CurrentBlock;
299 public int CurrentFile;
302 /// The location where we store the return value.
304 LocalBuilder return_value;
307 /// The location where return has to jump to return the
310 public Label ReturnLabel;
313 /// If we already defined the ReturnLabel
315 public bool HasReturnLabel;
318 /// Whether we are inside an iterator block.
320 public bool InIterator;
322 public bool IsLastStatement;
325 /// Whether we are in a `fixed' initialization
327 public bool InFixedInitializer;
330 /// Whether we are inside an anonymous method.
332 public AnonymousContainer CurrentAnonymousMethod;
335 /// Location for this EmitContext
340 /// Inside an enum definition, we do not resolve enumeration values
341 /// to their enumerations, but rather to the underlying type/value
342 /// This is so EnumVal + EnumValB can be evaluated.
344 /// There is no "E operator + (E x, E y)", so during an enum evaluation
345 /// we relax the rules
347 public bool InEnumContext;
350 /// Anonymous methods can capture local variables and fields,
351 /// this object tracks it. It is copied from the TopLevelBlock
354 public CaptureContext capture_context;
356 public readonly IResolveContext ResolveContext;
359 /// The current iterator
361 public Iterator CurrentIterator;
364 /// Whether we are in the resolving stage or not
372 bool isAnonymousMethodAllowed = true;
375 FlowBranching current_flow_branching;
377 static int next_id = 0;
380 public override string ToString ()
382 return String.Format ("EmitContext ({0}:{1}:{2})", id,
383 CurrentIterator, capture_context, loc);
386 public EmitContext (IResolveContext rc, DeclSpace parent, DeclSpace ds, Location l, ILGenerator ig,
387 Type return_type, int code_flags, bool is_constructor)
389 this.ResolveContext = rc;
392 TypeContainer = parent;
394 if (RootContext.Checked)
395 flags |= Flags.CheckState;
396 flags |= Flags.ConstantCheckState;
398 IsStatic = (code_flags & Modifiers.STATIC) != 0;
399 MethodIsStatic = IsStatic;
400 InIterator = (code_flags & Modifiers.METHOD_YIELDS) != 0;
401 ReturnType = return_type;
402 IsConstructor = is_constructor;
405 current_phase = Phase.Created;
408 // Can only be null for the ResolveType contexts.
409 ContainerType = parent.TypeBuilder;
410 if (rc.IsInUnsafeScope)
411 flags |= Flags.InUnsafe;
415 if (ReturnType == TypeManager.void_type)
419 public EmitContext (IResolveContext rc, DeclSpace ds, Location l, ILGenerator ig,
420 Type return_type, int code_flags, bool is_constructor)
421 : this (rc, ds, ds, l, ig, return_type, code_flags, is_constructor)
425 public EmitContext (IResolveContext rc, DeclSpace ds, Location l, ILGenerator ig,
426 Type return_type, int code_flags)
427 : this (rc, ds, ds, l, ig, return_type, code_flags, false)
431 public DeclSpace DeclContainer {
432 get { return declSpace; }
433 set { declSpace = value; }
436 public bool CheckState {
437 get { return (flags & Flags.CheckState) != 0; }
440 public bool ConstantCheckState {
441 get { return (flags & Flags.ConstantCheckState) != 0; }
444 public bool InUnsafe {
445 get { return (flags & Flags.InUnsafe) != 0; }
448 public bool InCatch {
449 get { return (flags & Flags.InCatch) != 0; }
452 public bool InFinally {
453 get { return (flags & Flags.InFinally) != 0; }
456 public bool DoFlowAnalysis {
457 get { return (flags & Flags.DoFlowAnalysis) != 0; }
460 public bool OmitStructFlowAnalysis {
461 get { return (flags & Flags.OmitStructFlowAnalysis) != 0; }
464 // utility helper for CheckExpr, UnCheckExpr, Checked and Unchecked statements
465 // it's public so that we can use a struct at the callsite
466 public struct FlagsHandle : IDisposable
469 Flags invmask, oldval;
470 internal FlagsHandle (EmitContext ec, Flags mask, Flags val)
474 oldval = ec.flags & mask;
475 ec.flags = (ec.flags & invmask) | (val & mask);
477 public void Dispose ()
479 ec.flags = (ec.flags & invmask) | oldval;
483 // Temporarily set all the given flags to the given value. Should be used in an 'using' statement
484 public FlagsHandle With (Flags bits, bool enable)
486 return new FlagsHandle (this, bits, enable ? bits : 0);
489 public FlagsHandle WithFlowAnalysis (bool do_flow_analysis, bool omit_struct_analysis)
492 (do_flow_analysis ? Flags.DoFlowAnalysis : 0) |
493 (omit_struct_analysis ? Flags.OmitStructFlowAnalysis : 0);
494 return new FlagsHandle (this, Flags.DoFlowAnalysis | Flags.OmitStructFlowAnalysis, newflags);
497 public bool IsInObsoleteScope {
498 get { return ResolveContext.IsInObsoleteScope; }
501 public bool IsInUnsafeScope {
502 get { return InUnsafe || ResolveContext.IsInUnsafeScope; }
505 public bool IsAnonymousMethodAllowed {
506 get { return isAnonymousMethodAllowed; }
507 set { isAnonymousMethodAllowed = value; }
510 public FlowBranching CurrentBranching {
511 get { return current_flow_branching; }
514 public bool HaveCaptureInfo {
515 get { return capture_context != null; }
518 public void EmitScopeInitFromBlock (Block b)
520 if (capture_context != null)
521 capture_context.EmitScopeInitFromBlock (this, b);
525 // Starts a new code branching. This inherits the state of all local
526 // variables and parameters from the current branching.
528 public FlowBranching StartFlowBranching (FlowBranching.BranchingType type, Location loc)
530 current_flow_branching = FlowBranching.CreateBranching (CurrentBranching, type, null, loc);
531 return current_flow_branching;
535 // Starts a new code branching for block `block'.
537 public FlowBranching StartFlowBranching (Block block)
539 FlowBranching.BranchingType type;
541 if ((CurrentBranching != null) &&
542 (CurrentBranching.Type == FlowBranching.BranchingType.Switch))
543 type = FlowBranching.BranchingType.SwitchSection;
545 type = FlowBranching.BranchingType.Block;
547 flags |= Flags.DoFlowAnalysis;
549 current_flow_branching = FlowBranching.CreateBranching (
550 CurrentBranching, type, block, block.StartLocation);
551 return current_flow_branching;
554 public FlowBranchingException StartFlowBranching (ExceptionStatement stmt)
556 FlowBranchingException branching = new FlowBranchingException (CurrentBranching, stmt);
557 current_flow_branching = branching;
561 public FlowBranchingLabeled StartFlowBranching (LabeledStatement stmt)
563 FlowBranchingLabeled branching = new FlowBranchingLabeled (CurrentBranching, stmt);
564 current_flow_branching = branching;
568 public FlowBranchingToplevel StartFlowBranching (ToplevelBlock stmt)
570 FlowBranchingToplevel branching = new FlowBranchingToplevel (CurrentBranching, stmt);
571 current_flow_branching = branching;
576 // Ends a code branching. Merges the state of locals and parameters
577 // from all the children of the ending branching.
579 public FlowBranching.UsageVector DoEndFlowBranching ()
581 FlowBranching old = current_flow_branching;
582 current_flow_branching = current_flow_branching.Parent;
584 return current_flow_branching.MergeChild (old);
588 // Ends a code branching. Merges the state of locals and parameters
589 // from all the children of the ending branching.
591 public FlowBranching.Reachability EndFlowBranching ()
593 FlowBranching.UsageVector vector = DoEndFlowBranching ();
595 return vector.Reachability;
599 // Kills the current code branching. This throws away any changed state
600 // information and should only be used in case of an error.
602 public void KillFlowBranching ()
604 current_flow_branching = current_flow_branching.Parent;
607 public void CaptureVariable (LocalInfo li)
609 capture_context.AddLocal (CurrentAnonymousMethod, li);
610 li.IsCaptured = true;
613 public void CaptureParameter (string name, Type t, int idx)
615 capture_context.AddParameter (this, CurrentAnonymousMethod, name, t, idx);
618 public void CaptureThis ()
620 capture_context.CaptureThis (CurrentAnonymousMethod);
625 // Use to register a field as captured
627 public void CaptureField (FieldExpr fe)
629 capture_context.AddField (this, CurrentAnonymousMethod, fe);
633 // Whether anonymous methods have captured variables
635 public bool HaveCapturedVariables ()
637 if (capture_context != null)
638 return capture_context.HaveCapturedVariables;
643 // Whether anonymous methods have captured fields or this.
645 public bool HaveCapturedFields ()
647 if (capture_context != null)
648 return capture_context.HaveCapturedFields;
653 // Emits the instance pointer for the host method
655 public void EmitMethodHostInstance (EmitContext target, AnonymousMethod am)
657 if (capture_context != null)
658 capture_context.EmitMethodHostInstance (target, am);
660 target.ig.Emit (OpCodes.Ldnull);
662 target.ig.Emit (OpCodes.Ldarg_0);
666 // Returns whether the `local' variable has been captured by an anonymous
669 public bool IsCaptured (LocalInfo local)
671 return capture_context.IsCaptured (local);
674 public bool IsParameterCaptured (string name)
676 if (capture_context != null)
677 return capture_context.IsParameterCaptured (name);
681 public void EmitMeta (ToplevelBlock b)
683 if (capture_context != null)
684 capture_context.EmitAnonymousHelperClasses (this);
688 ReturnLabel = ig.DefineLabel ();
692 // Here until we can fix the problem with Mono.CSharp.Switch, which
693 // currently can not cope with ig == null during resolve (which must
694 // be fixed for switch statements to work on anonymous methods).
696 public void EmitTopBlock (IMethodData md, ToplevelBlock block)
703 if (ResolveTopBlock (null, block, md.ParameterInfo, md, out unreachable)){
706 current_phase = Phase.Emitting;
707 EmitResolvedTopBlock (block, unreachable);
713 public bool ResolveTopBlock (EmitContext anonymous_method_host, ToplevelBlock block,
714 Parameters ip, IMethodData md, out bool unreachable)
716 current_phase = Phase.Resolving;
723 capture_context = block.CaptureContext;
726 CurrentFile = loc.File;
731 if (!block.ResolveMeta (this, ip))
734 using (this.With (EmitContext.Flags.DoFlowAnalysis, true)) {
735 FlowBranchingToplevel top_level;
736 if (anonymous_method_host != null)
737 top_level = new FlowBranchingToplevel (anonymous_method_host.CurrentBranching, block);
739 top_level = block.TopLevelBranching;
741 current_flow_branching = top_level;
742 bool ok = block.Resolve (this);
743 current_flow_branching = null;
748 FlowBranching.Reachability reachability = top_level.End ();
749 if (reachability.IsUnreachable)
753 } catch (Exception e) {
754 Console.WriteLine ("Exception caught by the compiler while compiling:");
755 Console.WriteLine (" Block that caused the problem begin at: " + loc);
757 if (CurrentBlock != null){
758 Console.WriteLine (" Block being compiled: [{0},{1}]",
759 CurrentBlock.StartLocation, CurrentBlock.EndLocation);
761 Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
766 if (ReturnType != null && !unreachable) {
767 if (CurrentAnonymousMethod == null) {
768 Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
770 } else if (!CurrentAnonymousMethod.IsIterator) {
771 Report.Error (1643, CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
772 CurrentAnonymousMethod.GetSignatureForError ());
777 block.CompleteContexts ();
782 public void EmitResolvedTopBlock (ToplevelBlock block, bool unreachable)
788 ig.MarkLabel (ReturnLabel);
790 if (return_value != null){
791 ig.Emit (OpCodes.Ldloc, return_value);
792 ig.Emit (OpCodes.Ret);
795 // If `HasReturnLabel' is set, then we already emitted a
796 // jump to the end of the method, so we must emit a `ret'
799 // Unfortunately, System.Reflection.Emit automatically emits
800 // a leave to the end of a finally block. This is a problem
801 // if no code is following the try/finally block since we may
802 // jump to a point after the end of the method.
803 // As a workaround, we're always creating a return label in
807 bool in_iterator = (CurrentAnonymousMethod != null) &&
808 CurrentAnonymousMethod.IsIterator && InIterator;
810 if ((block != null) && block.IsDestructor) {
811 // Nothing to do; S.R.E automatically emits a leave.
812 } else if (HasReturnLabel || (!unreachable && !in_iterator)) {
813 if (ReturnType != null)
814 ig.Emit (OpCodes.Ldloc, TemporaryReturn ());
815 ig.Emit (OpCodes.Ret);
820 // Close pending helper classes if we are the toplevel
822 if (capture_context != null && capture_context.ParentToplevel == null)
823 capture_context.CloseAnonymousHelperClasses ();
827 /// This is called immediately before emitting an IL opcode to tell the symbol
828 /// writer to which source line this opcode belongs.
830 public void Mark (Location loc, bool check_file)
832 if ((CodeGen.SymbolWriter == null) || loc.IsNull)
835 if (check_file && (CurrentFile != loc.File))
838 CodeGen.SymbolWriter.MarkSequencePoint (ig, loc.Row, loc.Column);
841 public void DefineLocalVariable (string name, LocalBuilder builder)
843 if (CodeGen.SymbolWriter == null)
846 CodeGen.SymbolWriter.DefineLocalVariable (name, builder);
849 public void BeginScope ()
853 if (CodeGen.SymbolWriter != null)
854 CodeGen.SymbolWriter.OpenScope(ig);
857 public void EndScope ()
861 if (CodeGen.SymbolWriter != null)
862 CodeGen.SymbolWriter.CloseScope(ig);
866 /// Returns a temporary storage for a variable of type t as
867 /// a local variable in the current body.
869 public LocalBuilder GetTemporaryLocal (Type t)
871 if (temporary_storage != null) {
872 object o = temporary_storage [t];
876 o = s.Count == 0 ? null : s.Pop ();
878 temporary_storage.Remove (t);
882 return (LocalBuilder) o;
884 return ig.DeclareLocal (t);
887 public void FreeTemporaryLocal (LocalBuilder b, Type t)
891 if (temporary_storage == null) {
892 temporary_storage = new Hashtable ();
893 temporary_storage [t] = b;
896 object o = temporary_storage [t];
898 temporary_storage [t] = b;
906 temporary_storage [t] = s;
912 /// Current loop begin and end labels.
914 public Label LoopBegin, LoopEnd;
917 /// Default target in a switch statement. Only valid if
920 public Label DefaultTarget;
923 /// If this is non-null, points to the current switch statement
925 public Switch Switch;
928 /// ReturnValue creates on demand the LocalBuilder for the
929 /// return value from the function. By default this is not
930 /// used. This is only required when returns are found inside
931 /// Try or Catch statements.
933 /// This method is typically invoked from the Emit phase, so
934 /// we allow the creation of a return label if it was not
935 /// requested during the resolution phase. Could be cleaned
936 /// up, but it would replicate a lot of logic in the Emit phase
937 /// of the code that uses it.
939 public LocalBuilder TemporaryReturn ()
941 if (return_value == null){
942 return_value = ig.DeclareLocal (ReturnType);
943 if (!HasReturnLabel){
944 ReturnLabel = ig.DefineLabel ();
945 HasReturnLabel = true;
953 /// This method is used during the Resolution phase to flag the
954 /// need to define the ReturnLabel
956 public void NeedReturnLabel ()
958 if (current_phase != Phase.Resolving){
960 // The reason is that the `ReturnLabel' is declared between
961 // resolution and emission
963 throw new Exception ("NeedReturnLabel called from Emit phase, should only be called during Resolve");
966 if (!InIterator && !HasReturnLabel)
967 HasReturnLabel = true;
971 // Emits the proper object to address fields on a remapped
972 // variable/parameter to field in anonymous-method/iterator proxy classes.
974 public void EmitThis (bool need_address)
976 ig.Emit (OpCodes.Ldarg_0);
977 if (capture_context != null && CurrentAnonymousMethod != null){
978 ScopeInfo si = CurrentAnonymousMethod.Scope;
980 if (si.ParentLink != null)
981 ig.Emit (OpCodes.Ldfld, si.ParentLink);
982 if (si.THIS != null){
983 if (need_address && TypeManager.IsValueType (si.THIS.FieldType))
984 ig.Emit (OpCodes.Ldflda, si.THIS);
986 ig.Emit (OpCodes.Ldfld, si.THIS);
995 // Emits the code necessary to load the instance required
996 // to access the captured LocalInfo
998 public void EmitCapturedVariableInstance (LocalInfo li)
1000 if (capture_context == null)
1001 throw new Exception ("Calling EmitCapturedContext when there is no capture_context");
1003 capture_context.EmitCapturedVariableInstance (this, li, CurrentAnonymousMethod);
1006 public void EmitParameter (string name, bool leave_copy, bool prepared, ref LocalTemporary temp)
1008 capture_context.EmitParameter (this, name, leave_copy, prepared, ref temp);
1011 public void EmitAssignParameter (string name, Expression source, bool leave_copy, bool prepare_for_load, ref LocalTemporary temp)
1013 capture_context.EmitAssignParameter (this, name, source, leave_copy, prepare_for_load, ref temp);
1016 public void EmitAddressOfParameter (string name)
1018 capture_context.EmitAddressOfParameter (this, name);
1021 public Expression GetThis (Location loc)
1024 if (CurrentBlock != null)
1025 my_this = new This (CurrentBlock, loc);
1027 my_this = new This (loc);
1029 if (!my_this.ResolveBase (this))
1037 public abstract class CommonAssemblyModulClass : Attributable, IResolveContext {
1039 protected CommonAssemblyModulClass ():
1044 public void AddAttributes (ArrayList attrs)
1046 foreach (Attribute a in attrs)
1049 if (attributes == null) {
1050 attributes = new Attributes (attrs);
1053 attributes.AddAttributes (attrs);
1056 public virtual void Emit (TypeContainer tc)
1058 if (OptAttributes == null)
1061 OptAttributes.Emit ();
1064 protected Attribute ResolveAttribute (Type a_type)
1066 if (OptAttributes == null)
1069 // Ensure that we only have GlobalAttributes, since the Search below isn't safe with other types.
1070 if (!OptAttributes.CheckTargets ())
1073 Attribute a = OptAttributes.Search (a_type);
1080 public override IResolveContext ResolveContext {
1081 get { return this; }
1084 #region IResolveContext Members
1086 public DeclSpace DeclContainer {
1087 get { return RootContext.ToplevelTypes; }
1090 public bool IsInObsoleteScope {
1091 get { return false; }
1094 public bool IsInUnsafeScope {
1095 get { return false; }
1101 public class AssemblyClass : CommonAssemblyModulClass {
1102 // TODO: make it private and move all builder based methods here
1103 public AssemblyBuilder Builder;
1104 bool is_cls_compliant;
1105 bool wrap_non_exception_throws;
1107 public Attribute ClsCompliantAttribute;
1109 ListDictionary declarative_security;
1111 // Module is here just because of error messages
1112 static string[] attribute_targets = new string [] { "assembly", "module" };
1114 public AssemblyClass (): base ()
1116 wrap_non_exception_throws = true;
1119 public bool IsClsCompliant {
1121 return is_cls_compliant;
1125 public bool WrapNonExceptionThrows {
1127 return wrap_non_exception_throws;
1131 public override AttributeTargets AttributeTargets {
1133 return AttributeTargets.Assembly;
1137 public override bool IsClsComplianceRequired ()
1139 return is_cls_compliant;
1142 public void Resolve ()
1144 ClsCompliantAttribute = ResolveAttribute (TypeManager.cls_compliant_attribute_type);
1145 if (ClsCompliantAttribute != null) {
1146 is_cls_compliant = ClsCompliantAttribute.GetClsCompliantAttributeValue ();
1150 Attribute a = ResolveAttribute (TypeManager.runtime_compatibility_attr_type);
1152 object val = a.GetPropertyValue ("WrapNonExceptionThrows");
1154 wrap_non_exception_throws = (bool)val;
1160 private void SetPublicKey (AssemblyName an, byte[] strongNameBlob)
1163 // check for possible ECMA key
1164 if (strongNameBlob.Length == 16) {
1165 // will be rejected if not "the" ECMA key
1166 an.SetPublicKey (strongNameBlob);
1169 // take it, with or without, a private key
1170 RSA rsa = CryptoConvert.FromCapiKeyBlob (strongNameBlob);
1171 // and make sure we only feed the public part to Sys.Ref
1172 byte[] publickey = CryptoConvert.ToCapiPublicKeyBlob (rsa);
1174 // AssemblyName.SetPublicKey requires an additional header
1175 byte[] publicKeyHeader = new byte [12] { 0x00, 0x24, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00 };
1177 byte[] encodedPublicKey = new byte [12 + publickey.Length];
1178 Buffer.BlockCopy (publicKeyHeader, 0, encodedPublicKey, 0, 12);
1179 Buffer.BlockCopy (publickey, 0, encodedPublicKey, 12, publickey.Length);
1180 an.SetPublicKey (encodedPublicKey);
1184 Error_AssemblySigning ("The specified file `" + RootContext.StrongNameKeyFile + "' is incorrectly encoded");
1185 Environment.Exit (1);
1189 // TODO: rewrite this code (to kill N bugs and make it faster) and use standard ApplyAttribute way.
1190 public AssemblyName GetAssemblyName (string name, string output)
1192 if (OptAttributes != null) {
1193 foreach (Attribute a in OptAttributes.Attrs) {
1194 // cannot rely on any resolve-based members before you call Resolve
1195 if (a.ExplicitTarget == null || a.ExplicitTarget != "assembly")
1198 // TODO: This code is buggy: comparing Attribute name without resolving is wrong.
1199 // However, this is invoked by CodeGen.Init, when none of the namespaces
1201 // TODO: Does not handle quoted attributes properly
1203 case "AssemblyKeyFile":
1204 case "AssemblyKeyFileAttribute":
1205 case "System.Reflection.AssemblyKeyFileAttribute":
1206 if (RootContext.StrongNameKeyFile != null) {
1207 Report.SymbolRelatedToPreviousError (a.Location, a.Name);
1208 Report.Warning (1616, 1, "Option `{0}' overrides attribute `{1}' given in a source file or added module",
1209 "keyfile", "System.Reflection.AssemblyKeyFileAttribute");
1212 string value = a.GetString ();
1213 if (value.Length != 0)
1214 RootContext.StrongNameKeyFile = value;
1217 case "AssemblyKeyName":
1218 case "AssemblyKeyNameAttribute":
1219 case "System.Reflection.AssemblyKeyNameAttribute":
1220 if (RootContext.StrongNameKeyContainer != null) {
1221 Report.SymbolRelatedToPreviousError (a.Location, a.Name);
1222 Report.Warning (1616, 1, "Option `{0}' overrides attribute `{1}' given in a source file or added module",
1223 "keycontainer", "System.Reflection.AssemblyKeyNameAttribute");
1226 string value = a.GetString ();
1227 if (value.Length != 0)
1228 RootContext.StrongNameKeyContainer = value;
1231 case "AssemblyDelaySign":
1232 case "AssemblyDelaySignAttribute":
1233 case "System.Reflection.AssemblyDelaySignAttribute":
1234 RootContext.StrongNameDelaySign = a.GetBoolean ();
1240 AssemblyName an = new AssemblyName ();
1241 an.Name = Path.GetFileNameWithoutExtension (name);
1243 // note: delay doesn't apply when using a key container
1244 if (RootContext.StrongNameKeyContainer != null) {
1245 an.KeyPair = new StrongNameKeyPair (RootContext.StrongNameKeyContainer);
1249 // strongname is optional
1250 if (RootContext.StrongNameKeyFile == null)
1253 string AssemblyDir = Path.GetDirectoryName (output);
1255 // the StrongName key file may be relative to (a) the compiled
1256 // file or (b) to the output assembly. See bugzilla #55320
1257 // http://bugzilla.ximian.com/show_bug.cgi?id=55320
1259 // (a) relative to the compiled file
1260 string filename = Path.GetFullPath (RootContext.StrongNameKeyFile);
1261 bool exist = File.Exists (filename);
1262 if ((!exist) && (AssemblyDir != null) && (AssemblyDir != String.Empty)) {
1263 // (b) relative to the outputed assembly
1264 filename = Path.GetFullPath (Path.Combine (AssemblyDir, RootContext.StrongNameKeyFile));
1265 exist = File.Exists (filename);
1269 using (FileStream fs = new FileStream (filename, FileMode.Open, FileAccess.Read)) {
1270 byte[] snkeypair = new byte [fs.Length];
1271 fs.Read (snkeypair, 0, snkeypair.Length);
1273 if (RootContext.StrongNameDelaySign) {
1274 // delayed signing - DO NOT include private key
1275 SetPublicKey (an, snkeypair);
1278 // no delay so we make sure we have the private key
1280 CryptoConvert.FromCapiPrivateKeyBlob (snkeypair);
1281 an.KeyPair = new StrongNameKeyPair (snkeypair);
1283 catch (CryptographicException) {
1284 if (snkeypair.Length == 16) {
1285 // error # is different for ECMA key
1286 Report.Error (1606, "Could not sign the assembly. " +
1287 "ECMA key can only be used to delay-sign assemblies");
1290 Error_AssemblySigning ("The specified file `" + RootContext.StrongNameKeyFile + "' does not have a private key");
1298 Error_AssemblySigning ("The specified file `" + RootContext.StrongNameKeyFile + "' does not exist");
1304 void Error_AssemblySigning (string text)
1306 Report.Error (1548, "Error during assembly signing. " + text);
1309 public override void ApplyAttributeBuilder (Attribute a, CustomAttributeBuilder customBuilder)
1311 if (a.Type.IsSubclassOf (TypeManager.security_attr_type) && a.CheckSecurityActionValidity (true)) {
1312 if (declarative_security == null)
1313 declarative_security = new ListDictionary ();
1315 a.ExtractSecurityPermissionSet (declarative_security);
1319 if (a.Type == TypeManager.assembly_culture_attribute_type) {
1320 string value = a.GetString ();
1321 if (value == null || value.Length == 0)
1324 if (RootContext.Target == Target.Exe) {
1325 a.Error_AttributeEmitError ("The executables cannot be satelite assemblies, remove the attribute or keep it empty");
1330 Builder.SetCustomAttribute (customBuilder);
1333 public override void Emit (TypeContainer tc)
1337 if (declarative_security != null) {
1339 MethodInfo add_permission = typeof (AssemblyBuilder).GetMethod ("AddPermissionRequests", BindingFlags.Instance | BindingFlags.NonPublic);
1340 object builder_instance = Builder;
1343 // Microsoft runtime hacking
1344 if (add_permission == null) {
1345 Type assembly_builder = typeof (AssemblyBuilder).Assembly.GetType ("System.Reflection.Emit.AssemblyBuilderData");
1346 add_permission = assembly_builder.GetMethod ("AddPermissionRequests", BindingFlags.Instance | BindingFlags.NonPublic);
1348 FieldInfo fi = typeof (AssemblyBuilder).GetField ("m_assemblyData", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.GetField);
1349 builder_instance = fi.GetValue (Builder);
1352 object[] args = new object [] { declarative_security [SecurityAction.RequestMinimum],
1353 declarative_security [SecurityAction.RequestOptional],
1354 declarative_security [SecurityAction.RequestRefuse] };
1355 add_permission.Invoke (builder_instance, args);
1358 Report.RuntimeMissingSupport (Location.Null, "assembly permission setting");
1363 public override string[] ValidAttributeTargets {
1365 return attribute_targets;
1370 public class ModuleClass : CommonAssemblyModulClass {
1371 // TODO: make it private and move all builder based methods here
1372 public ModuleBuilder Builder;
1373 bool m_module_is_unsafe;
1375 public CharSet DefaultCharSet = CharSet.Ansi;
1376 public TypeAttributes DefaultCharSetType = TypeAttributes.AnsiClass;
1378 static string[] attribute_targets = new string [] { "module" };
1380 public ModuleClass (bool is_unsafe)
1382 m_module_is_unsafe = is_unsafe;
1385 public override AttributeTargets AttributeTargets {
1387 return AttributeTargets.Module;
1391 public override bool IsClsComplianceRequired ()
1393 return CodeGen.Assembly.IsClsCompliant;
1396 public override void Emit (TypeContainer tc)
1400 if (!m_module_is_unsafe)
1403 if (TypeManager.unverifiable_code_ctor == null) {
1404 Console.WriteLine ("Internal error ! Cannot set unverifiable code attribute.");
1408 Builder.SetCustomAttribute (new CustomAttributeBuilder (TypeManager.unverifiable_code_ctor, new object [0]));
1411 public override void ApplyAttributeBuilder (Attribute a, CustomAttributeBuilder customBuilder)
1413 if (a.Type == TypeManager.cls_compliant_attribute_type) {
1414 if (CodeGen.Assembly.ClsCompliantAttribute == null) {
1415 Report.Warning (3012, 1, a.Location, "You must specify the CLSCompliant attribute on the assembly, not the module, to enable CLS compliance checking");
1417 else if (CodeGen.Assembly.IsClsCompliant != a.GetBoolean ()) {
1418 Report.SymbolRelatedToPreviousError (CodeGen.Assembly.ClsCompliantAttribute.Location, CodeGen.Assembly.ClsCompliantAttribute.GetSignatureForError ());
1419 Report.Error (3017, a.Location, "You cannot specify the CLSCompliant attribute on a module that differs from the CLSCompliant attribute on the assembly");
1424 Builder.SetCustomAttribute (customBuilder);
1428 /// It is called very early therefore can resolve only predefined attributes
1430 public void ResolveAttributes ()
1433 Attribute a = ResolveAttribute (TypeManager.default_charset_type);
1435 DefaultCharSet = a.GetCharSetValue ();
1436 switch (DefaultCharSet) {
1441 DefaultCharSetType = TypeAttributes.AutoClass;
1443 case CharSet.Unicode:
1444 DefaultCharSetType = TypeAttributes.UnicodeClass;
1447 Report.Error (1724, a.Location, "Value specified for the argument to 'System.Runtime.InteropServices.DefaultCharSetAttribute' is not valid");
1454 public override string[] ValidAttributeTargets {
1456 return attribute_targets;