X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fgmcs%2Fcodegen.cs;h=fedc652b551bc3cba80107b32f37c59cd9e6f9b8;hb=b0eff411fcc55fa6a7bbc5ee97bb592fe3204309;hp=a06373e464308b8279c95347f2a78107ffab5bc9;hpb=3763abdafb2c1e5f9594b015fe4bf3acb553a25a;p=mono.git diff --git a/mcs/gmcs/codegen.cs b/mcs/gmcs/codegen.cs old mode 100755 new mode 100644 index a06373e4643..fedc652b551 --- a/mcs/gmcs/codegen.cs +++ b/mcs/gmcs/codegen.cs @@ -4,16 +4,20 @@ // Author: // Miguel de Icaza (miguel@ximian.com) // -// (C) 2001 Ximian, Inc. +// (C) 2001, 2002, 2003 Ximian, Inc. +// (C) 2004 Novell, Inc. // - +//#define PRODUCTION using System; using System.IO; using System.Collections; +using System.Collections.Specialized; using System.Reflection; using System.Reflection.Emit; using System.Runtime.InteropServices; +using System.Security; using System.Security.Cryptography; +using System.Security.Permissions; using Mono.Security.Cryptography; @@ -30,6 +34,11 @@ namespace Mono.CSharp { public static ModuleClass Module; static CodeGen () + { + Reset (); + } + + public static void Reset () { Assembly = new AssemblyClass (); Module = new ModuleClass (RootContext.Unsafe); @@ -63,28 +72,21 @@ namespace Mono.CSharp { return "."; } - static string TrimExt (string name) - { - int pos = name.LastIndexOf ('.'); - - return name.Substring (0, pos); - } - static public string FileName; // // Initializes the symbol writer // - static void InitializeSymbolWriter () + static void InitializeSymbolWriter (string filename) { - SymbolWriter = SymbolWriter.GetSymbolWriter (Module.Builder); + SymbolWriter = SymbolWriter.GetSymbolWriter (Module.Builder, filename); // // If we got an ISymbolWriter instance, initialize it. // if (SymbolWriter == null) { Report.Warning ( - -18, "Could not find the symbol writer assembly (Mono.CSharp.Debugger.dll). This is normally an installation problem. Please make sure to compile and install the mcs/class/Mono.CSharp.Debugger directory."); + -18, "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."); return; } } @@ -92,10 +94,28 @@ namespace Mono.CSharp { // // Initializes the code generator variables // - static public void Init (string name, string output, bool want_debugging_support) + static public bool Init (string name, string output, bool want_debugging_support) { FileName = output; AssemblyName an = Assembly.GetAssemblyName (name, output); + if (an == null) + return false; + + if (an.KeyPair != null) { + // If we are going to strong name our assembly make + // sure all its refs are strong named + foreach (Assembly a in TypeManager.GetAssemblies ()) { + AssemblyName ref_name = a.GetName (); + byte [] b = ref_name.GetPublicKeyToken (); + if (b == null || b.Length == 0) { + Report.Warning (1577, "Assembly generation failed " + + "-- Referenced assembly '" + + ref_name.Name + + "' does not have a strong name."); + //Environment.Exit (1); + } + } + } current_domain = AppDomain.CurrentDomain; @@ -110,14 +130,14 @@ namespace Mono.CSharp { RootContext.StrongNameKeyContainer + "'."); Environment.Exit (1); } - throw; + return false; } catch (CryptographicException) { if ((RootContext.StrongNameKeyContainer != null) || (RootContext.StrongNameKeyFile != null)) { Report.Error (1548, "Could not use the specified key to strongname the assembly."); Environment.Exit (1); } - throw; + return false; } // @@ -129,10 +149,12 @@ namespace Mono.CSharp { // load the default symbol writer. // Module.Builder = Assembly.Builder.DefineDynamicModule ( - Basename (name), Basename (output), want_debugging_support); + Basename (name), Basename (output), false); if (want_debugging_support) - InitializeSymbolWriter (); + InitializeSymbolWriter (output); + + return true; } static public void Save (string name) @@ -152,82 +174,15 @@ namespace Mono.CSharp { catch (System.IO.IOException io) { Report.Error (16, "Could not write to file `"+name+"', cause: " + io.Message); } - } - } - - // - // Provides "local" store across code that can yield: locals - // or fields, notice that this should not be used by anonymous - // methods to create local storage, those only require - // variable mapping. - // - public class VariableStorage { - ILGenerator ig; - FieldBuilder fb; - LocalBuilder local; - - static int count; - - public VariableStorage (EmitContext ec, Type t) - { - count++; - if (ec.InIterator) - fb = IteratorHandler.Current.MapVariable ("s_", count.ToString (), t); - else - local = ec.ig.DeclareLocal (t); - ig = ec.ig; - } - - public void EmitThis () - { - if (fb != null) - ig.Emit (OpCodes.Ldarg_0); - } - - public void EmitStore () - { - if (fb == null) - ig.Emit (OpCodes.Stloc, local); - else - ig.Emit (OpCodes.Stfld, fb); - } - - public void EmitLoad () - { - if (fb == null) - ig.Emit (OpCodes.Ldloc, local); - else - ig.Emit (OpCodes.Ldfld, fb); - } - - public void EmitCall (MethodInfo mi) - { - // FIXME : we should handle a call like tostring - // here, where boxing is needed. However, we will - // never encounter that with the current usage. - - bool value_type_call; - EmitThis (); - if (fb == null) { - value_type_call = local.LocalType.IsValueType; - - if (value_type_call) - ig.Emit (OpCodes.Ldloca, local); - else - ig.Emit (OpCodes.Ldloc, local); - } else { - value_type_call = fb.FieldType.IsValueType; - - if (value_type_call) - ig.Emit (OpCodes.Ldflda, fb); - else - ig.Emit (OpCodes.Ldfld, fb); + catch (System.UnauthorizedAccessException ua) { + Report.Error (16, "Could not write to file `"+name+"', cause: " + ua.Message); } - - ig.Emit (value_type_call ? OpCodes.Call : OpCodes.Callvirt, mi); + + if (SymbolWriter != null) + SymbolWriter.WriteSymbolFile (); } } - + /// /// An Emit Context is created for each body of code (from methods, /// properties bodies, indexer bodies or constructor bodies) @@ -262,11 +217,26 @@ namespace Mono.CSharp { /// public bool IsStatic; + /// + /// Whether the actual created method is static or instance method. + /// Althoug the method might be declared as `static', if an anonymous + /// method is involved, we might turn this into an instance method. + /// + /// So this reflects the low-level staticness of the method, while + /// IsStatic represents the semantic, high-level staticness. + /// + public bool MethodIsStatic; + /// /// Whether we are emitting a field initializer /// public bool IsFieldInitializer; + /// + /// We are resolving a class'es base class and interfaces. + /// + public bool ResolvingTypeTree; + /// /// The value that is allowed to be returned or NULL if there is no /// return type. @@ -288,7 +258,7 @@ namespace Mono.CSharp { /// Whether we're control flow analysis enabled /// public bool DoFlowAnalysis; - + /// /// Keeps track of the Type to LocalBuilder temporary storage created /// to store structures (used to compute the address of the structure @@ -323,12 +293,6 @@ namespace Mono.CSharp { public bool IsLastStatement; - /// - /// Whether remapping of locals, parameters and fields is turned on. - /// Used by iterators and anonymous methods. - /// - public bool RemapToProxy; - /// /// Whether we are inside an unsafe block /// @@ -339,23 +303,21 @@ namespace Mono.CSharp { /// public bool InFixedInitializer; + public bool InRefOutArgumentResolving; + + public bool InCatch; + public bool InFinally; + /// /// Whether we are inside an anonymous method. /// - public bool InAnonymousMethod; + public AnonymousContainer CurrentAnonymousMethod; /// /// Location for this EmitContext /// public Location loc; - /// - /// Used to flag that it is ok to define types recursively, as the - /// expressions are being evaluated as part of the type lookup - /// during the type resolution process - /// - public bool ResolvingTypeTree; - /// /// Inside an enum definition, we do not resolve enumeration values /// to their enumerations, but rather to the underlying type/value @@ -366,7 +328,44 @@ namespace Mono.CSharp { /// public bool InEnumContext; + /// + /// Anonymous methods can capture local variables and fields, + /// this object tracks it. It is copied from the TopLevelBlock + /// field. + /// + public CaptureContext capture_context; + + /// + /// Trace when method is called and is obsolete then this member suppress message + /// when call is inside next [Obsolete] method or type. + /// + public bool TestObsoleteMethodUsage = true; + + /// + /// The current iterator + /// + public Iterator CurrentIterator; + + /// + /// Whether we are in the resolving stage or not + /// + enum Phase { + Created, + Resolving, + Emitting + } + + Phase current_phase; FlowBranching current_flow_branching; + + static int next_id = 0; + int id = ++next_id; + + public override string ToString () + { + return String.Format ("EmitContext ({0}:{1}:{2})", id, + CurrentIterator, capture_context, loc); + } public EmitContext (DeclSpace parent, DeclSpace ds, Location l, ILGenerator ig, Type return_type, int code_flags, bool is_constructor) @@ -377,14 +376,18 @@ namespace Mono.CSharp { DeclSpace = ds; CheckState = RootContext.Checked; ConstantCheckState = true; + + if ((return_type is TypeBuilder) && return_type.IsGenericTypeDefinition) + throw new InternalErrorException (); IsStatic = (code_flags & Modifiers.STATIC) != 0; + MethodIsStatic = IsStatic; InIterator = (code_flags & Modifiers.METHOD_YIELDS) != 0; - RemapToProxy = InIterator; ReturnType = return_type; IsConstructor = is_constructor; CurrentBlock = null; CurrentFile = 0; + current_phase = Phase.Created; if (parent != null){ // Can only be null for the ResolveType contexts. @@ -418,6 +421,12 @@ namespace Mono.CSharp { } } + public bool HaveCaptureInfo { + get { + return capture_context != null; + } + } + // // Starts a new code branching. This inherits the state of all local // variables and parameters from the current branching. @@ -435,15 +444,27 @@ namespace Mono.CSharp { { FlowBranching.BranchingType type; - if (CurrentBranching.Type == FlowBranching.BranchingType.Switch) + if ((CurrentBranching != null) && + (CurrentBranching.Type == FlowBranching.BranchingType.Switch)) type = FlowBranching.BranchingType.SwitchSection; else type = FlowBranching.BranchingType.Block; - current_flow_branching = FlowBranching.CreateBranching (CurrentBranching, type, block, block.StartLocation); + DoFlowAnalysis = true; + + current_flow_branching = FlowBranching.CreateBranching ( + CurrentBranching, type, block, block.StartLocation); return current_flow_branching; } + public FlowBranchingException StartFlowBranching (ExceptionStatement stmt) + { + FlowBranchingException branching = new FlowBranchingException ( + CurrentBranching, stmt); + current_flow_branching = branching; + return branching; + } + // // Ends a code branching. Merges the state of locals and parameters // from all the children of the ending branching. @@ -476,46 +497,160 @@ namespace Mono.CSharp { current_flow_branching = current_flow_branching.Parent; } - public void EmitTopBlock (Block block, InternalParameters ip, Location loc) + public void CaptureVariable (LocalInfo li) { - bool unreachable = false; + capture_context.AddLocal (CurrentAnonymousMethod, li); + li.IsCaptured = true; + } + + public void CaptureParameter (string name, Type t, int idx) + { + capture_context.AddParameter (this, CurrentAnonymousMethod, name, t, idx); + } + + public void CaptureThis () + { + capture_context.CaptureThis (); + } + + + // + // Use to register a field as captured + // + public void CaptureField (FieldExpr fe) + { + capture_context.AddField (fe); + } + + // + // Whether anonymous methods have captured variables + // + public bool HaveCapturedVariables () + { + if (capture_context != null) + return capture_context.HaveCapturedVariables; + return false; + } + + // + // Whether anonymous methods have captured fields or this. + // + public bool HaveCapturedFields () + { + if (capture_context != null) + return capture_context.HaveCapturedFields; + return false; + } + + // + // Emits the instance pointer for the host method + // + public void EmitMethodHostInstance (EmitContext target, AnonymousMethod am) + { + if (capture_context != null) + capture_context.EmitMethodHostInstance (target, am); + else if (IsStatic) + target.ig.Emit (OpCodes.Ldnull); + else + target.ig.Emit (OpCodes.Ldarg_0); + } + + // + // Returns whether the `local' variable has been captured by an anonymous + // method + // + public bool IsCaptured (LocalInfo local) + { + return capture_context.IsCaptured (local); + } + + public bool IsParameterCaptured (string name) + { + if (capture_context != null) + return capture_context.IsParameterCaptured (name); + return false; + } + + public void EmitMeta (ToplevelBlock b, InternalParameters ip) + { + if (capture_context != null) + capture_context.EmitAnonymousHelperClasses (this); + b.EmitMeta (this); + + if (HasReturnLabel) + ReturnLabel = ig.DefineLabel (); + } + + // + // Here until we can fix the problem with Mono.CSharp.Switch, which + // currently can not cope with ig == null during resolve (which must + // be fixed for switch statements to work on anonymous methods). + // + public void EmitTopBlock (ToplevelBlock block, InternalParameters ip, Location loc) + { + if (block == null) + return; + + bool unreachable; + + if (ResolveTopBlock (null, block, ip, loc, out unreachable)){ + EmitMeta (block, ip); + + current_phase = Phase.Emitting; + EmitResolvedTopBlock (block, unreachable); + } + } + + bool resolved; + + public bool ResolveTopBlock (EmitContext anonymous_method_host, ToplevelBlock block, + InternalParameters ip, Location loc, out bool unreachable) + { + current_phase = Phase.Resolving; + + unreachable = false; + + if (resolved) + return true; + + capture_context = block.CaptureContext; if (!Location.IsNull (loc)) CurrentFile = loc.File; - if (block != null){ - try { - int errors = Report.Errors; - - block.EmitMeta (this, ip); +#if PRODUCTION + try { +#endif + if (!block.ResolveMeta (this, ip)) + return false; - if (Report.Errors == errors){ - bool old_do_flow_analysis = DoFlowAnalysis; - DoFlowAnalysis = true; + bool old_do_flow_analysis = DoFlowAnalysis; + DoFlowAnalysis = true; + if (anonymous_method_host != null) current_flow_branching = FlowBranching.CreateBranching ( - null, FlowBranching.BranchingType.Block, block, loc); + anonymous_method_host.CurrentBranching, + FlowBranching.BranchingType.Block, block, loc); + else + current_flow_branching = block.TopLevelBranching; - if (!block.Resolve (this)) { - current_flow_branching = null; - DoFlowAnalysis = old_do_flow_analysis; - return; - } - - FlowBranching.Reachability reachability = current_flow_branching.MergeTopBlock (); + if (!block.Resolve (this)) { current_flow_branching = null; - DoFlowAnalysis = old_do_flow_analysis; + return false; + } - block.Emit (this); + FlowBranching.Reachability reachability = current_flow_branching.MergeTopBlock (); + current_flow_branching = null; - if (reachability.AlwaysReturns || - reachability.AlwaysThrows || - reachability.IsUnreachable) - unreachable = true; - } -#if FIXME - } catch (Exception e) { + DoFlowAnalysis = old_do_flow_analysis; + + if (reachability.AlwaysReturns || + reachability.AlwaysThrows || + reachability.IsUnreachable) + unreachable = true; +#if PRODUCTION + } catch (Exception e) { Console.WriteLine ("Exception caught by the compiler while compiling:"); Console.WriteLine (" Block that caused the problem begin at: " + loc); @@ -524,24 +659,36 @@ namespace Mono.CSharp { CurrentBlock.StartLocation, CurrentBlock.EndLocation); } Console.WriteLine (e.GetType ().FullName + ": " + e.Message); - Console.WriteLine (Report.FriendlyStackTrace (e)); - - Environment.Exit (1); -#else - } finally { -#endif - } + throw; } +#endif - if (ReturnType != null && !unreachable){ - if (!InIterator){ + if (ReturnType != null && !unreachable) { + if (CurrentAnonymousMethod == null) { Report.Error (161, loc, "Not all code paths return a value"); - return; + return false; + } else if (!CurrentAnonymousMethod.IsIterator) { + Report.Error ( + 1643, loc, "Not all code paths return a " + + "value in anonymous method of type `{0}'", + CurrentAnonymousMethod.Type); + return false; } } + block.CompleteContexts (); + resolved = true; + return true; + } + + public void EmitResolvedTopBlock (ToplevelBlock block, bool unreachable) + { + if (block != null) + block.Emit (this); + if (HasReturnLabel) ig.MarkLabel (ReturnLabel); + if (return_value != null){ ig.Emit (OpCodes.Ldloc, return_value); ig.Emit (OpCodes.Ret); @@ -559,14 +706,23 @@ namespace Mono.CSharp { // this case. // + bool in_iterator = (CurrentAnonymousMethod != null) && + CurrentAnonymousMethod.IsIterator && InIterator; + if ((block != null) && block.IsDestructor) { // Nothing to do; S.R.E automatically emits a leave. - } else if (HasReturnLabel || (!unreachable && !InIterator)) { + } else if (HasReturnLabel || (!unreachable && !in_iterator)) { if (ReturnType != null) ig.Emit (OpCodes.Ldloc, TemporaryReturn ()); ig.Emit (OpCodes.Ret); } } + + // + // Close pending helper classes if we are the toplevel + // + if (capture_context != null && capture_context.ParentToplevel == null) + capture_context.CloseAnonymousHelperClasses (); } /// @@ -581,7 +737,31 @@ namespace Mono.CSharp { if (check_file && (CurrentFile != loc.File)) return; - ig.MarkSequencePoint (null, loc.Row, 0, 0, 0); + CodeGen.SymbolWriter.MarkSequencePoint (ig, loc.Row, 0); + } + + public void DefineLocalVariable (string name, LocalBuilder builder) + { + if (CodeGen.SymbolWriter == null) + return; + + CodeGen.SymbolWriter.DefineLocalVariable (name, builder); + } + + public void BeginScope () + { + ig.BeginScope(); + + if (CodeGen.SymbolWriter != null) + CodeGen.SymbolWriter.OpenScope(ig); + } + + public void EndScope () + { + ig.EndScope(); + + if (CodeGen.SymbolWriter != null) + CodeGen.SymbolWriter.CloseScope(ig); } /// @@ -665,84 +845,90 @@ namespace Mono.CSharp { /// return value from the function. By default this is not /// used. This is only required when returns are found inside /// Try or Catch statements. + /// + /// This method is typically invoked from the Emit phase, so + /// we allow the creation of a return label if it was not + /// requested during the resolution phase. Could be cleaned + /// up, but it would replicate a lot of logic in the Emit phase + /// of the code that uses it. /// public LocalBuilder TemporaryReturn () { if (return_value == null){ return_value = ig.DeclareLocal (ReturnType); + if (!HasReturnLabel){ ReturnLabel = ig.DefineLabel (); HasReturnLabel = true; } + } return return_value; } + /// + /// This method is used during the Resolution phase to flag the + /// need to define the ReturnLabel + /// public void NeedReturnLabel () { - if (!HasReturnLabel) { - ReturnLabel = ig.DefineLabel (); - HasReturnLabel = true; + if (current_phase != Phase.Resolving){ + // + // The reason is that the `ReturnLabel' is declared between + // resolution and emission + // + throw new Exception ("NeedReturnLabel called from Emit phase, should only be called during Resolve"); } + + if (!InIterator && !HasReturnLabel) + HasReturnLabel = true; } // - // Creates a field `name' with the type `t' on the proxy class + // Emits the proper object to address fields on a remapped + // variable/parameter to field in anonymous-method/iterator proxy classes. // - public FieldBuilder MapVariable (string name, Type t) + public void EmitThis () { - if (InIterator){ - return IteratorHandler.Current.MapVariable ("v_", name, t); - } - - throw new Exception ("MapVariable for an unknown state"); + ig.Emit (OpCodes.Ldarg_0); + if (capture_context != null && CurrentAnonymousMethod != null){ + ScopeInfo si = CurrentAnonymousMethod.Scope; + while (si != null){ + if (si.ParentLink != null) + ig.Emit (OpCodes.Ldfld, si.ParentLink); + if (si.THIS != null){ + ig.Emit (OpCodes.Ldfld, si.THIS); + break; + } + si = si.ParentScope; + } + } } // - // Invoke this routine to remap a VariableInfo into the - // proper MemberAccess expression + // Emits the code necessary to load the instance required + // to access the captured LocalInfo // - public Expression RemapLocal (LocalInfo local_info) + public void EmitCapturedVariableInstance (LocalInfo li) { - FieldExpr fe = new FieldExpr (local_info.FieldBuilder, loc); - fe.InstanceExpression = new ProxyInstance (); - return fe.DoResolve (this); + if (capture_context == null) + throw new Exception ("Calling EmitCapturedContext when there is no capture_context"); + + capture_context.EmitCapturedVariableInstance (this, li, CurrentAnonymousMethod); } - public Expression RemapLocalLValue (LocalInfo local_info, Expression right_side) + public void EmitParameter (string name) { - FieldExpr fe = new FieldExpr (local_info.FieldBuilder, loc); - fe.InstanceExpression = new ProxyInstance (); - return fe.DoResolveLValue (this, right_side); + capture_context.EmitParameter (this, name); } - public Expression RemapParameter (int idx) + public void EmitAssignParameter (string name, Expression source, bool leave_copy, bool prepare_for_load) { - FieldExpr fe = new FieldExprNoAddress (IteratorHandler.Current.parameter_fields [idx], loc); - fe.InstanceExpression = new ProxyInstance (); - return fe.DoResolve (this); + capture_context.EmitAssignParameter (this, name, source, leave_copy, prepare_for_load); } - public Expression RemapParameterLValue (int idx, Expression right_side) + public void EmitAddressOfParameter (string name) { - FieldExpr fe = new FieldExprNoAddress (IteratorHandler.Current.parameter_fields [idx], loc); - fe.InstanceExpression = new ProxyInstance (); - return fe.DoResolveLValue (this, right_side); - } - - // - // Emits the proper object to address fields on a remapped - // variable/parameter to field in anonymous-method/iterator proxy classes. - // - public void EmitThis () - { - ig.Emit (OpCodes.Ldarg_0); - - if (!IsStatic){ - if (InIterator) - ig.Emit (OpCodes.Ldfld, IteratorHandler.Current.this_field); - else - throw new Exception ("EmitThis for an unknown state"); - } + capture_context.EmitAddressOfParameter (this, name); } public Expression GetThis (Location loc) @@ -761,87 +947,58 @@ namespace Mono.CSharp { } - public abstract class CommonAssemblyModulClass: IAttributeSupport { - protected Hashtable m_attributes; - - protected CommonAssemblyModulClass () + public abstract class CommonAssemblyModulClass: Attributable { + protected CommonAssemblyModulClass (): + base (null) { - m_attributes = new Hashtable (); } - // - // Adds a global attribute that was declared in `container', - // the attribute is in `attr', and it was defined at `loc' - // - public void AddAttribute (TypeContainer container, AttributeSection attr) + public void AddAttributes (ArrayList attrs) { - NamespaceEntry ns = container.NamespaceEntry; - Attributes a = (Attributes) m_attributes [ns]; - - if (a == null) { - m_attributes [ns] = new Attributes (attr); + if (OptAttributes == null) { + OptAttributes = new Attributes (attrs); return; } - - a.AddAttributeSection (attr); + OptAttributes.AddAttributes (attrs); } - public virtual void Emit () + public virtual void Emit (TypeContainer tc) { - if (m_attributes.Count < 1) + if (OptAttributes == null) return; - TypeContainer dummy = new TypeContainer (); - EmitContext temp_ec = new EmitContext (dummy, Mono.CSharp.Location.Null, null, null, 0, false); - - foreach (DictionaryEntry de in m_attributes) - { - NamespaceEntry ns = (NamespaceEntry) de.Key; - Attributes attrs = (Attributes) de.Value; - - dummy.NamespaceEntry = ns; - Attribute.ApplyAttributes (temp_ec, null, this, attrs); - } + EmitContext ec = new EmitContext (tc, Mono.CSharp.Location.Null, null, null, 0, false); + OptAttributes.Emit (ec, this); } - - protected Attribute GetClsCompliantAttribute () + + protected Attribute ResolveAttribute (Type a_type) { - if (m_attributes.Count < 1) + if (OptAttributes == null) return null; - EmitContext temp_ec = new EmitContext (new TypeContainer (), Mono.CSharp.Location.Null, null, null, 0, false); - - foreach (DictionaryEntry de in m_attributes) { - - NamespaceEntry ns = (NamespaceEntry) de.Key; - Attributes attrs = (Attributes) de.Value; - temp_ec.TypeContainer.NamespaceEntry = ns; - - foreach (AttributeSection attr_section in attrs.AttributeSections) { - foreach (Attribute a in attr_section.Attributes) { - TypeExpr attributeType = RootContext.LookupType (temp_ec.DeclSpace, Attributes.GetAttributeFullName (a.Name), true, Location.Null); - if (attributeType != null && attributeType.Type == TypeManager.cls_compliant_attribute_type) { - a.Resolve (temp_ec); - return a; - } - } - } + // Ensure that we only have GlobalAttributes, since the Search below isn't safe with other types. + if (!OptAttributes.CheckTargets (this)) + return null; + + EmitContext temp_ec = new EmitContext (RootContext.Tree.Types, Mono.CSharp.Location.Null, null, null, 0, false); + Attribute a = OptAttributes.Search (a_type, temp_ec); + if (a != null) { + a.Resolve (temp_ec); } - return null; + return a; } - - #region IAttributeSupport Members - public abstract void SetCustomAttribute(CustomAttributeBuilder customBuilder); - #endregion - } - public class AssemblyClass: CommonAssemblyModulClass { // TODO: make it private and move all builder based methods here public AssemblyBuilder Builder; bool is_cls_compliant; + public Attribute ClsCompliantAttribute; + + ListDictionary declarative_security; + + static string[] attribute_targets = new string [] { "assembly" }; public AssemblyClass (): base () { @@ -854,31 +1011,76 @@ namespace Mono.CSharp { } } + public override AttributeTargets AttributeTargets { + get { + return AttributeTargets.Assembly; + } + } + + public override bool IsClsComplianceRequired(DeclSpace ds) + { + return is_cls_compliant; + } + public void ResolveClsCompliance () { - Attribute a = GetClsCompliantAttribute (); - if (a == null) + ClsCompliantAttribute = ResolveAttribute (TypeManager.cls_compliant_attribute_type); + if (ClsCompliantAttribute == null) return; - is_cls_compliant = a.GetClsCompliantAttributeValue (null); + is_cls_compliant = ClsCompliantAttribute.GetClsCompliantAttributeValue (null); } + // fix bug #56621 + private void SetPublicKey (AssemblyName an, byte[] strongNameBlob) + { + try { + // check for possible ECMA key + if (strongNameBlob.Length == 16) { + // will be rejected if not "the" ECMA key + an.SetPublicKey (strongNameBlob); + } + else { + // take it, with or without, a private key + RSA rsa = CryptoConvert.FromCapiKeyBlob (strongNameBlob); + // and make sure we only feed the public part to Sys.Ref + byte[] publickey = CryptoConvert.ToCapiPublicKeyBlob (rsa); + + // AssemblyName.SetPublicKey requires an additional header + byte[] publicKeyHeader = new byte [12] { 0x00, 0x24, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00 }; + + byte[] encodedPublicKey = new byte [12 + publickey.Length]; + Buffer.BlockCopy (publicKeyHeader, 0, encodedPublicKey, 0, 12); + Buffer.BlockCopy (publickey, 0, encodedPublicKey, 12, publickey.Length); + an.SetPublicKey (encodedPublicKey); + } + } + catch (Exception) { + Report.Error (1548, "Could not strongname the assembly. File `" + + RootContext.StrongNameKeyFile + "' incorrectly encoded."); + Environment.Exit (1); + } + } + + // TODO: rewrite this code (to kill N bugs and make it faster) and use standard ApplyAttribute way. public AssemblyName GetAssemblyName (string name, string output) { - // scan assembly attributes for strongname related attr - foreach (DictionaryEntry nsattr in m_attributes) { - ArrayList list = ((Attributes)nsattr.Value).AttributeSections; - for (int i=0; i < list.Count; i++) { - AttributeSection asect = (AttributeSection) list [i]; - if (asect.Target != "assembly") - continue; - // strongname attributes don't support AllowMultiple - Attribute a = (Attribute) asect.Attributes [0]; + if (OptAttributes != null) { + foreach (Attribute a in OptAttributes.Attrs) { + // cannot rely on any resolve-based members before you call Resolve + if (a.ExplicitTarget == null || a.ExplicitTarget != "assembly") + continue; + + // TODO: This code is buggy: comparing Attribute name without resolving it is wrong. + // However, this is invoked by CodeGen.Init, at which time none of the namespaces + // are loaded yet. switch (a.Name) { case "AssemblyKeyFile": + case "AssemblyKeyFileAttribute": + case "System.Reflection.AssemblyKeyFileAttribute": if (RootContext.StrongNameKeyFile != null) { - Report.Warning (1616, "Compiler option -keyfile overrides " + - "AssemblyKeyFileAttribute"); + Report.SymbolRelatedToPreviousError (a.Location, a.Name); + Report.Warning (1616, "Compiler option '{0}' overrides '{1}' given in source", "keyfile", "System.Reflection.AssemblyKeyFileAttribute"); } else { string value = a.GetString (); @@ -887,9 +1089,11 @@ namespace Mono.CSharp { } break; case "AssemblyKeyName": + case "AssemblyKeyNameAttribute": + case "System.Reflection.AssemblyKeyNameAttribute": if (RootContext.StrongNameKeyContainer != null) { - Report.Warning (1616, "Compiler option -keycontainer overrides " + - "AssemblyKeyNameAttribute"); + Report.SymbolRelatedToPreviousError (a.Location, a.Name); + Report.Warning (1616, "keycontainer", "Compiler option '{0}' overrides '{1}' given in source", "System.Reflection.AssemblyKeyNameAttribute"); } else { string value = a.GetString (); @@ -898,6 +1102,8 @@ namespace Mono.CSharp { } break; case "AssemblyDelaySign": + case "AssemblyDelaySignAttribute": + case "System.Reflection.AssemblyDelaySignAttribute": RootContext.StrongNameDelaySign = a.GetBoolean (); break; } @@ -939,25 +1145,7 @@ namespace Mono.CSharp { if (RootContext.StrongNameDelaySign) { // delayed signing - DO NOT include private key - try { - // check for possible ECMA key - if (snkeypair.Length == 16) { - // will be rejected if not "the" ECMA key - an.KeyPair = new StrongNameKeyPair (snkeypair); - } - else { - // take it, with or without, a private key - RSA rsa = CryptoConvert.FromCapiKeyBlob (snkeypair); - // and make sure we only feed the public part to Sys.Ref - byte[] publickey = CryptoConvert.ToCapiPublicKeyBlob (rsa); - an.KeyPair = new StrongNameKeyPair (publickey); - } - } - catch (Exception) { - Report.Error (1548, "Could not strongname the assembly. File `" + - RootContext.StrongNameKeyFile + "' incorrectly encoded."); - Environment.Exit (1); - } + SetPublicKey (an, snkeypair); } else { // no delay so we make sure we have the private key @@ -976,7 +1164,7 @@ namespace Mono.CSharp { RootContext.StrongNameKeyFile + "' doesn't have a private key."); } - Environment.Exit (1); + return null; } } } @@ -984,37 +1172,91 @@ namespace Mono.CSharp { else { Report.Error (1548, "Could not strongname the assembly. File `" + RootContext.StrongNameKeyFile + "' not found."); - Environment.Exit (1); + return null; } return an; } - public override void SetCustomAttribute(CustomAttributeBuilder customBuilder) + public override void ApplyAttributeBuilder (Attribute a, CustomAttributeBuilder customBuilder) { + if (a.Type.IsSubclassOf (TypeManager.security_attr_type) && a.CheckSecurityActionValidity (true)) { + if (declarative_security == null) + declarative_security = new ListDictionary (); + + a.ExtractSecurityPermissionSet (declarative_security); + return; + } + Builder.SetCustomAttribute (customBuilder); } + + public override void Emit (TypeContainer tc) + { + base.Emit (tc); + + if (declarative_security != null) { + + MethodInfo add_permission = typeof (AssemblyBuilder).GetMethod ("AddPermissionRequests", BindingFlags.Instance | BindingFlags.NonPublic); + object builder_instance = Builder; + + try { + // Microsoft runtime hacking + if (add_permission == null) { + Type assembly_builder = typeof (AssemblyBuilder).Assembly.GetType ("System.Reflection.Emit.AssemblyBuilderData"); + add_permission = assembly_builder.GetMethod ("AddPermissionRequests", BindingFlags.Instance | BindingFlags.NonPublic); + + FieldInfo fi = typeof (AssemblyBuilder).GetField ("m_assemblyData", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.GetField); + builder_instance = fi.GetValue (Builder); + } + + object[] args = new object [] { declarative_security [SecurityAction.RequestMinimum], + declarative_security [SecurityAction.RequestOptional], + declarative_security [SecurityAction.RequestRefuse] }; + add_permission.Invoke (builder_instance, args); + } + catch { + Report.RuntimeMissingSupport (Location.Null, "assembly permission setting"); + } + } + } + + public override string[] ValidAttributeTargets { + get { + return attribute_targets; + } + } } public class ModuleClass: CommonAssemblyModulClass { // TODO: make it private and move all builder based methods here public ModuleBuilder Builder; - bool m_module_is_unsafe; + public CharSet DefaultCharSet = CharSet.Ansi; + public TypeAttributes DefaultCharSetType = TypeAttributes.AnsiClass; + + static string[] attribute_targets = new string [] { "module" }; + public ModuleClass (bool is_unsafe) { m_module_is_unsafe = is_unsafe; } - public override void Emit () - { - base.Emit (); + public override AttributeTargets AttributeTargets { + get { + return AttributeTargets.Module; + } + } - Attribute a = GetClsCompliantAttribute (); - if (a != null) { - Report.Warning (3012, a.Location); + public override bool IsClsComplianceRequired(DeclSpace ds) + { + return CodeGen.Assembly.IsClsCompliant; } + public override void Emit (TypeContainer tc) + { + base.Emit (tc); + if (!m_module_is_unsafe) return; @@ -1023,13 +1265,54 @@ namespace Mono.CSharp { return; } - SetCustomAttribute (new CustomAttributeBuilder (TypeManager.unverifiable_code_ctor, new object [0])); + Builder.SetCustomAttribute (new CustomAttributeBuilder (TypeManager.unverifiable_code_ctor, new object [0])); } - public override void SetCustomAttribute(CustomAttributeBuilder customBuilder) + public override void ApplyAttributeBuilder (Attribute a, CustomAttributeBuilder customBuilder) { + if (a.Type == TypeManager.cls_compliant_attribute_type) { + if (CodeGen.Assembly.ClsCompliantAttribute == null) { + Report.Warning (3012, a.Location, "You must specify the CLSCompliant attribute on the assembly, not the module, to enable CLS compliance checking"); + } + else if (CodeGen.Assembly.IsClsCompliant != a.GetBoolean ()) { + Report.SymbolRelatedToPreviousError (CodeGen.Assembly.ClsCompliantAttribute.Location, CodeGen.Assembly.ClsCompliantAttribute.Name); + Report.Error (3017, a.Location, "You cannot specify the CLSCompliant attribute on a module that differs from the CLSCompliant attribute on the assembly"); + return; + } + } + Builder.SetCustomAttribute (customBuilder); } - } + /// + /// It is called very early therefore can resolve only predefined attributes + /// + public void ResolveAttributes () + { + Attribute a = ResolveAttribute (TypeManager.default_charset_type); + if (a != null) { + DefaultCharSet = a.GetCharSetValue (); + switch (DefaultCharSet) { + case CharSet.Ansi: + case CharSet.None: + break; + case CharSet.Auto: + DefaultCharSetType = TypeAttributes.AutoClass; + break; + case CharSet.Unicode: + DefaultCharSetType = TypeAttributes.UnicodeClass; + break; + default: + Report.Error (1724, a.Location, "Value specified for the argument to 'System.Runtime.InteropServices.DefaultCharSetAttribute' is not valid"); + break; + } + } + } + + public override string[] ValidAttributeTargets { + get { + return attribute_targets; + } + } + } }