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 catch (COMException
) {
170 if ((RootContext
.StrongNameKeyFile
== null) || (!RootContext
.StrongNameDelaySign
))
173 // FIXME: it seems Microsoft AssemblyBuilder doesn't like to delay sign assemblies
174 Report
.Error (1548, "Couldn't delay-sign the assembly with the '" +
175 RootContext
.StrongNameKeyFile
+
176 "', Use MCS with the Mono runtime or CSC to compile this assembly.");
178 catch (System
.IO
.IOException io
) {
179 Report
.Error (16, "Could not write to file `"+name
+"', cause: " + io
.Message
);
181 catch (System
.UnauthorizedAccessException ua
) {
182 Report
.Error (16, "Could not write to file `"+name
+"', cause: " + ua
.Message
);
185 if (SymbolWriter
!= null)
186 SymbolWriter
.WriteSymbolFile ();
191 /// An Emit Context is created for each body of code (from methods,
192 /// properties bodies, indexer bodies or constructor bodies)
194 public class EmitContext
{
195 public DeclSpace DeclSpace
;
196 public DeclSpace TypeContainer
;
197 public ILGenerator ig
;
200 /// This variable tracks the `checked' state of the compilation,
201 /// it controls whether we should generate code that does overflow
202 /// checking, or if we generate code that ignores overflows.
204 /// The default setting comes from the command line option to generate
205 /// checked or unchecked code plus any source code changes using the
206 /// checked/unchecked statements or expressions. Contrast this with
207 /// the ConstantCheckState flag.
210 public bool CheckState
;
213 /// The constant check state is always set to `true' and cant be changed
214 /// from the command line. The source code can change this setting with
215 /// the `checked' and `unchecked' statements and expressions.
217 public bool ConstantCheckState
;
220 /// Whether we are emitting code inside a static or instance method
222 public bool IsStatic
;
225 /// Whether the actual created method is static or instance method.
226 /// Althoug the method might be declared as `static', if an anonymous
227 /// method is involved, we might turn this into an instance method.
229 /// So this reflects the low-level staticness of the method, while
230 /// IsStatic represents the semantic, high-level staticness.
232 public bool MethodIsStatic
;
235 /// Whether we are emitting a field initializer
237 public bool IsFieldInitializer
;
240 /// The value that is allowed to be returned or NULL if there is no
243 public Type ReturnType
;
246 /// Points to the Type (extracted from the TypeContainer) that
247 /// declares this body of code
249 public Type ContainerType
;
252 /// Whether this is generating code for a constructor
254 public bool IsConstructor
;
257 /// Whether we're control flow analysis enabled
259 public bool DoFlowAnalysis
;
262 /// Whether we're control flow analysis disabled on struct
264 public bool OmitStructFlowAnalysis
;
267 /// Keeps track of the Type to LocalBuilder temporary storage created
268 /// to store structures (used to compute the address of the structure
269 /// value on structure method invocations)
271 public Hashtable temporary_storage
;
273 public Block CurrentBlock
;
275 public int CurrentFile
;
278 /// The location where we store the return value.
280 LocalBuilder return_value
;
283 /// The location where return has to jump to return the
286 public Label ReturnLabel
;
289 /// If we already defined the ReturnLabel
291 public bool HasReturnLabel
;
294 /// Whether we are inside an iterator block.
296 public bool InIterator
;
298 public bool IsLastStatement
;
301 /// Whether we are inside an unsafe block
303 public bool InUnsafe
;
306 /// Whether we are in a `fixed' initialization
308 public bool InFixedInitializer
;
310 public bool InRefOutArgumentResolving
;
313 public bool InFinally
;
316 /// Whether we are inside an anonymous method.
318 public AnonymousContainer CurrentAnonymousMethod
;
321 /// Location for this EmitContext
326 /// Inside an enum definition, we do not resolve enumeration values
327 /// to their enumerations, but rather to the underlying type/value
328 /// This is so EnumVal + EnumValB can be evaluated.
330 /// There is no "E operator + (E x, E y)", so during an enum evaluation
331 /// we relax the rules
333 public bool InEnumContext
;
336 /// Anonymous methods can capture local variables and fields,
337 /// this object tracks it. It is copied from the TopLevelBlock
340 public CaptureContext capture_context
;
343 /// Trace when method is called and is obsolete then this member suppress message
344 /// when call is inside next [Obsolete] method or type.
346 public bool TestObsoleteMethodUsage
= true;
349 /// The current iterator
351 public Iterator CurrentIterator
;
354 /// Whether we are in the resolving stage or not
363 FlowBranching current_flow_branching
;
365 static int next_id
= 0;
368 public override string ToString ()
370 return String
.Format ("EmitContext ({0}:{1}:{2})", id
,
371 CurrentIterator
, capture_context
, loc
);
374 public EmitContext (DeclSpace parent
, DeclSpace ds
, Location l
, ILGenerator ig
,
375 Type return_type
, int code_flags
, bool is_constructor
)
379 TypeContainer
= parent
;
381 CheckState
= RootContext
.Checked
;
382 ConstantCheckState
= true;
384 IsStatic
= (code_flags
& Modifiers
.STATIC
) != 0;
385 MethodIsStatic
= IsStatic
;
386 InIterator
= (code_flags
& Modifiers
.METHOD_YIELDS
) != 0;
387 ReturnType
= return_type
;
388 IsConstructor
= is_constructor
;
391 current_phase
= Phase
.Created
;
394 // Can only be null for the ResolveType contexts.
395 ContainerType
= parent
.TypeBuilder
;
396 if (parent
.UnsafeContext
)
399 InUnsafe
= (code_flags
& Modifiers
.UNSAFE
) != 0;
403 if (ReturnType
== TypeManager
.void_type
)
407 public EmitContext (TypeContainer tc
, Location l
, ILGenerator ig
,
408 Type return_type
, int code_flags
, bool is_constructor
)
409 : this (tc
, tc
, l
, ig
, return_type
, code_flags
, is_constructor
)
413 public EmitContext (TypeContainer tc
, Location l
, ILGenerator ig
,
414 Type return_type
, int code_flags
)
415 : this (tc
, tc
, l
, ig
, return_type
, code_flags
, false)
419 public FlowBranching CurrentBranching
{
421 return current_flow_branching
;
425 public bool HaveCaptureInfo
{
427 return capture_context
!= null;
432 // Starts a new code branching. This inherits the state of all local
433 // variables and parameters from the current branching.
435 public FlowBranching
StartFlowBranching (FlowBranching
.BranchingType type
, Location loc
)
437 current_flow_branching
= FlowBranching
.CreateBranching (CurrentBranching
, type
, null, loc
);
438 return current_flow_branching
;
442 // Starts a new code branching for block `block'.
444 public FlowBranching
StartFlowBranching (Block block
)
446 FlowBranching
.BranchingType type
;
448 if ((CurrentBranching
!= null) &&
449 (CurrentBranching
.Type
== FlowBranching
.BranchingType
.Switch
))
450 type
= FlowBranching
.BranchingType
.SwitchSection
;
452 type
= FlowBranching
.BranchingType
.Block
;
454 DoFlowAnalysis
= true;
456 current_flow_branching
= FlowBranching
.CreateBranching (
457 CurrentBranching
, type
, block
, block
.StartLocation
);
458 return current_flow_branching
;
461 public FlowBranchingException
StartFlowBranching (ExceptionStatement stmt
)
463 FlowBranchingException branching
= new FlowBranchingException (
464 CurrentBranching
, stmt
);
465 current_flow_branching
= branching
;
470 // Ends a code branching. Merges the state of locals and parameters
471 // from all the children of the ending branching.
473 public FlowBranching
.UsageVector
DoEndFlowBranching ()
475 FlowBranching old
= current_flow_branching
;
476 current_flow_branching
= current_flow_branching
.Parent
;
478 return current_flow_branching
.MergeChild (old
);
482 // Ends a code branching. Merges the state of locals and parameters
483 // from all the children of the ending branching.
485 public FlowBranching
.Reachability
EndFlowBranching ()
487 FlowBranching
.UsageVector vector
= DoEndFlowBranching ();
489 return vector
.Reachability
;
493 // Kills the current code branching. This throws away any changed state
494 // information and should only be used in case of an error.
496 public void KillFlowBranching ()
498 current_flow_branching
= current_flow_branching
.Parent
;
501 public void CaptureVariable (LocalInfo li
)
503 capture_context
.AddLocal (CurrentAnonymousMethod
, li
);
504 li
.IsCaptured
= true;
507 public void CaptureParameter (string name
, Type t
, int idx
)
509 capture_context
.AddParameter (this, CurrentAnonymousMethod
, name
, t
, idx
);
512 public void CaptureThis ()
514 capture_context
.CaptureThis (CurrentAnonymousMethod
);
519 // Use to register a field as captured
521 public void CaptureField (FieldExpr fe
)
523 capture_context
.AddField (this, CurrentAnonymousMethod
, fe
);
527 // Whether anonymous methods have captured variables
529 public bool HaveCapturedVariables ()
531 if (capture_context
!= null)
532 return capture_context
.HaveCapturedVariables
;
537 // Whether anonymous methods have captured fields or this.
539 public bool HaveCapturedFields ()
541 if (capture_context
!= null)
542 return capture_context
.HaveCapturedFields
;
547 // Emits the instance pointer for the host method
549 public void EmitMethodHostInstance (EmitContext target
, AnonymousMethod am
)
551 if (capture_context
!= null)
552 capture_context
.EmitMethodHostInstance (target
, am
);
554 target
.ig
.Emit (OpCodes
.Ldnull
);
556 target
.ig
.Emit (OpCodes
.Ldarg_0
);
560 // Returns whether the `local' variable has been captured by an anonymous
563 public bool IsCaptured (LocalInfo local
)
565 return capture_context
.IsCaptured (local
);
568 public bool IsParameterCaptured (string name
)
570 if (capture_context
!= null)
571 return capture_context
.IsParameterCaptured (name
);
575 public void EmitMeta (ToplevelBlock b
)
577 if (capture_context
!= null)
578 capture_context
.EmitAnonymousHelperClasses (this);
582 ReturnLabel
= ig
.DefineLabel ();
586 // Here until we can fix the problem with Mono.CSharp.Switch, which
587 // currently can not cope with ig == null during resolve (which must
588 // be fixed for switch statements to work on anonymous methods).
590 public void EmitTopBlock (IMethodData md
, ToplevelBlock block
)
597 if (ResolveTopBlock (null, block
, md
.ParameterInfo
, md
, out unreachable
)){
600 current_phase
= Phase
.Emitting
;
601 EmitResolvedTopBlock (block
, unreachable
);
607 public bool ResolveTopBlock (EmitContext anonymous_method_host
, ToplevelBlock block
,
608 Parameters ip
, IMethodData md
, out bool unreachable
)
610 current_phase
= Phase
.Resolving
;
617 capture_context
= block
.CaptureContext
;
620 CurrentFile
= loc
.File
;
625 if (!block
.ResolveMeta (this, ip
))
628 bool old_do_flow_analysis
= DoFlowAnalysis
;
629 DoFlowAnalysis
= true;
631 if (anonymous_method_host
!= null)
632 current_flow_branching
= FlowBranching
.CreateBranching (
633 anonymous_method_host
.CurrentBranching
,
634 FlowBranching
.BranchingType
.Block
, block
, loc
);
636 current_flow_branching
= block
.TopLevelBranching
;
638 if (!block
.Resolve (this)) {
639 current_flow_branching
= null;
640 DoFlowAnalysis
= old_do_flow_analysis
;
644 FlowBranching
.Reachability reachability
= current_flow_branching
.MergeTopBlock ();
645 current_flow_branching
= null;
647 DoFlowAnalysis
= old_do_flow_analysis
;
649 if (reachability
.AlwaysReturns
||
650 reachability
.AlwaysThrows
||
651 reachability
.IsUnreachable
)
654 } catch (Exception e
) {
655 Console
.WriteLine ("Exception caught by the compiler while compiling:");
656 Console
.WriteLine (" Block that caused the problem begin at: " + loc
);
658 if (CurrentBlock
!= null){
659 Console
.WriteLine (" Block being compiled: [{0},{1}]",
660 CurrentBlock
.StartLocation
, CurrentBlock
.EndLocation
);
662 Console
.WriteLine (e
.GetType ().FullName
+ ": " + e
.Message
);
667 if (ReturnType
!= null && !unreachable
) {
668 if (CurrentAnonymousMethod
== null) {
669 Report
.Error (161, md
.Location
, "`{0}': not all code paths return a value", md
.GetSignatureForError ());
671 } else if (!CurrentAnonymousMethod
.IsIterator
) {
672 Report
.Error (1643, CurrentAnonymousMethod
.Location
, "Not all code paths return a value in anonymous method of type `{0}'",
673 CurrentAnonymousMethod
.GetSignatureForError ());
678 block
.CompleteContexts ();
683 public void EmitResolvedTopBlock (ToplevelBlock block
, bool unreachable
)
689 ig
.MarkLabel (ReturnLabel
);
691 if (return_value
!= null){
692 ig
.Emit (OpCodes
.Ldloc
, return_value
);
693 ig
.Emit (OpCodes
.Ret
);
696 // If `HasReturnLabel' is set, then we already emitted a
697 // jump to the end of the method, so we must emit a `ret'
700 // Unfortunately, System.Reflection.Emit automatically emits
701 // a leave to the end of a finally block. This is a problem
702 // if no code is following the try/finally block since we may
703 // jump to a point after the end of the method.
704 // As a workaround, we're always creating a return label in
708 bool in_iterator
= (CurrentAnonymousMethod
!= null) &&
709 CurrentAnonymousMethod
.IsIterator
&& InIterator
;
711 if ((block
!= null) && block
.IsDestructor
) {
712 // Nothing to do; S.R.E automatically emits a leave.
713 } else if (HasReturnLabel
|| (!unreachable
&& !in_iterator
)) {
714 if (ReturnType
!= null)
715 ig
.Emit (OpCodes
.Ldloc
, TemporaryReturn ());
716 ig
.Emit (OpCodes
.Ret
);
721 // Close pending helper classes if we are the toplevel
723 if (capture_context
!= null && capture_context
.ParentToplevel
== null)
724 capture_context
.CloseAnonymousHelperClasses ();
728 /// This is called immediately before emitting an IL opcode to tell the symbol
729 /// writer to which source line this opcode belongs.
731 public void Mark (Location loc
, bool check_file
)
733 if ((CodeGen
.SymbolWriter
== null) || loc
.IsNull
)
736 if (check_file
&& (CurrentFile
!= loc
.File
))
739 CodeGen
.SymbolWriter
.MarkSequencePoint (ig
, loc
.Row
, loc
.Column
);
742 public void DefineLocalVariable (string name
, LocalBuilder builder
)
744 if (CodeGen
.SymbolWriter
== null)
747 CodeGen
.SymbolWriter
.DefineLocalVariable (name
, builder
);
750 public void BeginScope ()
754 if (CodeGen
.SymbolWriter
!= null)
755 CodeGen
.SymbolWriter
.OpenScope(ig
);
758 public void EndScope ()
762 if (CodeGen
.SymbolWriter
!= null)
763 CodeGen
.SymbolWriter
.CloseScope(ig
);
767 /// Returns a temporary storage for a variable of type t as
768 /// a local variable in the current body.
770 public LocalBuilder
GetTemporaryLocal (Type t
)
772 LocalBuilder location
= null;
774 if (temporary_storage
!= null){
775 object o
= temporary_storage
[t
];
778 ArrayList al
= (ArrayList
) o
;
780 for (int i
= 0; i
< al
.Count
; i
++){
782 location
= (LocalBuilder
) al
[i
];
788 location
= (LocalBuilder
) o
;
789 if (location
!= null)
794 return ig
.DeclareLocal (t
);
797 public void FreeTemporaryLocal (LocalBuilder b
, Type t
)
799 if (temporary_storage
== null){
800 temporary_storage
= new Hashtable ();
801 temporary_storage
[t
] = b
;
804 object o
= temporary_storage
[t
];
806 temporary_storage
[t
] = b
;
810 ArrayList al
= (ArrayList
) o
;
811 for (int i
= 0; i
< al
.Count
; i
++){
820 ArrayList replacement
= new ArrayList ();
822 temporary_storage
.Remove (t
);
823 temporary_storage
[t
] = replacement
;
827 /// Current loop begin and end labels.
829 public Label LoopBegin
, LoopEnd
;
832 /// Default target in a switch statement. Only valid if
835 public Label DefaultTarget
;
838 /// If this is non-null, points to the current switch statement
840 public Switch Switch
;
843 /// ReturnValue creates on demand the LocalBuilder for the
844 /// return value from the function. By default this is not
845 /// used. This is only required when returns are found inside
846 /// Try or Catch statements.
848 /// This method is typically invoked from the Emit phase, so
849 /// we allow the creation of a return label if it was not
850 /// requested during the resolution phase. Could be cleaned
851 /// up, but it would replicate a lot of logic in the Emit phase
852 /// of the code that uses it.
854 public LocalBuilder
TemporaryReturn ()
856 if (return_value
== null){
857 return_value
= ig
.DeclareLocal (ReturnType
);
858 if (!HasReturnLabel
){
859 ReturnLabel
= ig
.DefineLabel ();
860 HasReturnLabel
= true;
868 /// This method is used during the Resolution phase to flag the
869 /// need to define the ReturnLabel
871 public void NeedReturnLabel ()
873 if (current_phase
!= Phase
.Resolving
){
875 // The reason is that the `ReturnLabel' is declared between
876 // resolution and emission
878 throw new Exception ("NeedReturnLabel called from Emit phase, should only be called during Resolve");
881 if (!InIterator
&& !HasReturnLabel
)
882 HasReturnLabel
= true;
886 // Emits the proper object to address fields on a remapped
887 // variable/parameter to field in anonymous-method/iterator proxy classes.
889 public void EmitThis (bool need_address
)
891 ig
.Emit (OpCodes
.Ldarg_0
);
892 if (capture_context
!= null && CurrentAnonymousMethod
!= null){
893 ScopeInfo si
= CurrentAnonymousMethod
.Scope
;
895 if (si
.ParentLink
!= null)
896 ig
.Emit (OpCodes
.Ldfld
, si
.ParentLink
);
897 if (si
.THIS
!= null){
898 if (need_address
&& TypeManager
.IsValueType (si
.THIS
.FieldType
))
899 ig
.Emit (OpCodes
.Ldflda
, si
.THIS
);
901 ig
.Emit (OpCodes
.Ldfld
, si
.THIS
);
910 // Emits the code necessary to load the instance required
911 // to access the captured LocalInfo
913 public void EmitCapturedVariableInstance (LocalInfo li
)
915 if (capture_context
== null)
916 throw new Exception ("Calling EmitCapturedContext when there is no capture_context");
918 capture_context
.EmitCapturedVariableInstance (this, li
, CurrentAnonymousMethod
);
921 public void EmitParameter (string name
, bool leave_copy
, bool prepared
, ref LocalTemporary temp
)
923 capture_context
.EmitParameter (this, name
, leave_copy
, prepared
, ref temp
);
926 public void EmitAssignParameter (string name
, Expression source
, bool leave_copy
, bool prepare_for_load
, ref LocalTemporary temp
)
928 capture_context
.EmitAssignParameter (this, name
, source
, leave_copy
, prepare_for_load
, ref temp
);
931 public void EmitAddressOfParameter (string name
)
933 capture_context
.EmitAddressOfParameter (this, name
);
936 public Expression
GetThis (Location loc
)
939 if (CurrentBlock
!= null)
940 my_this
= new This (CurrentBlock
, loc
);
942 my_this
= new This (loc
);
944 if (!my_this
.ResolveBase (this))
952 public abstract class CommonAssemblyModulClass
: Attributable
{
953 protected CommonAssemblyModulClass ():
958 public void AddAttributes (ArrayList attrs
)
960 if (OptAttributes
== null) {
961 OptAttributes
= new Attributes (attrs
);
964 OptAttributes
.AddAttributes (attrs
);
967 public virtual void Emit (TypeContainer tc
)
969 if (OptAttributes
== null)
972 EmitContext ec
= new EmitContext (tc
, Mono
.CSharp
.Location
.Null
, null, null, 0, false);
973 OptAttributes
.Emit (ec
, this);
976 protected Attribute
ResolveAttribute (Type a_type
)
978 if (OptAttributes
== null)
981 // Ensure that we only have GlobalAttributes, since the Search below isn't safe with other types.
982 if (!OptAttributes
.CheckTargets (this))
985 EmitContext temp_ec
= new EmitContext (RootContext
.Tree
.Types
, Mono
.CSharp
.Location
.Null
, null, null, 0, false);
986 Attribute a
= OptAttributes
.Search (a_type
, temp_ec
);
994 public class AssemblyClass
: CommonAssemblyModulClass
{
995 // TODO: make it private and move all builder based methods here
996 public AssemblyBuilder Builder
;
997 bool is_cls_compliant
;
998 bool wrap_non_exception_throws
;
1000 public Attribute ClsCompliantAttribute
;
1002 ListDictionary declarative_security
;
1004 // Module is here just because of error messages
1005 static string[] attribute_targets
= new string [] { "assembly", "module" }
;
1007 public AssemblyClass (): base ()
1009 wrap_non_exception_throws
= true;
1012 public bool IsClsCompliant
{
1014 return is_cls_compliant
;
1018 public bool WrapNonExceptionThrows
{
1020 return wrap_non_exception_throws
;
1024 public override AttributeTargets AttributeTargets
{
1026 return AttributeTargets
.Assembly
;
1030 public override bool IsClsComplianceRequired(DeclSpace ds
)
1032 return is_cls_compliant
;
1035 public void Resolve ()
1037 ClsCompliantAttribute
= ResolveAttribute (TypeManager
.cls_compliant_attribute_type
);
1038 if (ClsCompliantAttribute
!= null) {
1039 is_cls_compliant
= ClsCompliantAttribute
.GetClsCompliantAttributeValue (null);
1043 Attribute a
= ResolveAttribute (TypeManager
.runtime_compatibility_attr_type
);
1045 object val
= a
.GetPropertyValue ("WrapNonExceptionThrows");
1047 wrap_non_exception_throws
= (bool)val
;
1053 private void SetPublicKey (AssemblyName an
, byte[] strongNameBlob
)
1056 // check for possible ECMA key
1057 if (strongNameBlob
.Length
== 16) {
1058 // will be rejected if not "the" ECMA key
1059 an
.SetPublicKey (strongNameBlob
);
1062 // take it, with or without, a private key
1063 RSA rsa
= CryptoConvert
.FromCapiKeyBlob (strongNameBlob
);
1064 // and make sure we only feed the public part to Sys.Ref
1065 byte[] publickey
= CryptoConvert
.ToCapiPublicKeyBlob (rsa
);
1067 // AssemblyName.SetPublicKey requires an additional header
1068 byte[] publicKeyHeader
= new byte [12] { 0x00, 0x24, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00 }
;
1070 byte[] encodedPublicKey
= new byte [12 + publickey
.Length
];
1071 Buffer
.BlockCopy (publicKeyHeader
, 0, encodedPublicKey
, 0, 12);
1072 Buffer
.BlockCopy (publickey
, 0, encodedPublicKey
, 12, publickey
.Length
);
1073 an
.SetPublicKey (encodedPublicKey
);
1077 Error_AssemblySigning ("The specified file `" + RootContext
.StrongNameKeyFile
+ "' is incorrectly encoded");
1078 Environment
.Exit (1);
1082 // TODO: rewrite this code (to kill N bugs and make it faster) and use standard ApplyAttribute way.
1083 public AssemblyName
GetAssemblyName (string name
, string output
)
1085 if (OptAttributes
!= null) {
1086 foreach (Attribute a
in OptAttributes
.Attrs
) {
1087 // cannot rely on any resolve-based members before you call Resolve
1088 if (a
.ExplicitTarget
== null || a
.ExplicitTarget
!= "assembly")
1091 // TODO: This code is buggy: comparing Attribute name without resolving is wrong.
1092 // However, this is invoked by CodeGen.Init, when none of the namespaces
1094 // TODO: Does not handle quoted attributes properly
1096 case "AssemblyKeyFile":
1097 case "AssemblyKeyFileAttribute":
1098 case "System.Reflection.AssemblyKeyFileAttribute":
1099 if (RootContext
.StrongNameKeyFile
!= null) {
1100 Report
.SymbolRelatedToPreviousError (a
.Location
, a
.Name
);
1101 Report
.Warning (1616, 1, "Option `{0}' overrides attribute `{1}' given in a source file or added module",
1102 "keyfile", "System.Reflection.AssemblyKeyFileAttribute");
1105 string value = a
.GetString ();
1106 if (value != String
.Empty
)
1107 RootContext
.StrongNameKeyFile
= value;
1110 case "AssemblyKeyName":
1111 case "AssemblyKeyNameAttribute":
1112 case "System.Reflection.AssemblyKeyNameAttribute":
1113 if (RootContext
.StrongNameKeyContainer
!= null) {
1114 Report
.SymbolRelatedToPreviousError (a
.Location
, a
.Name
);
1115 Report
.Warning (1616, 1, "Option `{0}' overrides attribute `{1}' given in a source file or added module",
1116 "keycontainer", "System.Reflection.AssemblyKeyNameAttribute");
1119 string value = a
.GetString ();
1120 if (value != String
.Empty
)
1121 RootContext
.StrongNameKeyContainer
= value;
1124 case "AssemblyDelaySign":
1125 case "AssemblyDelaySignAttribute":
1126 case "System.Reflection.AssemblyDelaySignAttribute":
1127 RootContext
.StrongNameDelaySign
= a
.GetBoolean ();
1133 AssemblyName an
= new AssemblyName ();
1134 an
.Name
= Path
.GetFileNameWithoutExtension (name
);
1136 // note: delay doesn't apply when using a key container
1137 if (RootContext
.StrongNameKeyContainer
!= null) {
1138 an
.KeyPair
= new StrongNameKeyPair (RootContext
.StrongNameKeyContainer
);
1142 // strongname is optional
1143 if (RootContext
.StrongNameKeyFile
== null)
1146 string AssemblyDir
= Path
.GetDirectoryName (output
);
1148 // the StrongName key file may be relative to (a) the compiled
1149 // file or (b) to the output assembly. See bugzilla #55320
1150 // http://bugzilla.ximian.com/show_bug.cgi?id=55320
1152 // (a) relative to the compiled file
1153 string filename
= Path
.GetFullPath (RootContext
.StrongNameKeyFile
);
1154 bool exist
= File
.Exists (filename
);
1155 if ((!exist
) && (AssemblyDir
!= null) && (AssemblyDir
!= String
.Empty
)) {
1156 // (b) relative to the outputed assembly
1157 filename
= Path
.GetFullPath (Path
.Combine (AssemblyDir
, RootContext
.StrongNameKeyFile
));
1158 exist
= File
.Exists (filename
);
1162 using (FileStream fs
= new FileStream (filename
, FileMode
.Open
, FileAccess
.Read
)) {
1163 byte[] snkeypair
= new byte [fs
.Length
];
1164 fs
.Read (snkeypair
, 0, snkeypair
.Length
);
1166 if (RootContext
.StrongNameDelaySign
) {
1167 // delayed signing - DO NOT include private key
1168 SetPublicKey (an
, snkeypair
);
1171 // no delay so we make sure we have the private key
1173 CryptoConvert
.FromCapiPrivateKeyBlob (snkeypair
);
1174 an
.KeyPair
= new StrongNameKeyPair (snkeypair
);
1176 catch (CryptographicException
) {
1177 if (snkeypair
.Length
== 16) {
1178 // error # is different for ECMA key
1179 Report
.Error (1606, "Could not sign the assembly. " +
1180 "ECMA key can only be used to delay-sign assemblies");
1183 Error_AssemblySigning ("The specified file `" + RootContext
.StrongNameKeyFile
+ "' does not have a private key");
1191 Error_AssemblySigning ("The specified file `" + RootContext
.StrongNameKeyFile
+ "' does not exist");
1197 void Error_AssemblySigning (string text
)
1199 Report
.Error (1548, "Error during assembly signing. " + text
);
1202 public override void ApplyAttributeBuilder (Attribute a
, CustomAttributeBuilder customBuilder
)
1204 if (a
.Type
.IsSubclassOf (TypeManager
.security_attr_type
) && a
.CheckSecurityActionValidity (true)) {
1205 if (declarative_security
== null)
1206 declarative_security
= new ListDictionary ();
1208 a
.ExtractSecurityPermissionSet (declarative_security
);
1212 if (a
.Type
== TypeManager
.assembly_culture_attribute_type
) {
1213 string value = a
.GetString ();
1214 if (value == null || value.Length
== 0)
1217 if (RootContext
.Target
== Target
.Exe
) {
1218 a
.Error_AttributeEmitError ("The executables cannot be satelite assemblies, remove the attribute or keep it empty");
1223 Builder
.SetCustomAttribute (customBuilder
);
1226 public override void Emit (TypeContainer tc
)
1230 if (declarative_security
!= null) {
1232 MethodInfo add_permission
= typeof (AssemblyBuilder
).GetMethod ("AddPermissionRequests", BindingFlags
.Instance
| BindingFlags
.NonPublic
);
1233 object builder_instance
= Builder
;
1236 // Microsoft runtime hacking
1237 if (add_permission
== null) {
1238 Type assembly_builder
= typeof (AssemblyBuilder
).Assembly
.GetType ("System.Reflection.Emit.AssemblyBuilderData");
1239 add_permission
= assembly_builder
.GetMethod ("AddPermissionRequests", BindingFlags
.Instance
| BindingFlags
.NonPublic
);
1241 FieldInfo fi
= typeof (AssemblyBuilder
).GetField ("m_assemblyData", BindingFlags
.Instance
| BindingFlags
.NonPublic
| BindingFlags
.GetField
);
1242 builder_instance
= fi
.GetValue (Builder
);
1245 object[] args
= new object [] { declarative_security
[SecurityAction
.RequestMinimum
],
1246 declarative_security
[SecurityAction
.RequestOptional
],
1247 declarative_security
[SecurityAction
.RequestRefuse
] };
1248 add_permission
.Invoke (builder_instance
, args
);
1251 Report
.RuntimeMissingSupport (Location
.Null
, "assembly permission setting");
1256 if (!OptAttributes
.Contains (TypeManager
.runtime_compatibility_attr_type
, null)) {
1257 ConstructorInfo ci
= TypeManager
.runtime_compatibility_attr_type
.GetConstructor (TypeManager
.NoTypes
);
1258 PropertyInfo pi
= TypeManager
.runtime_compatibility_attr_type
.GetProperty ("WrapNonExceptionThrows");
1259 Builder
.SetCustomAttribute (new CustomAttributeBuilder (ci
, new object [0],
1260 new PropertyInfo
[] { pi }
, new object[] { true }
));
1265 public override string[] ValidAttributeTargets
{
1267 return attribute_targets
;
1272 public class ModuleClass
: CommonAssemblyModulClass
{
1273 // TODO: make it private and move all builder based methods here
1274 public ModuleBuilder Builder
;
1275 bool m_module_is_unsafe
;
1277 public CharSet DefaultCharSet
= CharSet
.Ansi
;
1278 public TypeAttributes DefaultCharSetType
= TypeAttributes
.AnsiClass
;
1280 static string[] attribute_targets
= new string [] { "module" }
;
1282 public ModuleClass (bool is_unsafe
)
1284 m_module_is_unsafe
= is_unsafe
;
1287 public override AttributeTargets AttributeTargets
{
1289 return AttributeTargets
.Module
;
1293 public override bool IsClsComplianceRequired(DeclSpace ds
)
1295 return CodeGen
.Assembly
.IsClsCompliant
;
1298 public override void Emit (TypeContainer tc
)
1302 if (!m_module_is_unsafe
)
1305 if (TypeManager
.unverifiable_code_ctor
== null) {
1306 Console
.WriteLine ("Internal error ! Cannot set unverifiable code attribute.");
1310 Builder
.SetCustomAttribute (new CustomAttributeBuilder (TypeManager
.unverifiable_code_ctor
, new object [0]));
1313 public override void ApplyAttributeBuilder (Attribute a
, CustomAttributeBuilder customBuilder
)
1315 if (a
.Type
== TypeManager
.cls_compliant_attribute_type
) {
1316 if (CodeGen
.Assembly
.ClsCompliantAttribute
== null) {
1317 Report
.Warning (3012, 1, a
.Location
, "You must specify the CLSCompliant attribute on the assembly, not the module, to enable CLS compliance checking");
1319 else if (CodeGen
.Assembly
.IsClsCompliant
!= a
.GetBoolean ()) {
1320 Report
.SymbolRelatedToPreviousError (CodeGen
.Assembly
.ClsCompliantAttribute
.Location
, CodeGen
.Assembly
.ClsCompliantAttribute
.GetSignatureForError ());
1321 Report
.Error (3017, a
.Location
, "You cannot specify the CLSCompliant attribute on a module that differs from the CLSCompliant attribute on the assembly");
1326 Builder
.SetCustomAttribute (customBuilder
);
1330 /// It is called very early therefore can resolve only predefined attributes
1332 public void ResolveAttributes ()
1335 Attribute a
= ResolveAttribute (TypeManager
.default_charset_type
);
1337 DefaultCharSet
= a
.GetCharSetValue ();
1338 switch (DefaultCharSet
) {
1343 DefaultCharSetType
= TypeAttributes
.AutoClass
;
1345 case CharSet
.Unicode
:
1346 DefaultCharSetType
= TypeAttributes
.UnicodeClass
;
1349 Report
.Error (1724, a
.Location
, "Value specified for the argument to 'System.Runtime.InteropServices.DefaultCharSetAttribute' is not valid");
1356 public override string[] ValidAttributeTargets
{
1358 return attribute_targets
;