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 // Get the complete AssemblyName from the builder
149 // (We need to get the public key and token)
150 Assembly.Name = Assembly.Builder.GetName ();
154 // Pass a path-less name to DefineDynamicModule. Wonder how
155 // this copes with output in different directories then.
156 // FIXME: figure out how this copes with --output /tmp/blah
158 // If the third argument is true, the ModuleBuilder will dynamically
159 // load the default symbol writer.
161 Module.Builder = Assembly.Builder.DefineDynamicModule (
162 Basename (name), Basename (output), false);
164 if (want_debugging_support)
165 InitializeSymbolWriter (output);
170 static public void Save (string name)
173 Assembly.Builder.Save (Basename (name));
175 if (SymbolWriter != null)
176 SymbolWriter.WriteSymbolFile ();
178 catch (COMException) {
179 if ((RootContext.StrongNameKeyFile == null) || (!RootContext.StrongNameDelaySign))
182 // FIXME: it seems Microsoft AssemblyBuilder doesn't like to delay sign assemblies
183 Report.Error (1548, "Couldn't delay-sign the assembly with the '" +
184 RootContext.StrongNameKeyFile +
185 "', Use MCS with the Mono runtime or CSC to compile this assembly.");
187 catch (System.IO.IOException io) {
188 Report.Error (16, "Could not write to file `"+name+"', cause: " + io.Message);
190 catch (System.UnauthorizedAccessException ua) {
191 Report.Error (16, "Could not write to file `"+name+"', cause: " + ua.Message);
197 public interface IResolveContext
199 DeclSpace DeclContainer { get; }
200 bool IsInObsoleteScope { get; }
201 bool IsInUnsafeScope { get; }
203 // the declcontainer to lookup for type-parameters. Should only use LookupGeneric on it.
205 // FIXME: This is somewhat of a hack. We don't need a full DeclSpace for this. We just need the
206 // current type parameters in scope. IUIC, that will require us to rewrite GenericMethod.
207 // Maybe we can replace this with a 'LookupGeneric (string)' instead, but we'll have to
208 // handle generic method overrides differently
209 DeclSpace GenericDeclContainer { get; }
213 /// An Emit Context is created for each body of code (from methods,
214 /// properties bodies, indexer bodies or constructor bodies)
216 public class EmitContext : IResolveContext {
219 public DeclSpace TypeContainer;
220 public ILGenerator ig;
223 public enum Flags : byte {
225 /// This flag tracks the `checked' state of the compilation,
226 /// it controls whether we should generate code that does overflow
227 /// checking, or if we generate code that ignores overflows.
229 /// The default setting comes from the command line option to generate
230 /// checked or unchecked code plus any source code changes using the
231 /// checked/unchecked statements or expressions. Contrast this with
232 /// the ConstantCheckState flag.
237 /// The constant check state is always set to `true' and cant be changed
238 /// from the command line. The source code can change this setting with
239 /// the `checked' and `unchecked' statements and expressions.
241 ConstantCheckState = 1 << 1,
243 AllCheckStateFlags = CheckState | ConstantCheckState,
246 /// Whether we are inside an unsafe block
254 /// Whether control flow analysis is enabled
256 DoFlowAnalysis = 1 << 5,
259 /// Whether control flow analysis is disabled on structs
260 /// (only meaningful when DoFlowAnalysis is set)
262 OmitStructFlowAnalysis = 1 << 6
268 /// Whether we are emitting code inside a static or instance method
270 public bool IsStatic;
273 /// Whether the actual created method is static or instance method.
274 /// Althoug the method might be declared as `static', if an anonymous
275 /// method is involved, we might turn this into an instance method.
277 /// So this reflects the low-level staticness of the method, while
278 /// IsStatic represents the semantic, high-level staticness.
280 public bool MethodIsStatic;
283 /// Whether we are emitting a field initializer
285 public bool IsFieldInitializer;
288 /// The value that is allowed to be returned or NULL if there is no
291 public readonly Type ReturnType;
294 /// Points to the Type (extracted from the TypeContainer) that
295 /// declares this body of code
297 public Type ContainerType;
300 /// Whether this is generating code for a constructor
302 public bool IsConstructor;
305 /// Keeps track of the Type to LocalBuilder temporary storage created
306 /// to store structures (used to compute the address of the structure
307 /// value on structure method invocations)
309 public Hashtable temporary_storage;
311 public Block CurrentBlock;
313 public int CurrentFile;
316 /// The location where we store the return value.
318 LocalBuilder return_value;
321 /// The location where return has to jump to return the
324 public Label ReturnLabel;
327 /// If we already defined the ReturnLabel
329 public bool HasReturnLabel;
332 /// Whether we are inside an iterator block.
334 public bool InIterator;
336 public bool IsLastStatement;
339 /// Whether we are in a `fixed' initialization
341 public bool InFixedInitializer;
344 /// Whether we are inside an anonymous method.
346 public AnonymousContainer CurrentAnonymousMethod;
349 /// Location for this EmitContext
354 /// Inside an enum definition, we do not resolve enumeration values
355 /// to their enumerations, but rather to the underlying type/value
356 /// This is so EnumVal + EnumValB can be evaluated.
358 /// There is no "E operator + (E x, E y)", so during an enum evaluation
359 /// we relax the rules
361 public bool InEnumContext;
363 public readonly IResolveContext ResolveContext;
366 /// The current iterator
368 public Iterator CurrentIterator {
369 get { return CurrentAnonymousMethod as Iterator; }
373 /// Whether we are in the resolving stage or not
381 bool isAnonymousMethodAllowed = true;
384 FlowBranching current_flow_branching;
386 static int next_id = 0;
389 public override string ToString ()
391 return String.Format ("EmitContext ({0}:{1})", id,
392 CurrentAnonymousMethod, loc);
395 public EmitContext (IResolveContext rc, DeclSpace parent, DeclSpace ds, Location l, ILGenerator ig,
396 Type return_type, int code_flags, bool is_constructor)
398 this.ResolveContext = rc;
401 TypeContainer = parent;
403 if (RootContext.Checked)
404 flags |= Flags.CheckState;
405 flags |= Flags.ConstantCheckState;
408 if ((return_type is TypeBuilder) && return_type.IsGenericTypeDefinition)
409 throw new InternalErrorException ();
412 IsStatic = (code_flags & Modifiers.STATIC) != 0;
413 MethodIsStatic = IsStatic;
414 InIterator = (code_flags & Modifiers.METHOD_YIELDS) != 0;
415 ReturnType = return_type;
416 IsConstructor = is_constructor;
419 current_phase = Phase.Created;
422 // Can only be null for the ResolveType contexts.
423 ContainerType = parent.TypeBuilder;
424 if (rc.IsInUnsafeScope)
425 flags |= Flags.InUnsafe;
429 if (ReturnType == TypeManager.void_type)
433 public EmitContext (IResolveContext rc, DeclSpace ds, Location l, ILGenerator ig,
434 Type return_type, int code_flags, bool is_constructor)
435 : this (rc, ds, ds, l, ig, return_type, code_flags, is_constructor)
439 public EmitContext (IResolveContext rc, DeclSpace ds, Location l, ILGenerator ig,
440 Type return_type, int code_flags)
441 : this (rc, ds, ds, l, ig, return_type, code_flags, false)
445 public DeclSpace DeclContainer {
446 get { return declSpace; }
447 set { declSpace = value; }
450 public DeclSpace GenericDeclContainer {
451 get { return DeclContainer; }
454 public bool CheckState {
455 get { return (flags & Flags.CheckState) != 0; }
458 public bool ConstantCheckState {
459 get { return (flags & Flags.ConstantCheckState) != 0; }
462 public bool InUnsafe {
463 get { return (flags & Flags.InUnsafe) != 0; }
466 public bool InCatch {
467 get { return (flags & Flags.InCatch) != 0; }
470 public bool InFinally {
471 get { return (flags & Flags.InFinally) != 0; }
474 public bool DoFlowAnalysis {
475 get { return (flags & Flags.DoFlowAnalysis) != 0; }
478 public bool OmitStructFlowAnalysis {
479 get { return (flags & Flags.OmitStructFlowAnalysis) != 0; }
482 // utility helper for CheckExpr, UnCheckExpr, Checked and Unchecked statements
483 // it's public so that we can use a struct at the callsite
484 public struct FlagsHandle : IDisposable
487 Flags invmask, oldval;
488 internal FlagsHandle (EmitContext ec, Flags mask, Flags val)
492 oldval = ec.flags & mask;
493 ec.flags = (ec.flags & invmask) | (val & mask);
495 public void Dispose ()
497 ec.flags = (ec.flags & invmask) | oldval;
501 // Temporarily set all the given flags to the given value. Should be used in an 'using' statement
502 public FlagsHandle With (Flags bits, bool enable)
504 return new FlagsHandle (this, bits, enable ? bits : 0);
507 public FlagsHandle WithFlowAnalysis (bool do_flow_analysis, bool omit_struct_analysis)
510 (do_flow_analysis ? Flags.DoFlowAnalysis : 0) |
511 (omit_struct_analysis ? Flags.OmitStructFlowAnalysis : 0);
512 return new FlagsHandle (this, Flags.DoFlowAnalysis | Flags.OmitStructFlowAnalysis, newflags);
515 public bool IsInObsoleteScope {
516 get { return ResolveContext.IsInObsoleteScope; }
519 public bool IsInUnsafeScope {
520 get { return InUnsafe || ResolveContext.IsInUnsafeScope; }
523 public bool IsAnonymousMethodAllowed {
524 get { return isAnonymousMethodAllowed; }
525 set { isAnonymousMethodAllowed = value; }
528 public FlowBranching CurrentBranching {
529 get { return current_flow_branching; }
533 // Starts a new code branching. This inherits the state of all local
534 // variables and parameters from the current branching.
536 public FlowBranching StartFlowBranching (FlowBranching.BranchingType type, Location loc)
538 current_flow_branching = FlowBranching.CreateBranching (CurrentBranching, type, null, loc);
539 return current_flow_branching;
543 // Starts a new code branching for block `block'.
545 public FlowBranching StartFlowBranching (Block block)
547 FlowBranching.BranchingType type;
549 if ((CurrentBranching != null) &&
550 (CurrentBranching.Type == FlowBranching.BranchingType.Switch))
551 type = FlowBranching.BranchingType.SwitchSection;
553 type = FlowBranching.BranchingType.Block;
555 flags |= Flags.DoFlowAnalysis;
557 current_flow_branching = FlowBranching.CreateBranching (
558 CurrentBranching, type, block, block.StartLocation);
559 return current_flow_branching;
562 public FlowBranchingException StartFlowBranching (ExceptionStatement stmt)
564 FlowBranchingException branching = new FlowBranchingException (CurrentBranching, stmt);
565 current_flow_branching = branching;
569 public FlowBranchingLabeled StartFlowBranching (LabeledStatement stmt)
571 FlowBranchingLabeled branching = new FlowBranchingLabeled (CurrentBranching, stmt);
572 current_flow_branching = branching;
576 public FlowBranchingToplevel StartFlowBranching (ToplevelBlock stmt)
578 FlowBranchingToplevel branching = new FlowBranchingToplevel (CurrentBranching, stmt);
579 current_flow_branching = branching;
584 // Ends a code branching. Merges the state of locals and parameters
585 // from all the children of the ending branching.
587 public FlowBranching.UsageVector DoEndFlowBranching ()
589 FlowBranching old = current_flow_branching;
590 current_flow_branching = current_flow_branching.Parent;
592 return current_flow_branching.MergeChild (old);
596 // Ends a code branching. Merges the state of locals and parameters
597 // from all the children of the ending branching.
599 public FlowBranching.Reachability EndFlowBranching ()
601 FlowBranching.UsageVector vector = DoEndFlowBranching ();
603 return vector.Reachability;
607 // Kills the current code branching. This throws away any changed state
608 // information and should only be used in case of an error.
610 public void KillFlowBranching ()
612 current_flow_branching = current_flow_branching.Parent;
615 public bool MustCaptureVariable (LocalInfo local)
617 if (CurrentAnonymousMethod == null)
619 if (CurrentAnonymousMethod.IsIterator)
621 return local.Block.Toplevel != CurrentBlock.Toplevel;
624 public void EmitMeta (ToplevelBlock b)
629 ReturnLabel = ig.DefineLabel ();
633 // Here until we can fix the problem with Mono.CSharp.Switch, which
634 // currently can not cope with ig == null during resolve (which must
635 // be fixed for switch statements to work on anonymous methods).
637 public void EmitTopBlock (IMethodData md, ToplevelBlock block)
644 if (ResolveTopBlock (null, block, md.ParameterInfo, md, out unreachable)){
647 current_phase = Phase.Emitting;
648 EmitResolvedTopBlock (block, unreachable);
654 public bool ResolveTopBlock (EmitContext anonymous_method_host, ToplevelBlock block,
655 Parameters ip, IMethodData md, out bool unreachable)
657 current_phase = Phase.Resolving;
665 CurrentFile = loc.File;
670 if (!block.ResolveMeta (this, ip))
673 if ((md != null) && (md.Iterator != null)) {
674 if (!md.Iterator.Resolve (this))
678 using (this.With (EmitContext.Flags.DoFlowAnalysis, true)) {
679 FlowBranchingToplevel top_level;
680 if (anonymous_method_host != null)
681 top_level = new FlowBranchingToplevel (anonymous_method_host.CurrentBranching, block);
683 top_level = block.TopLevelBranching;
685 current_flow_branching = top_level;
686 bool ok = block.Resolve (this);
687 current_flow_branching = null;
692 FlowBranching.Reachability reachability = top_level.End ();
693 if (reachability.IsUnreachable)
697 } catch (Exception e) {
698 Console.WriteLine ("Exception caught by the compiler while compiling:");
699 Console.WriteLine (" Block that caused the problem begin at: " + loc);
701 if (CurrentBlock != null){
702 Console.WriteLine (" Block being compiled: [{0},{1}]",
703 CurrentBlock.StartLocation, CurrentBlock.EndLocation);
705 Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
710 if (ReturnType != null && !unreachable) {
711 if (CurrentAnonymousMethod == null) {
712 Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
714 } else if (!CurrentAnonymousMethod.IsIterator) {
715 Report.Error (1643, CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
716 CurrentAnonymousMethod.GetSignatureForError ());
721 if (!block.CompleteContexts (this))
728 public void EmitResolvedTopBlock (ToplevelBlock block, bool unreachable)
734 ig.MarkLabel (ReturnLabel);
736 if (return_value != null){
737 ig.Emit (OpCodes.Ldloc, return_value);
738 ig.Emit (OpCodes.Ret);
741 // If `HasReturnLabel' is set, then we already emitted a
742 // jump to the end of the method, so we must emit a `ret'
745 // Unfortunately, System.Reflection.Emit automatically emits
746 // a leave to the end of a finally block. This is a problem
747 // if no code is following the try/finally block since we may
748 // jump to a point after the end of the method.
749 // As a workaround, we're always creating a return label in
753 bool in_iterator = (CurrentAnonymousMethod != null) &&
754 CurrentAnonymousMethod.IsIterator && InIterator;
756 if ((block != null) && block.IsDestructor) {
757 // Nothing to do; S.R.E automatically emits a leave.
758 } else if (HasReturnLabel || (!unreachable && !in_iterator)) {
759 if (ReturnType != null)
760 ig.Emit (OpCodes.Ldloc, TemporaryReturn ());
761 ig.Emit (OpCodes.Ret);
767 /// This is called immediately before emitting an IL opcode to tell the symbol
768 /// writer to which source line this opcode belongs.
770 public void Mark (Location loc, bool check_file)
772 if ((CodeGen.SymbolWriter == null) || loc.IsNull)
775 if (check_file && (CurrentFile != loc.File))
778 CodeGen.SymbolWriter.MarkSequencePoint (ig, loc.Row, loc.Column);
781 public void DefineLocalVariable (string name, LocalBuilder builder)
783 if (CodeGen.SymbolWriter == null)
786 CodeGen.SymbolWriter.DefineLocalVariable (name, builder);
789 public void BeginScope ()
793 if (CodeGen.SymbolWriter != null)
794 CodeGen.SymbolWriter.OpenScope(ig);
797 public void EndScope ()
801 if (CodeGen.SymbolWriter != null)
802 CodeGen.SymbolWriter.CloseScope(ig);
806 /// Returns a temporary storage for a variable of type t as
807 /// a local variable in the current body.
809 public LocalBuilder GetTemporaryLocal (Type t)
811 if (temporary_storage != null) {
812 object o = temporary_storage [t];
816 o = s.Count == 0 ? null : s.Pop ();
818 temporary_storage.Remove (t);
822 return (LocalBuilder) o;
824 return ig.DeclareLocal (t);
827 public void FreeTemporaryLocal (LocalBuilder b, Type t)
831 if (temporary_storage == null) {
832 temporary_storage = new Hashtable ();
833 temporary_storage [t] = b;
836 object o = temporary_storage [t];
838 temporary_storage [t] = b;
846 temporary_storage [t] = s;
852 /// Current loop begin and end labels.
854 public Label LoopBegin, LoopEnd;
857 /// Default target in a switch statement. Only valid if
860 public Label DefaultTarget;
863 /// If this is non-null, points to the current switch statement
865 public Switch Switch;
868 /// ReturnValue creates on demand the LocalBuilder for the
869 /// return value from the function. By default this is not
870 /// used. This is only required when returns are found inside
871 /// Try or Catch statements.
873 /// This method is typically invoked from the Emit phase, so
874 /// we allow the creation of a return label if it was not
875 /// requested during the resolution phase. Could be cleaned
876 /// up, but it would replicate a lot of logic in the Emit phase
877 /// of the code that uses it.
879 public LocalBuilder TemporaryReturn ()
881 if (return_value == null){
882 return_value = ig.DeclareLocal (ReturnType);
883 if (!HasReturnLabel){
884 ReturnLabel = ig.DefineLabel ();
885 HasReturnLabel = true;
893 /// This method is used during the Resolution phase to flag the
894 /// need to define the ReturnLabel
896 public void NeedReturnLabel ()
898 if (current_phase != Phase.Resolving){
900 // The reason is that the `ReturnLabel' is declared between
901 // resolution and emission
903 throw new Exception ("NeedReturnLabel called from Emit phase, should only be called during Resolve");
906 if (!InIterator && !HasReturnLabel)
907 HasReturnLabel = true;
911 public Expression GetThis (Location loc)
914 if (CurrentBlock != null)
915 my_this = new This (CurrentBlock, loc);
917 my_this = new This (loc);
919 if (!my_this.ResolveBase (this))
927 public abstract class CommonAssemblyModulClass : Attributable, IResolveContext {
929 protected CommonAssemblyModulClass ():
934 public void AddAttributes (ArrayList attrs)
936 foreach (Attribute a in attrs)
939 if (attributes == null) {
940 attributes = new Attributes (attrs);
943 attributes.AddAttributes (attrs);
946 public virtual void Emit (TypeContainer tc)
948 if (OptAttributes == null)
951 OptAttributes.Emit ();
954 protected Attribute ResolveAttribute (Type a_type)
956 Attribute a = OptAttributes.Search (a_type);
963 public override IResolveContext ResolveContext {
967 #region IResolveContext Members
969 public DeclSpace DeclContainer {
970 get { return RootContext.ToplevelTypes; }
973 public DeclSpace GenericDeclContainer {
974 get { return DeclContainer; }
977 public bool IsInObsoleteScope {
978 get { return false; }
981 public bool IsInUnsafeScope {
982 get { return false; }
988 public class AssemblyClass : CommonAssemblyModulClass {
989 // TODO: make it private and move all builder based methods here
990 public AssemblyBuilder Builder;
991 bool is_cls_compliant;
992 bool wrap_non_exception_throws;
994 public Attribute ClsCompliantAttribute;
996 ListDictionary declarative_security;
998 public AssemblyName Name;
999 MethodInfo add_type_forwarder;
1000 ListDictionary emitted_forwarders;
1003 // Module is here just because of error messages
1004 static string[] attribute_targets = new string [] { "assembly", "module" };
1006 public AssemblyClass (): base ()
1009 wrap_non_exception_throws = true;
1013 public bool IsClsCompliant {
1015 return is_cls_compliant;
1019 public bool WrapNonExceptionThrows {
1021 return wrap_non_exception_throws;
1025 public override AttributeTargets AttributeTargets {
1027 return AttributeTargets.Assembly;
1031 public override bool IsClsComplianceRequired ()
1033 return is_cls_compliant;
1036 public void Resolve ()
1038 if (OptAttributes == null)
1041 // Ensure that we only have GlobalAttributes, since the Search isn't safe with other types.
1042 if (!OptAttributes.CheckTargets())
1045 ClsCompliantAttribute = ResolveAttribute (TypeManager.cls_compliant_attribute_type);
1046 if (ClsCompliantAttribute != null) {
1047 is_cls_compliant = ClsCompliantAttribute.GetClsCompliantAttributeValue ();
1051 Attribute a = ResolveAttribute (TypeManager.runtime_compatibility_attr_type);
1053 object val = a.GetPropertyValue ("WrapNonExceptionThrows");
1055 wrap_non_exception_throws = (bool)val;
1061 private void SetPublicKey (AssemblyName an, byte[] strongNameBlob)
1064 // check for possible ECMA key
1065 if (strongNameBlob.Length == 16) {
1066 // will be rejected if not "the" ECMA key
1067 an.SetPublicKey (strongNameBlob);
1070 // take it, with or without, a private key
1071 RSA rsa = CryptoConvert.FromCapiKeyBlob (strongNameBlob);
1072 // and make sure we only feed the public part to Sys.Ref
1073 byte[] publickey = CryptoConvert.ToCapiPublicKeyBlob (rsa);
1075 // AssemblyName.SetPublicKey requires an additional header
1076 byte[] publicKeyHeader = new byte [12] { 0x00, 0x24, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00 };
1078 byte[] encodedPublicKey = new byte [12 + publickey.Length];
1079 Buffer.BlockCopy (publicKeyHeader, 0, encodedPublicKey, 0, 12);
1080 Buffer.BlockCopy (publickey, 0, encodedPublicKey, 12, publickey.Length);
1081 an.SetPublicKey (encodedPublicKey);
1085 Error_AssemblySigning ("The specified file `" + RootContext.StrongNameKeyFile + "' is incorrectly encoded");
1086 Environment.Exit (1);
1090 // TODO: rewrite this code (to kill N bugs and make it faster) and use standard ApplyAttribute way.
1091 public AssemblyName GetAssemblyName (string name, string output)
1093 if (OptAttributes != null) {
1094 foreach (Attribute a in OptAttributes.Attrs) {
1095 // cannot rely on any resolve-based members before you call Resolve
1096 if (a.ExplicitTarget == null || a.ExplicitTarget != "assembly")
1099 // TODO: This code is buggy: comparing Attribute name without resolving is wrong.
1100 // However, this is invoked by CodeGen.Init, when none of the namespaces
1102 // TODO: Does not handle quoted attributes properly
1104 case "AssemblyKeyFile":
1105 case "AssemblyKeyFileAttribute":
1106 case "System.Reflection.AssemblyKeyFileAttribute":
1107 if (RootContext.StrongNameKeyFile != null) {
1108 Report.SymbolRelatedToPreviousError (a.Location, a.Name);
1109 Report.Warning (1616, 1, "Option `{0}' overrides attribute `{1}' given in a source file or added module",
1110 "keyfile", "System.Reflection.AssemblyKeyFileAttribute");
1113 string value = a.GetString ();
1114 if (value.Length != 0)
1115 RootContext.StrongNameKeyFile = value;
1118 case "AssemblyKeyName":
1119 case "AssemblyKeyNameAttribute":
1120 case "System.Reflection.AssemblyKeyNameAttribute":
1121 if (RootContext.StrongNameKeyContainer != null) {
1122 Report.SymbolRelatedToPreviousError (a.Location, a.Name);
1123 Report.Warning (1616, 1, "Option `{0}' overrides attribute `{1}' given in a source file or added module",
1124 "keycontainer", "System.Reflection.AssemblyKeyNameAttribute");
1127 string value = a.GetString ();
1128 if (value.Length != 0)
1129 RootContext.StrongNameKeyContainer = value;
1132 case "AssemblyDelaySign":
1133 case "AssemblyDelaySignAttribute":
1134 case "System.Reflection.AssemblyDelaySignAttribute":
1135 RootContext.StrongNameDelaySign = a.GetBoolean ();
1141 AssemblyName an = new AssemblyName ();
1142 an.Name = Path.GetFileNameWithoutExtension (name);
1144 // note: delay doesn't apply when using a key container
1145 if (RootContext.StrongNameKeyContainer != null) {
1146 an.KeyPair = new StrongNameKeyPair (RootContext.StrongNameKeyContainer);
1150 // strongname is optional
1151 if (RootContext.StrongNameKeyFile == null)
1154 string AssemblyDir = Path.GetDirectoryName (output);
1156 // the StrongName key file may be relative to (a) the compiled
1157 // file or (b) to the output assembly. See bugzilla #55320
1158 // http://bugzilla.ximian.com/show_bug.cgi?id=55320
1160 // (a) relative to the compiled file
1161 string filename = Path.GetFullPath (RootContext.StrongNameKeyFile);
1162 bool exist = File.Exists (filename);
1163 if ((!exist) && (AssemblyDir != null) && (AssemblyDir != String.Empty)) {
1164 // (b) relative to the outputed assembly
1165 filename = Path.GetFullPath (Path.Combine (AssemblyDir, RootContext.StrongNameKeyFile));
1166 exist = File.Exists (filename);
1170 using (FileStream fs = new FileStream (filename, FileMode.Open, FileAccess.Read)) {
1171 byte[] snkeypair = new byte [fs.Length];
1172 fs.Read (snkeypair, 0, snkeypair.Length);
1174 if (RootContext.StrongNameDelaySign) {
1175 // delayed signing - DO NOT include private key
1176 SetPublicKey (an, snkeypair);
1179 // no delay so we make sure we have the private key
1181 CryptoConvert.FromCapiPrivateKeyBlob (snkeypair);
1182 an.KeyPair = new StrongNameKeyPair (snkeypair);
1184 catch (CryptographicException) {
1185 if (snkeypair.Length == 16) {
1186 // error # is different for ECMA key
1187 Report.Error (1606, "Could not sign the assembly. " +
1188 "ECMA key can only be used to delay-sign assemblies");
1191 Error_AssemblySigning ("The specified file `" + RootContext.StrongNameKeyFile + "' does not have a private key");
1199 Error_AssemblySigning ("The specified file `" + RootContext.StrongNameKeyFile + "' does not exist");
1205 void Error_AssemblySigning (string text)
1207 Report.Error (1548, "Error during assembly signing. " + text);
1211 bool CheckInternalsVisibleAttribute (Attribute a)
1213 string assembly_name = a.GetString ();
1214 if (assembly_name.Length == 0)
1217 AssemblyName aname = null;
1219 aname = new AssemblyName (assembly_name);
1220 } catch (FileLoadException) {
1221 } catch (ArgumentException) {
1224 // Bad assembly name format
1226 Report.Warning (1700, 3, a.Location, "Assembly reference `" + assembly_name + "' is invalid and cannot be resolved");
1227 // Report error if we have defined Version or Culture
1228 else if (aname.Version != null || aname.CultureInfo != null)
1229 throw new Exception ("Friend assembly `" + a.GetString () +
1230 "' is invalid. InternalsVisibleTo cannot have version or culture specified.");
1231 else if (aname.GetPublicKey () == null && Name.GetPublicKey () != null) {
1232 Report.Error (1726, a.Location, "Friend assembly reference `" + aname.FullName + "' is invalid." +
1233 " Strong named assemblies must specify a public key in their InternalsVisibleTo declarations");
1241 public override void ApplyAttributeBuilder (Attribute a, CustomAttributeBuilder customBuilder)
1243 if (a.Type.IsSubclassOf (TypeManager.security_attr_type) && a.CheckSecurityActionValidity (true)) {
1244 if (declarative_security == null)
1245 declarative_security = new ListDictionary ();
1247 a.ExtractSecurityPermissionSet (declarative_security);
1251 if (a.Type == TypeManager.assembly_culture_attribute_type) {
1252 string value = a.GetString ();
1253 if (value == null || value.Length == 0)
1256 if (RootContext.Target == Target.Exe) {
1257 a.Error_AttributeEmitError ("The executables cannot be satelite assemblies, remove the attribute or keep it empty");
1263 if (a.Type == TypeManager.internals_visible_attr_type && !CheckInternalsVisibleAttribute (a))
1266 if (a.Type == TypeManager.type_forwarder_attr_type) {
1267 Type t = a.GetArgumentType ();
1268 if (t == null || TypeManager.HasElementType (t)) {
1269 Report.Error (735, a.Location, "Invalid type specified as an argument for TypeForwardedTo attribute");
1273 if (emitted_forwarders == null) {
1274 emitted_forwarders = new ListDictionary();
1275 } else if (emitted_forwarders.Contains(t)) {
1276 Report.SymbolRelatedToPreviousError(((Attribute)emitted_forwarders[t]).Location, null);
1277 Report.Error(739, a.Location, "A duplicate type forward of type `{0}'",
1278 TypeManager.CSharpName(t));
1282 emitted_forwarders.Add(t, a);
1284 if (TypeManager.LookupDeclSpace (t) != null) {
1285 Report.SymbolRelatedToPreviousError (t);
1286 Report.Error (729, a.Location, "Cannot forward type `{0}' because it is defined in this assembly",
1287 TypeManager.CSharpName (t));
1292 Report.Error (730, a.Location, "Cannot forward type `{0}' because it is a nested type",
1293 TypeManager.CSharpName (t));
1297 if (t.IsGenericType) {
1298 Report.Error (733, a.Location, "Cannot forward generic type `{0}'", TypeManager.CSharpName (t));
1302 if (add_type_forwarder == null) {
1303 add_type_forwarder = typeof (AssemblyBuilder).GetMethod ("AddTypeForwarder",
1304 BindingFlags.NonPublic | BindingFlags.Instance);
1306 if (add_type_forwarder == null) {
1307 Report.RuntimeMissingSupport (a.Location, "TypeForwardedTo attribute");
1312 add_type_forwarder.Invoke (Builder, new object[] { t });
1317 Builder.SetCustomAttribute (customBuilder);
1320 public override void Emit (TypeContainer tc)
1325 // FIXME: Does this belong inside SRE.AssemblyBuilder instead?
1326 if (OptAttributes == null || !OptAttributes.Contains (TypeManager.runtime_compatibility_attr_type)) {
1327 ConstructorInfo ci = TypeManager.GetConstructor (
1328 TypeManager.runtime_compatibility_attr_type, Type.EmptyTypes);
1329 PropertyInfo [] pis = new PropertyInfo [1];
1330 pis [0] = TypeManager.GetProperty (
1331 TypeManager.runtime_compatibility_attr_type, "WrapNonExceptionThrows");
1332 object [] pargs = new object [1];
1334 Builder.SetCustomAttribute (new CustomAttributeBuilder (ci, new object [0], pis, pargs));
1338 if (declarative_security != null) {
1340 MethodInfo add_permission = typeof (AssemblyBuilder).GetMethod ("AddPermissionRequests", BindingFlags.Instance | BindingFlags.NonPublic);
1341 object builder_instance = Builder;
1344 // Microsoft runtime hacking
1345 if (add_permission == null) {
1346 Type assembly_builder = typeof (AssemblyBuilder).Assembly.GetType ("System.Reflection.Emit.AssemblyBuilderData");
1347 add_permission = assembly_builder.GetMethod ("AddPermissionRequests", BindingFlags.Instance | BindingFlags.NonPublic);
1349 FieldInfo fi = typeof (AssemblyBuilder).GetField ("m_assemblyData", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.GetField);
1350 builder_instance = fi.GetValue (Builder);
1353 object[] args = new object [] { declarative_security [SecurityAction.RequestMinimum],
1354 declarative_security [SecurityAction.RequestOptional],
1355 declarative_security [SecurityAction.RequestRefuse] };
1356 add_permission.Invoke (builder_instance, args);
1359 Report.RuntimeMissingSupport (Location.Null, "assembly permission setting");
1364 public override string[] ValidAttributeTargets {
1366 return attribute_targets;
1370 // Wrapper for AssemblyBuilder.AddModule
1371 static MethodInfo adder_method;
1372 static public MethodInfo AddModule_Method {
1374 if (adder_method == null)
1375 adder_method = typeof (AssemblyBuilder).GetMethod ("AddModule", BindingFlags.Instance|BindingFlags.NonPublic);
1376 return adder_method;
1379 public Module AddModule (string module)
1381 MethodInfo m = AddModule_Method;
1383 Report.RuntimeMissingSupport (Location.Null, "/addmodule");
1384 Environment.Exit (1);
1388 return (Module) m.Invoke (Builder, new object [] { module });
1389 } catch (TargetInvocationException ex) {
1390 throw ex.InnerException;
1395 public class ModuleClass : CommonAssemblyModulClass {
1396 // TODO: make it private and move all builder based methods here
1397 public ModuleBuilder Builder;
1398 bool m_module_is_unsafe;
1400 public CharSet DefaultCharSet = CharSet.Ansi;
1401 public TypeAttributes DefaultCharSetType = TypeAttributes.AnsiClass;
1403 static string[] attribute_targets = new string [] { "module" };
1405 public ModuleClass (bool is_unsafe)
1407 m_module_is_unsafe = is_unsafe;
1410 public override AttributeTargets AttributeTargets {
1412 return AttributeTargets.Module;
1416 public override bool IsClsComplianceRequired ()
1418 return CodeGen.Assembly.IsClsCompliant;
1421 public override void Emit (TypeContainer tc)
1425 if (!m_module_is_unsafe)
1428 if (TypeManager.unverifiable_code_ctor == null) {
1429 Console.WriteLine ("Internal error ! Cannot set unverifiable code attribute.");
1433 Builder.SetCustomAttribute (new CustomAttributeBuilder (TypeManager.unverifiable_code_ctor, new object [0]));
1436 public override void ApplyAttributeBuilder (Attribute a, CustomAttributeBuilder customBuilder)
1438 if (a.Type == TypeManager.cls_compliant_attribute_type) {
1439 if (CodeGen.Assembly.ClsCompliantAttribute == null) {
1440 Report.Warning (3012, 1, a.Location, "You must specify the CLSCompliant attribute on the assembly, not the module, to enable CLS compliance checking");
1442 else if (CodeGen.Assembly.IsClsCompliant != a.GetBoolean ()) {
1443 Report.SymbolRelatedToPreviousError (CodeGen.Assembly.ClsCompliantAttribute.Location, CodeGen.Assembly.ClsCompliantAttribute.GetSignatureForError ());
1444 Report.Error (3017, a.Location, "You cannot specify the CLSCompliant attribute on a module that differs from the CLSCompliant attribute on the assembly");
1449 Builder.SetCustomAttribute (customBuilder);
1453 /// It is called very early therefore can resolve only predefined attributes
1455 public void Resolve ()
1458 if (OptAttributes == null)
1461 if (!OptAttributes.CheckTargets())
1464 Attribute a = ResolveAttribute (TypeManager.default_charset_type);
1466 DefaultCharSet = a.GetCharSetValue ();
1467 switch (DefaultCharSet) {
1472 DefaultCharSetType = TypeAttributes.AutoClass;
1474 case CharSet.Unicode:
1475 DefaultCharSetType = TypeAttributes.UnicodeClass;
1478 Report.Error (1724, a.Location, "Value specified for the argument to 'System.Runtime.InteropServices.DefaultCharSetAttribute' is not valid");
1485 public override string[] ValidAttributeTargets {
1487 return attribute_targets;