// // codegen.cs: The code generator // // Author: // Miguel de Icaza (miguel@ximian.com) // // Copyright 2001, 2002, 2003 Ximian, Inc. // Copyright 2004 Novell, Inc. // // // Please leave this defined on SVN: The idea is that when we ship the // compiler to end users, if the compiler crashes, they have a chance // to narrow down the problem. // // Only remove it if you need to debug locally on your tree. // //#define PRODUCTION using System; using System.IO; using System.Collections; using System.Collections.Specialized; using System.Globalization; 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; namespace Mono.CSharp { /// /// Code generator class. /// public class CodeGen { static AppDomain current_domain; public static AssemblyClass Assembly; static CodeGen () { Reset (); } public static void Reset () { Assembly = new AssemblyClass (); } public static string Basename (string name) { int pos = name.LastIndexOf ('/'); if (pos != -1) return name.Substring (pos + 1); pos = name.LastIndexOf ('\\'); if (pos != -1) return name.Substring (pos + 1); return name; } public static string Dirname (string name) { int pos = name.LastIndexOf ('/'); if (pos != -1) return name.Substring (0, pos); pos = name.LastIndexOf ('\\'); if (pos != -1) return name.Substring (0, pos); return "."; } static public string FileName; #if MS_COMPATIBLE const AssemblyBuilderAccess COMPILER_ACCESS = 0; #else /* Keep this in sync with System.Reflection.Emit.AssemblyBuilder */ const AssemblyBuilderAccess COMPILER_ACCESS = (AssemblyBuilderAccess) 0x800; #endif // // Initializes the code generator variables for interactive use (repl) // static public void InitDynamic (string name) { current_domain = AppDomain.CurrentDomain; AssemblyName an = Assembly.GetAssemblyName (name, name); Assembly.Builder = current_domain.DefineDynamicAssembly (an, AssemblyBuilderAccess.Run | COMPILER_ACCESS); RootContext.ToplevelTypes = new ModuleContainer (true); RootContext.ToplevelTypes.Builder = Assembly.Builder.DefineDynamicModule (Basename (name), false); Assembly.Name = Assembly.Builder.GetName (); } // // Initializes the code generator variables // 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 GlobalRootNamespace.Instance.Assemblies) { AssemblyName ref_name = a.GetName (); byte [] b = ref_name.GetPublicKeyToken (); if (b == null || b.Length == 0) { Report.Error (1577, "Assembly generation failed " + "-- Referenced assembly '" + ref_name.Name + "' does not have a strong name."); //Environment.Exit (1); } } } current_domain = AppDomain.CurrentDomain; try { Assembly.Builder = current_domain.DefineDynamicAssembly (an, AssemblyBuilderAccess.RunAndSave | COMPILER_ACCESS, Dirname (name)); } catch (ArgumentException) { // specified key may not be exportable outside it's container if (RootContext.StrongNameKeyContainer != null) { Report.Error (1548, "Could not access the key inside the container `" + RootContext.StrongNameKeyContainer + "'."); Environment.Exit (1); } throw; } 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); } return false; } // Get the complete AssemblyName from the builder // (We need to get the public key and token) Assembly.Name = Assembly.Builder.GetName (); // // Pass a path-less name to DefineDynamicModule. Wonder how // this copes with output in different directories then. // FIXME: figure out how this copes with --output /tmp/blah // // If the third argument is true, the ModuleBuilder will dynamically // load the default symbol writer. // try { RootContext.ToplevelTypes.Builder = Assembly.Builder.DefineDynamicModule ( Basename (name), Basename (output), want_debugging_support); #if !MS_COMPATIBLE // TODO: We should use SymbolWriter from DefineDynamicModule if (want_debugging_support && !SymbolWriter.Initialize (RootContext.ToplevelTypes.Builder, output)) { Report.Error (40, "Unexpected debug information initialization error `{0}'", "Could not find the symbol writer assembly (Mono.CompilerServices.SymbolWriter.dll)"); return false; } #endif } catch (ExecutionEngineException e) { Report.Error (40, "Unexpected debug information initialization error `{0}'", e.Message); return false; } return true; } static public void Save (string name, bool saveDebugInfo) { #if GMCS_SOURCE PortableExecutableKinds pekind; ImageFileMachine machine; switch (RootContext.Platform) { case Platform.X86: pekind = PortableExecutableKinds.Required32Bit; machine = ImageFileMachine.I386; break; case Platform.X64: pekind = PortableExecutableKinds.PE32Plus; machine = ImageFileMachine.AMD64; break; case Platform.IA64: pekind = PortableExecutableKinds.PE32Plus; machine = ImageFileMachine.IA64; break; case Platform.AnyCPU: default: pekind = PortableExecutableKinds.ILOnly; machine = ImageFileMachine.I386; break; } #endif try { #if GMCS_SOURCE Assembly.Builder.Save (Basename (name), pekind, machine); #else Assembly.Builder.Save (Basename (name)); #endif } catch (COMException) { if ((RootContext.StrongNameKeyFile == null) || (!RootContext.StrongNameDelaySign)) throw; // FIXME: it seems Microsoft AssemblyBuilder doesn't like to delay sign assemblies Report.Error (1548, "Couldn't delay-sign the assembly with the '" + RootContext.StrongNameKeyFile + "', Use MCS with the Mono runtime or CSC to compile this assembly."); } catch (System.IO.IOException io) { Report.Error (16, "Could not write to file `"+name+"', cause: " + io.Message); return; } catch (System.UnauthorizedAccessException ua) { Report.Error (16, "Could not write to file `"+name+"', cause: " + ua.Message); return; } catch (System.NotImplementedException nie) { Report.RuntimeMissingSupport (Location.Null, nie.Message); return; } // // Write debuger symbol file // if (saveDebugInfo) SymbolWriter.WriteSymbolFile (); } } /// /// An interface to hold all the information needed in the resolving phase. /// public interface IMemberContext { // // A scope type context, it can be inflated for generic types // Type CurrentType { get; } // // A scope type parameters either VAR or MVAR // TypeParameter[] CurrentTypeParameters { get; } // // A type definition of the type context. For partial types definition use // CurrentTypeDefinition.PartialContainer otherwise the context is local // // TODO: CurrentType.Definition // TypeContainer CurrentTypeDefinition { get; } bool IsInObsoleteScope { get; } bool IsInUnsafeScope { get; } bool IsStatic { get; } ExtensionMethodGroupExpr LookupExtensionMethod (Type extensionType, string name, Location loc); FullNamedExpression LookupNamespaceOrType (string name, Location loc, bool ignore_cs0104); FullNamedExpression LookupNamespaceAlias (string name); } // // Block or statement resolving context // public class BlockContext : ResolveContext { FlowBranching current_flow_branching; public BlockContext (IMemberContext mc, ExplicitBlock block, Type returnType) : base (mc) { if (returnType == null) throw new ArgumentNullException ("returnType"); this.return_type = returnType; // TODO: check for null value CurrentBlock = block; } public override FlowBranching CurrentBranching { get { return current_flow_branching; } } // // Starts a new code branching. This inherits the state of all local // variables and parameters from the current branching. // public FlowBranching StartFlowBranching (FlowBranching.BranchingType type, Location loc) { current_flow_branching = FlowBranching.CreateBranching (CurrentBranching, type, null, loc); return current_flow_branching; } // // Starts a new code branching for block `block'. // public FlowBranching StartFlowBranching (Block block) { Set (Options.DoFlowAnalysis); current_flow_branching = FlowBranching.CreateBranching ( CurrentBranching, FlowBranching.BranchingType.Block, block, block.StartLocation); return current_flow_branching; } public FlowBranchingTryCatch StartFlowBranching (TryCatch stmt) { FlowBranchingTryCatch branching = new FlowBranchingTryCatch (CurrentBranching, stmt); current_flow_branching = branching; return branching; } public FlowBranchingException StartFlowBranching (ExceptionStatement stmt) { FlowBranchingException branching = new FlowBranchingException (CurrentBranching, stmt); current_flow_branching = branching; return branching; } public FlowBranchingLabeled StartFlowBranching (LabeledStatement stmt) { FlowBranchingLabeled branching = new FlowBranchingLabeled (CurrentBranching, stmt); current_flow_branching = branching; return branching; } public FlowBranchingIterator StartFlowBranching (Iterator iterator) { FlowBranchingIterator branching = new FlowBranchingIterator (CurrentBranching, iterator); current_flow_branching = branching; return branching; } public FlowBranchingToplevel StartFlowBranching (ToplevelBlock stmt, FlowBranching parent) { FlowBranchingToplevel branching = new FlowBranchingToplevel (parent, 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. // public bool EndFlowBranching () { FlowBranching old = current_flow_branching; current_flow_branching = current_flow_branching.Parent; FlowBranching.UsageVector vector = current_flow_branching.MergeChild (old); return vector.IsUnreachable; } // // Kills the current code branching. This throws away any changed state // information and should only be used in case of an error. // // FIXME: this is evil public void KillFlowBranching () { current_flow_branching = current_flow_branching.Parent; } // // This method is used during the Resolution phase to flag the // need to define the ReturnLabel // public void NeedReturnLabel () { if (!HasReturnLabel) HasReturnLabel = true; } } /// /// An Emit Context is created for each body of code (from methods, /// properties bodies, indexer bodies or constructor bodies) /// public class EmitContext : IMemberContext { [Flags] public enum Options { /// /// This flag tracks the `checked' state of the compilation, /// it controls whether we should generate code that does overflow /// checking, or if we generate code that ignores overflows. /// /// The default setting comes from the command line option to generate /// checked or unchecked code plus any source code changes using the /// checked/unchecked statements or expressions. Contrast this with /// the ConstantCheckState flag. /// CheckedScope = 1 << 0, /// /// The constant check state is always set to `true' and cant be changed /// from the command line. The source code can change this setting with /// the `checked' and `unchecked' statements and expressions. /// ConstantCheckState = 1 << 1, AllCheckStateFlags = CheckedScope | ConstantCheckState, // // unsafe { ... } scope // UnsafeScope = 1 << 2, CatchScope = 1 << 3, FinallyScope = 1 << 4, FieldInitializerScope = 1 << 5, CompoundAssignmentScope = 1 << 6, FixedInitializerScope = 1 << 7, BaseInitializer = 1 << 8, // // Inside an enum definition, we do not resolve enumeration values // to their enumerations, but rather to the underlying type/value // This is so EnumVal + EnumValB can be evaluated. // // There is no "E operator + (E x, E y)", so during an enum evaluation // we relax the rules // EnumScope = 1 << 9, ConstantScope = 1 << 10, ConstructorScope = 1 << 11, /// /// Whether control flow analysis is enabled /// DoFlowAnalysis = 1 << 20, /// /// Whether control flow analysis is disabled on structs /// (only meaningful when DoFlowAnalysis is set) /// OmitStructFlowAnalysis = 1 << 21, /// /// Indicates the current context is in probing mode, no errors are reported. /// ProbingMode = 1 << 22, // // Return and ContextualReturn statements will set the ReturnType // value based on the expression types of each return statement // instead of the method return type which is initially null. // InferReturnType = 1 << 23, OmitDebuggingInfo = 1 << 24 } // utility helper for CheckExpr, UnCheckExpr, Checked and Unchecked statements // it's public so that we can use a struct at the callsite public struct FlagsHandle : IDisposable { EmitContext ec; readonly Options invmask, oldval; public FlagsHandle (EmitContext ec, Options flagsToSet) : this (ec, flagsToSet, flagsToSet) { } internal FlagsHandle (EmitContext ec, Options mask, Options val) { this.ec = ec; invmask = ~mask; oldval = ec.flags & mask; ec.flags = (ec.flags & invmask) | (val & mask); if ((mask & Options.ProbingMode) != 0) Report.DisableReporting (); } public void Dispose () { if ((invmask & Options.ProbingMode) == 0) Report.EnableReporting (); ec.flags = (ec.flags & invmask) | oldval; } } Options flags; // // Holds a varible used during collection or object initialization. // public Expression CurrentInitializerVariable; public ILGenerator ig; /// /// The value that is allowed to be returned or NULL if there is no /// return type. /// protected Type return_type; /// /// Keeps track of the Type to LocalBuilder temporary storage created /// to store structures (used to compute the address of the structure /// value on structure method invocations) /// public Hashtable temporary_storage; public Block CurrentBlock; /// /// The location where we store the return value. /// public LocalBuilder return_value; /// /// The location where return has to jump to return the /// value /// public Label ReturnLabel; /// /// If we already defined the ReturnLabel /// public bool HasReturnLabel; /// /// Whether we are inside an anonymous method. /// public AnonymousExpression CurrentAnonymousMethod; public IMemberContext MemberContext; /// /// The current iterator /// public Iterator CurrentIterator { get { return CurrentAnonymousMethod as Iterator; } } public TypeInferenceContext ReturnTypeInference; public EmitContext (IMemberContext rc, ILGenerator ig, Type return_type) { this.MemberContext = rc; this.ig = ig; // // The default setting comes from the command line option // if (RootContext.Checked) flags |= Options.CheckedScope; // // The constant check state is always set to true // flags |= Options.ConstantCheckState; this.return_type = return_type; } public virtual FlowBranching CurrentBranching { get { return null; } } public Type CurrentType { get { return MemberContext.CurrentType; } } public TypeParameter[] CurrentTypeParameters { get { return MemberContext.CurrentTypeParameters; } } public TypeContainer CurrentTypeDefinition { get { return MemberContext.CurrentTypeDefinition; } } public bool ConstantCheckState { get { return (flags & Options.ConstantCheckState) != 0; } } public bool InUnsafe { get { return HasSet (Options.UnsafeScope) || MemberContext.IsInUnsafeScope; } } public bool DoFlowAnalysis { get { return (flags & Options.DoFlowAnalysis) != 0; } } public bool OmitStructFlowAnalysis { get { return (flags & Options.OmitStructFlowAnalysis) != 0; } } public bool HasSet (Options options) { return (this.flags & options) == options; } public bool HasAny (Options options) { return (this.flags & options) != 0; } // Temporarily set all the given flags to the given value. Should be used in an 'using' statement public FlagsHandle Set (Options options) { return new FlagsHandle (this, options); } public FlagsHandle With (Options options, bool enable) { return new FlagsHandle (this, options, enable ? options : 0); } public FlagsHandle WithFlowAnalysis (bool do_flow_analysis, bool omit_struct_analysis) { Options newflags = (do_flow_analysis ? Options.DoFlowAnalysis : 0) | (omit_struct_analysis ? Options.OmitStructFlowAnalysis : 0); return new FlagsHandle (this, Options.DoFlowAnalysis | Options.OmitStructFlowAnalysis, newflags); } // IResolveContext.IsInObsoleteScope public bool IsInObsoleteScope { get { // Disables obsolete checks when probing is on return IsInProbingMode || MemberContext.IsInObsoleteScope; } } public bool IsInProbingMode { get { return (flags & Options.ProbingMode) != 0; } } bool IMemberContext.IsInUnsafeScope { get { return InUnsafe; } } public bool IsStatic { get { return MemberContext.IsStatic; } } public bool IsVariableCapturingRequired { get { return !IsInProbingMode && (CurrentBranching == null || !CurrentBranching.CurrentUsageVector.IsUnreachable); } } public bool OmitDebuggingInfo { get { return (flags & Options.OmitDebuggingInfo) != 0; } set { if (value) flags |= Options.OmitDebuggingInfo; else flags &= ~Options.OmitDebuggingInfo; } } public bool MustCaptureVariable (LocalInfo local) { if (CurrentAnonymousMethod == null) return false; // FIXME: IsIterator is too aggressive, we should capture only if child // block contains yield if (CurrentAnonymousMethod.IsIterator) return true; return local.Block.Toplevel != CurrentBlock.Toplevel; } public Type ReturnType { get { return return_type; } } /// /// This is called immediately before emitting an IL opcode to tell the symbol /// writer to which source line this opcode belongs. /// public void Mark (Location loc) { if (!SymbolWriter.HasSymbolWriter || OmitDebuggingInfo || loc.IsNull) return; SymbolWriter.MarkSequencePoint (ig, loc); } public void DefineLocalVariable (string name, LocalBuilder builder) { SymbolWriter.DefineLocalVariable (name, builder); } public void BeginScope () { ig.BeginScope(); SymbolWriter.OpenScope(ig); } public void EndScope () { ig.EndScope(); SymbolWriter.CloseScope(ig); } /// /// Returns a temporary storage for a variable of type t as /// a local variable in the current body. /// public LocalBuilder GetTemporaryLocal (Type t) { if (temporary_storage != null) { object o = temporary_storage [t]; if (o != null) { if (o is Stack) { Stack s = (Stack) o; o = s.Count == 0 ? null : s.Pop (); } else { temporary_storage.Remove (t); } } if (o != null) return (LocalBuilder) o; } return ig.DeclareLocal (t); } public void FreeTemporaryLocal (LocalBuilder b, Type t) { if (temporary_storage == null) { temporary_storage = new Hashtable (); temporary_storage [t] = b; return; } object o = temporary_storage [t]; if (o == null) { temporary_storage [t] = b; return; } Stack s = o as Stack; if (s == null) { s = new Stack (); s.Push (o); temporary_storage [t] = s; } s.Push (b); } /// /// Current loop begin and end labels. /// public Label LoopBegin, LoopEnd; /// /// Default target in a switch statement. Only valid if /// InSwitch is true /// public Label DefaultTarget; /// /// If this is non-null, points to the current switch statement /// public Switch Switch; /// /// ReturnValue creates on demand the LocalBuilder for the /// 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 (return_type); if (!HasReturnLabel){ ReturnLabel = ig.DefineLabel (); HasReturnLabel = true; } } return return_value; } public Expression GetThis (Location loc) { This my_this; if (CurrentBlock != null) my_this = new This (CurrentBlock, loc); else my_this = new This (loc); if (!my_this.ResolveBase (this)) my_this = null; return my_this; } #region IResolveContext Members public ExtensionMethodGroupExpr LookupExtensionMethod (Type extensionType, string name, Location loc) { return MemberContext.LookupExtensionMethod (extensionType, name, loc); } public FullNamedExpression LookupNamespaceOrType (string name, Location loc, bool ignore_cs0104) { return MemberContext.LookupNamespaceOrType (name, loc, ignore_cs0104); } public FullNamedExpression LookupNamespaceAlias (string name) { return MemberContext.LookupNamespaceAlias (name); } #endregion } public class ResolveContext : EmitContext { public ResolveContext (IMemberContext mc) : base (mc, null, null) { } public ResolveContext (IMemberContext mc, Options options) : this (mc) { Set (options); } } public abstract class CommonAssemblyModulClass : Attributable, IMemberContext { public void AddAttributes (ArrayList attrs, IMemberContext context) { foreach (Attribute a in attrs) a.AttachTo (this, context); if (attributes == null) { attributes = new Attributes (attrs); return; } attributes.AddAttributes (attrs); } public virtual void Emit (TypeContainer tc) { if (OptAttributes == null) return; OptAttributes.Emit (); } protected Attribute ResolveAttribute (PredefinedAttribute a_type) { Attribute a = OptAttributes.Search (a_type); if (a != null) { a.Resolve (); } return a; } #region IResolveContext Members public Type CurrentType { get { return null; } } public TypeParameter[] CurrentTypeParameters { get { return null; } } public TypeContainer CurrentTypeDefinition { get { return RootContext.ToplevelTypes; } } public DeclSpace DeclContainer { get { return RootContext.ToplevelTypes; } } public bool IsInObsoleteScope { get { return false; } } public bool IsInUnsafeScope { get { return false; } } public bool IsStatic { get { return false; } } public ExtensionMethodGroupExpr LookupExtensionMethod (Type extensionType, string name, Location loc) { throw new NotImplementedException (); } public FullNamedExpression LookupNamespaceOrType (string name, Location loc, bool ignore_cs0104) { return RootContext.ToplevelTypes.LookupNamespaceOrType (name, loc, ignore_cs0104); } public FullNamedExpression LookupNamespaceAlias (string name) { return null; } #endregion } public class AssemblyClass : CommonAssemblyModulClass { // TODO: make it private and move all builder based methods here public AssemblyBuilder Builder; bool is_cls_compliant; bool wrap_non_exception_throws; public Attribute ClsCompliantAttribute; ListDictionary declarative_security; bool has_extension_method; public AssemblyName Name; MethodInfo add_type_forwarder; ListDictionary emitted_forwarders; // Module is here just because of error messages static string[] attribute_targets = new string [] { "assembly", "module" }; public AssemblyClass (): base () { wrap_non_exception_throws = true; } public bool HasExtensionMethods { set { has_extension_method = value; } } public bool IsClsCompliant { get { return is_cls_compliant; } } public bool WrapNonExceptionThrows { get { return wrap_non_exception_throws; } } public override AttributeTargets AttributeTargets { get { return AttributeTargets.Assembly; } } public override bool IsClsComplianceRequired () { return is_cls_compliant; } public void Resolve () { if (RootContext.Unsafe) { // // Emits [assembly: SecurityPermissionAttribute (SecurityAction.RequestMinimum, SkipVerification = true)] // when -unsafe option was specified // Location loc = Location.Null; MemberAccess system_security_permissions = new MemberAccess (new MemberAccess ( new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Security", loc), "Permissions", loc); Arguments pos = new Arguments (1); pos.Add (new Argument (new MemberAccess (new MemberAccess (system_security_permissions, "SecurityAction", loc), "RequestMinimum"))); Arguments named = new Arguments (1); named.Add (new NamedArgument (new LocatedToken (loc, "SkipVerification"), (new BoolLiteral (true, loc)))); GlobalAttribute g = new GlobalAttribute (new NamespaceEntry (null, null, null), "assembly", new MemberAccess (system_security_permissions, "SecurityPermissionAttribute"), new Arguments[] { pos, named }, loc, false); g.AttachTo (this, this); if (g.Resolve () != null) { declarative_security = new ListDictionary (); g.ExtractSecurityPermissionSet (declarative_security); } } if (OptAttributes == null) return; // Ensure that we only have GlobalAttributes, since the Search isn't safe with other types. if (!OptAttributes.CheckTargets()) return; ClsCompliantAttribute = ResolveAttribute (PredefinedAttributes.Get.CLSCompliant); if (ClsCompliantAttribute != null) { is_cls_compliant = ClsCompliantAttribute.GetClsCompliantAttributeValue (); } Attribute a = ResolveAttribute (PredefinedAttributes.Get.RuntimeCompatibility); if (a != null) { object val = a.GetPropertyValue ("WrapNonExceptionThrows"); if (val != null) wrap_non_exception_throws = (bool) val; } } // 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) { Error_AssemblySigning ("The specified file `" + RootContext.StrongNameKeyFile + "' is 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) { 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 is wrong. // However, this is invoked by CodeGen.Init, when none of the namespaces // are loaded yet. // TODO: Does not handle quoted attributes properly switch (a.Name) { case "AssemblyKeyFile": case "AssemblyKeyFileAttribute": case "System.Reflection.AssemblyKeyFileAttribute": if (RootContext.StrongNameKeyFile != null) { Report.SymbolRelatedToPreviousError (a.Location, a.GetSignatureForError ()); Report.Warning (1616, 1, "Option `{0}' overrides attribute `{1}' given in a source file or added module", "keyfile", "System.Reflection.AssemblyKeyFileAttribute"); } else { string value = a.GetString (); if (value != null && value.Length != 0) RootContext.StrongNameKeyFile = value; } break; case "AssemblyKeyName": case "AssemblyKeyNameAttribute": case "System.Reflection.AssemblyKeyNameAttribute": if (RootContext.StrongNameKeyContainer != null) { Report.SymbolRelatedToPreviousError (a.Location, a.GetSignatureForError ()); Report.Warning (1616, 1, "Option `{0}' overrides attribute `{1}' given in a source file or added module", "keycontainer", "System.Reflection.AssemblyKeyNameAttribute"); } else { string value = a.GetString (); if (value != null && value.Length != 0) RootContext.StrongNameKeyContainer = value; } break; case "AssemblyDelaySign": case "AssemblyDelaySignAttribute": case "System.Reflection.AssemblyDelaySignAttribute": RootContext.StrongNameDelaySign = a.GetBoolean (); break; } } } AssemblyName an = new AssemblyName (); an.Name = Path.GetFileNameWithoutExtension (name); // note: delay doesn't apply when using a key container if (RootContext.StrongNameKeyContainer != null) { an.KeyPair = new StrongNameKeyPair (RootContext.StrongNameKeyContainer); return an; } // strongname is optional if (RootContext.StrongNameKeyFile == null) return an; string AssemblyDir = Path.GetDirectoryName (output); // the StrongName key file may be relative to (a) the compiled // file or (b) to the output assembly. See bugzilla #55320 // http://bugzilla.ximian.com/show_bug.cgi?id=55320 // (a) relative to the compiled file string filename = Path.GetFullPath (RootContext.StrongNameKeyFile); bool exist = File.Exists (filename); if ((!exist) && (AssemblyDir != null) && (AssemblyDir != String.Empty)) { // (b) relative to the outputed assembly filename = Path.GetFullPath (Path.Combine (AssemblyDir, RootContext.StrongNameKeyFile)); exist = File.Exists (filename); } if (exist) { using (FileStream fs = new FileStream (filename, FileMode.Open, FileAccess.Read)) { byte[] snkeypair = new byte [fs.Length]; fs.Read (snkeypair, 0, snkeypair.Length); if (RootContext.StrongNameDelaySign) { // delayed signing - DO NOT include private key SetPublicKey (an, snkeypair); } else { // no delay so we make sure we have the private key try { CryptoConvert.FromCapiPrivateKeyBlob (snkeypair); an.KeyPair = new StrongNameKeyPair (snkeypair); } catch (CryptographicException) { if (snkeypair.Length == 16) { // error # is different for ECMA key Report.Error (1606, "Could not sign the assembly. " + "ECMA key can only be used to delay-sign assemblies"); } else { Error_AssemblySigning ("The specified file `" + RootContext.StrongNameKeyFile + "' does not have a private key"); } return null; } } } } else { Error_AssemblySigning ("The specified file `" + RootContext.StrongNameKeyFile + "' does not exist"); return null; } return an; } void Error_AssemblySigning (string text) { Report.Error (1548, "Error during assembly signing. " + text); } bool CheckInternalsVisibleAttribute (Attribute a) { string assembly_name = a.GetString (); if (assembly_name.Length == 0) return false; AssemblyName aname = null; try { #if GMCS_SOURCE aname = new AssemblyName (assembly_name); #else throw new NotSupportedException (); #endif } catch (FileLoadException) { } catch (ArgumentException) { } // Bad assembly name format if (aname == null) Report.Warning (1700, 3, a.Location, "Assembly reference `" + assembly_name + "' is invalid and cannot be resolved"); // Report error if we have defined Version or Culture else if (aname.Version != null || aname.CultureInfo != null) throw new Exception ("Friend assembly `" + a.GetString () + "' is invalid. InternalsVisibleTo cannot have version or culture specified."); else if (aname.GetPublicKey () == null && Name.GetPublicKey () != null && Name.GetPublicKey ().Length != 0) { Report.Error (1726, a.Location, "Friend assembly reference `" + aname.FullName + "' is invalid." + " Strong named assemblies must specify a public key in their InternalsVisibleTo declarations"); return false; } return true; } static bool IsValidAssemblyVersion (string version) { Version v; try { v = new Version (version); } catch { try { int major = int.Parse (version, CultureInfo.InvariantCulture); v = new Version (major, 0); } catch { return false; } } foreach (int candidate in new int [] { v.Major, v.Minor, v.Build, v.Revision }) { if (candidate > ushort.MaxValue) return false; } return true; } public override void ApplyAttributeBuilder (Attribute a, CustomAttributeBuilder cb, PredefinedAttributes pa) { if (a.IsValidSecurityAttribute ()) { if (declarative_security == null) declarative_security = new ListDictionary (); a.ExtractSecurityPermissionSet (declarative_security); return; } if (a.Type == pa.AssemblyCulture) { string value = a.GetString (); if (value == null || value.Length == 0) return; if (RootContext.Target == Target.Exe) { a.Error_AttributeEmitError ("The executables cannot be satelite assemblies, remove the attribute or keep it empty"); return; } } if (a.Type == pa.AssemblyVersion) { string value = a.GetString (); if (value == null || value.Length == 0) return; value = value.Replace ('*', '0'); if (!IsValidAssemblyVersion (value)) { a.Error_AttributeEmitError (string.Format ("Specified version `{0}' is not valid", value)); return; } } if (a.Type == pa.InternalsVisibleTo && !CheckInternalsVisibleAttribute (a)) return; if (a.Type == pa.TypeForwarder) { Type t = a.GetArgumentType (); if (t == null || TypeManager.HasElementType (t)) { Report.Error (735, a.Location, "Invalid type specified as an argument for TypeForwardedTo attribute"); return; } t = TypeManager.DropGenericTypeArguments (t); if (emitted_forwarders == null) { emitted_forwarders = new ListDictionary(); } else if (emitted_forwarders.Contains(t)) { Report.SymbolRelatedToPreviousError(((Attribute)emitted_forwarders[t]).Location, null); Report.Error(739, a.Location, "A duplicate type forward of type `{0}'", TypeManager.CSharpName(t)); return; } emitted_forwarders.Add(t, a); if (TypeManager.LookupDeclSpace (t) != null) { Report.SymbolRelatedToPreviousError (t); Report.Error (729, a.Location, "Cannot forward type `{0}' because it is defined in this assembly", TypeManager.CSharpName (t)); return; } if (t.DeclaringType != null) { Report.Error (730, a.Location, "Cannot forward type `{0}' because it is a nested type", TypeManager.CSharpName (t)); return; } if (add_type_forwarder == null) { add_type_forwarder = typeof (AssemblyBuilder).GetMethod ("AddTypeForwarder", BindingFlags.NonPublic | BindingFlags.Instance); if (add_type_forwarder == null) { Report.RuntimeMissingSupport (a.Location, "TypeForwardedTo attribute"); return; } } add_type_forwarder.Invoke (Builder, new object[] { t }); return; } if (a.Type == pa.Extension) { a.Error_MisusedExtensionAttribute (); return; } Builder.SetCustomAttribute (cb); } public override void Emit (TypeContainer tc) { base.Emit (tc); if (has_extension_method) PredefinedAttributes.Get.Extension.EmitAttribute (Builder); // FIXME: Does this belong inside SRE.AssemblyBuilder instead? PredefinedAttribute pa = PredefinedAttributes.Get.RuntimeCompatibility; if (pa.IsDefined && (OptAttributes == null || !OptAttributes.Contains (pa))) { ConstructorInfo ci = TypeManager.GetPredefinedConstructor ( pa.Type, Location.Null, Type.EmptyTypes); PropertyInfo [] pis = new PropertyInfo [1]; pis [0] = TypeManager.GetPredefinedProperty (pa.Type, "WrapNonExceptionThrows", Location.Null, TypeManager.bool_type); object [] pargs = new object [1]; pargs [0] = true; Builder.SetCustomAttribute (new CustomAttributeBuilder (ci, new object [0], pis, pargs)); } 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; } } // Wrapper for AssemblyBuilder.AddModule static MethodInfo adder_method; static public MethodInfo AddModule_Method { get { if (adder_method == null) adder_method = typeof (AssemblyBuilder).GetMethod ("AddModule", BindingFlags.Instance|BindingFlags.NonPublic); return adder_method; } } public Module AddModule (string module) { MethodInfo m = AddModule_Method; if (m == null) { Report.RuntimeMissingSupport (Location.Null, "/addmodule"); Environment.Exit (1); } try { return (Module) m.Invoke (Builder, new object [] { module }); } catch (TargetInvocationException ex) { throw ex.InnerException; } } } }