using System.Collections;
using System.Reflection;
using System.Reflection.Emit;
+using System.Runtime.InteropServices;
+using System.Security.Cryptography;
+
+using Mono.Security.Cryptography;
namespace Mono.CSharp {
/// </summary>
public class CodeGen {
static AppDomain current_domain;
- public static AssemblyBuilder AssemblyBuilder;
- public static ModuleBuilder ModuleBuilder;
-
static public SymbolWriter SymbolWriter;
+ public static AssemblyClass Assembly;
+ public static ModuleClass Module;
+
+ static CodeGen ()
+ {
+ Assembly = new AssemblyClass ();
+ Module = new ModuleClass (RootContext.Unsafe);
+ }
+
public static string Basename (string name)
{
- int pos = name.LastIndexOf ("/");
+ int pos = name.LastIndexOf ('/');
if (pos != -1)
return name.Substring (pos + 1);
- pos = name.LastIndexOf ("\\");
+ pos = name.LastIndexOf ('\\');
if (pos != -1)
return name.Substring (pos + 1);
public static string Dirname (string name)
{
- int pos = name.LastIndexOf ("/");
+ int pos = name.LastIndexOf ('/');
if (pos != -1)
return name.Substring (0, pos);
- pos = name.LastIndexOf ("\\");
+ pos = name.LastIndexOf ('\\');
if (pos != -1)
return name.Substring (0, pos);
static string TrimExt (string name)
{
- int pos = name.LastIndexOf (".");
+ int pos = name.LastIndexOf ('.');
return name.Substring (0, pos);
}
//
static void InitializeSymbolWriter ()
{
- SymbolWriter = SymbolWriter.GetSymbolWriter (ModuleBuilder);
+ SymbolWriter = SymbolWriter.GetSymbolWriter (Module.Builder);
//
// If we got an ISymbolWriter instance, initialize it.
//
static public void Init (string name, string output, bool want_debugging_support)
{
- AssemblyName an;
-
FileName = output;
- an = new AssemblyName ();
- an.Name = Path.GetFileNameWithoutExtension (name);
+ AssemblyName an = Assembly.GetAssemblyName (name, output);
current_domain = AppDomain.CurrentDomain;
- AssemblyBuilder = current_domain.DefineDynamicAssembly (
- an, AssemblyBuilderAccess.RunAndSave, Dirname (name));
+
+ try {
+ Assembly.Builder = current_domain.DefineDynamicAssembly (an,
+ AssemblyBuilderAccess.Save, 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);
+ }
+ throw;
+ }
//
// Pass a path-less name to DefineDynamicModule. Wonder how
// If the third argument is true, the ModuleBuilder will dynamically
// load the default symbol writer.
//
- ModuleBuilder = AssemblyBuilder.DefineDynamicModule (
+ Module.Builder = Assembly.Builder.DefineDynamicModule (
Basename (name), Basename (output), want_debugging_support);
if (want_debugging_support)
static public void Save (string name)
{
try {
- AssemblyBuilder.Save (Basename (name));
- } catch (System.IO.IOException io){
+ Assembly.Builder.Save (Basename (name));
+ }
+ 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);
}
}
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);
+ }
+
+ ig.Emit (value_type_call ? OpCodes.Call : OpCodes.Callvirt, mi);
+ }
}
/// <summary>
/// </summary>
public bool HasReturnLabel;
- /// <summary>
- /// Whether we are in a Finally block
- /// </summary>
- public bool InFinally;
-
- /// <summary>
- /// Whether we are in a Try block
- /// </summary>
- public bool InTry;
-
/// <summary>
/// Whether we are inside an iterator block.
/// </summary>
public bool InIterator;
- /// <summary>
- /// Whether we need an explicit return statement at the end of the method.
- /// </summary>
- public bool NeedExplicitReturn;
-
+ public bool IsLastStatement;
+
/// <summary>
/// Whether remapping of locals, parameters and fields is turned on.
/// Used by iterators and anonymous methods.
/// </summary>
public bool RemapToProxy;
- /// <summary>
- /// Whether we are in a Catch block
- /// </summary>
- public bool InCatch;
-
/// <summary>
/// Whether we are inside an unsafe block
/// </summary>
/// </summary>
public bool InEnumContext;
- protected Stack FlowStack;
+ FlowBranching current_flow_branching;
public EmitContext (DeclSpace parent, DeclSpace ds, Location l, ILGenerator ig,
Type return_type, int code_flags, bool is_constructor)
InUnsafe = (code_flags & Modifiers.UNSAFE) != 0;
}
loc = l;
-
- FlowStack = new Stack ();
if (ReturnType == TypeManager.void_type)
ReturnType = null;
public FlowBranching CurrentBranching {
get {
- return (FlowBranching) FlowStack.Peek ();
+ return current_flow_branching;
}
}
// Starts a new code branching. This inherits the state of all local
// variables and parameters from the current branching.
// </summary>
- public FlowBranching StartFlowBranching (FlowBranchingType type, Location loc)
+ public FlowBranching StartFlowBranching (FlowBranching.BranchingType type, Location loc)
{
- FlowBranching cfb = new FlowBranching (CurrentBranching, type, null, loc);
-
- FlowStack.Push (cfb);
-
- return cfb;
+ current_flow_branching = FlowBranching.CreateBranching (CurrentBranching, type, null, loc);
+ return current_flow_branching;
}
// <summary>
// </summary>
public FlowBranching StartFlowBranching (Block block)
{
- FlowBranching cfb;
- FlowBranchingType type;
+ FlowBranching.BranchingType type;
- if (CurrentBranching.Type == FlowBranchingType.SWITCH)
- type = FlowBranchingType.SWITCH_SECTION;
+ if (CurrentBranching.Type == FlowBranching.BranchingType.Switch)
+ type = FlowBranching.BranchingType.SwitchSection;
else
- type = FlowBranchingType.BLOCK;
+ type = FlowBranching.BranchingType.Block;
- cfb = new FlowBranching (CurrentBranching, type, block, block.StartLocation);
+ current_flow_branching = FlowBranching.CreateBranching (CurrentBranching, type, block, block.StartLocation);
+ return current_flow_branching;
+ }
- FlowStack.Push (cfb);
+ // <summary>
+ // Ends a code branching. Merges the state of locals and parameters
+ // from all the children of the ending branching.
+ // </summary>
+ public FlowBranching.UsageVector DoEndFlowBranching ()
+ {
+ FlowBranching old = current_flow_branching;
+ current_flow_branching = current_flow_branching.Parent;
- return cfb;
+ return current_flow_branching.MergeChild (old);
}
// <summary>
// Ends a code branching. Merges the state of locals and parameters
// from all the children of the ending branching.
// </summary>
- public FlowReturns EndFlowBranching ()
+ public FlowBranching.Reachability EndFlowBranching ()
{
- FlowBranching cfb = (FlowBranching) FlowStack.Pop ();
+ FlowBranching.UsageVector vector = DoEndFlowBranching ();
- return CurrentBranching.MergeChild (cfb);
+ return vector.Reachability;
}
// <summary>
// </summary>
public void KillFlowBranching ()
{
- FlowBranching cfb = (FlowBranching) FlowStack.Pop ();
+ current_flow_branching = current_flow_branching.Parent;
}
public void EmitTopBlock (Block block, InternalParameters ip, Location loc)
{
- bool has_ret = false;
+ bool unreachable = false;
if (!Location.IsNull (loc))
CurrentFile = loc.File;
bool old_do_flow_analysis = DoFlowAnalysis;
DoFlowAnalysis = true;
- FlowBranching cfb = new FlowBranching (block, loc);
- FlowStack.Push (cfb);
+ current_flow_branching = FlowBranching.CreateBranching (
+ null, FlowBranching.BranchingType.Block, block, loc);
if (!block.Resolve (this)) {
- FlowStack.Pop ();
+ current_flow_branching = null;
DoFlowAnalysis = old_do_flow_analysis;
return;
}
- cfb = (FlowBranching) FlowStack.Pop ();
- FlowReturns returns = cfb.MergeTopBlock ();
-
+ FlowBranching.Reachability reachability = current_flow_branching.MergeTopBlock ();
+ current_flow_branching = null;
+
DoFlowAnalysis = old_do_flow_analysis;
- has_ret = block.Emit (this);
-
- if ((returns == FlowReturns.ALWAYS) ||
- (returns == FlowReturns.EXCEPTION) ||
- (returns == FlowReturns.UNREACHABLE))
- has_ret = true;
+ block.Emit (this);
- if (Report.Errors == errors){
- if (RootContext.WarningLevel >= 3)
- block.UsageWarning ();
- }
+ if (reachability.AlwaysReturns ||
+ reachability.AlwaysThrows ||
+ reachability.IsUnreachable)
+ unreachable = true;
}
- } catch {
+ } catch (Exception e) {
Console.WriteLine ("Exception caught by the compiler while compiling:");
Console.WriteLine (" Block that caused the problem begin at: " + loc);
Console.WriteLine (" Block being compiled: [{0},{1}]",
CurrentBlock.StartLocation, CurrentBlock.EndLocation);
}
- throw;
+ Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
+ Console.WriteLine (Report.FriendlyStackTrace (e));
+
+ Environment.Exit (1);
}
}
- if (ReturnType != null && !has_ret){
- //
- // FIXME: we need full flow analysis to implement this
- // correctly and emit an error instead of a warning.
- //
- //
+ if (ReturnType != null && !unreachable){
if (!InIterator){
Report.Error (161, loc, "Not all code paths return a value");
return;
ig.Emit (OpCodes.Ldloc, return_value);
ig.Emit (OpCodes.Ret);
} else {
- if (!InTry){
- if (InIterator)
- has_ret = true;
-
- if (!has_ret || HasReturnLabel) {
- ig.Emit (OpCodes.Ret);
- NeedExplicitReturn = false;
- }
- }
-
- // Unfortunately, System.Reflection.Emit automatically emits a leave
- // to the end of a finally block. This is a problem if no code is
- // following the try/finally block since we may jump to a point after
- // the end of the method. As a workaround, emit an explicit ret here.
+ //
+ // If `HasReturnLabel' is set, then we already emitted a
+ // jump to the end of the method, so we must emit a `ret'
+ // there.
+ //
+ // Unfortunately, System.Reflection.Emit automatically emits
+ // a leave to the end of a finally block. This is a problem
+ // if no code is following the try/finally block since we may
+ // jump to a point after the end of the method.
+ // As a workaround, we're always creating a return label in
+ // this case.
+ //
- if (NeedExplicitReturn) {
+ if ((block != null) && block.IsDestructor) {
+ // Nothing to do; S.R.E automatically emits a leave.
+ } else if (HasReturnLabel || (!unreachable && !InIterator)) {
if (ReturnType != null)
ig.Emit (OpCodes.Ldloc, TemporaryReturn ());
ig.Emit (OpCodes.Ret);
/// </summary>
public Label LoopBegin, LoopEnd;
- /// <summary>
- /// Whether we are inside a loop and break/continue are possible.
- /// </summary>
- public bool InLoop;
-
- /// <summary>
- /// This is incremented each time we enter a try/catch block and
- /// decremented if we leave it.
- /// </summary>
- public int TryCatchLevel;
-
- /// <summary>
- /// The TryCatchLevel at the begin of the current loop.
- /// </summary>
- public int LoopBeginTryCatchLevel;
-
/// <summary>
/// Default target in a switch statement. Only valid if
/// InSwitch is true
return return_value;
}
+ public void NeedReturnLabel ()
+ {
+ if (!HasReturnLabel) {
+ ReturnLabel = ig.DefineLabel ();
+ HasReturnLabel = true;
+ }
+ }
+
//
// Creates a field `name' with the type `t' on the proxy class
//
//
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");
- }
+ if (InIterator){
+ ig.Emit (OpCodes.Ldarg_0);
+ if (!IsStatic){
+ FieldBuilder this_field = IteratorHandler.Current.this_field;
+ if (TypeManager.IsValueType (this_field.FieldType))
+ ig.Emit (OpCodes.Ldflda, this_field);
+ else
+ ig.Emit (OpCodes.Ldfld, this_field);
+ }
+ } else
+ ig.Emit (OpCodes.Ldarg_0);
}
public Expression GetThis (Location loc)
return my_this;
}
}
+
+
+ public abstract class CommonAssemblyModulClass: Attributable {
+ static string[] attribute_targets = new string [] { "assembly", "module" };
+
+ protected CommonAssemblyModulClass ():
+ base (null)
+ {
+ }
+
+ public void AddAttributes (ArrayList attrs)
+ {
+ if (OptAttributes == null) {
+ OptAttributes = new Attributes (attrs);
+ return;
+ }
+ OptAttributes.AddAttributes (attrs);
+ OptAttributes.CheckTargets (ValidAttributeTargets);
+ }
+
+ public virtual void Emit (TypeContainer tc)
+ {
+ if (OptAttributes == null)
+ return;
+
+ EmitContext ec = new EmitContext (tc, Mono.CSharp.Location.Null, null, null, 0, false);
+ OptAttributes.Emit (ec, this);
+ }
+
+ protected Attribute GetClsCompliantAttribute ()
+ {
+ if (OptAttributes == null)
+ return null;
+
+ EmitContext temp_ec = new EmitContext (new TypeContainer (), Mono.CSharp.Location.Null, null, null, 0, false);
+ Attribute a = OptAttributes.Search (TypeManager.cls_compliant_attribute_type, temp_ec);
+ if (a != null) {
+ a.Resolve (temp_ec);
+ }
+ return a;
+ }
+
+ protected override string[] ValidAttributeTargets {
+ get {
+ return attribute_targets;
+ }
+ }
+
+ }
+
+ public class AssemblyClass: CommonAssemblyModulClass {
+ // TODO: make it private and move all builder based methods here
+ public AssemblyBuilder Builder;
+ bool is_cls_compliant;
+
+ public AssemblyClass (): base ()
+ {
+ is_cls_compliant = false;
+ }
+
+ public bool IsClsCompliant {
+ get {
+ return is_cls_compliant;
+ }
+ }
+
+ public override AttributeTargets AttributeTargets {
+ get {
+ return AttributeTargets.Assembly;
+ }
+ }
+
+ public override bool IsClsCompliaceRequired(DeclSpace ds)
+ {
+ return is_cls_compliant;
+ }
+
+ public void ResolveClsCompliance ()
+ {
+ Attribute a = GetClsCompliantAttribute ();
+ if (a == null)
+ return;
+
+ is_cls_compliant = a.GetClsCompliantAttributeValue (null);
+ }
+
+ public AssemblyName GetAssemblyName (string name, string output)
+ {
+ if (OptAttributes != null) {
+ foreach (Attribute a in OptAttributes.Attrs) {
+ if (a.Target != "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");
+ }
+ else {
+ string value = a.GetString ();
+ if (value != String.Empty)
+ RootContext.StrongNameKeyFile = value;
+ }
+ break;
+ case "AssemblyKeyName":
+ case "AssemblyKeyNameAttribute":
+ case "System.Reflection.AssemblyKeyNameAttribute":
+ if (RootContext.StrongNameKeyContainer != null) {
+ Report.Warning (1616, "Compiler option -keycontainer overrides " +
+ "AssemblyKeyNameAttribute");
+ }
+ else {
+ string value = a.GetString ();
+ if (value != String.Empty)
+ 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
+ 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);
+ }
+ }
+ 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 strongname the assembly. " +
+ "ECMA key can only be used to delay-sign assemblies");
+ }
+ else {
+ Report.Error (1548, "Could not strongname the assembly. File `" +
+ RootContext.StrongNameKeyFile +
+ "' doesn't have a private key.");
+ }
+ Environment.Exit (1);
+ }
+ }
+ }
+ }
+ else {
+ Report.Error (1548, "Could not strongname the assembly. File `" +
+ RootContext.StrongNameKeyFile + "' not found.");
+ Environment.Exit (1);
+ }
+ return an;
+ }
+
+ public override void ApplyAttributeBuilder (Attribute a, CustomAttributeBuilder customBuilder)
+ {
+ Builder.SetCustomAttribute (customBuilder);
+ }
+ }
+
+ public class ModuleClass: CommonAssemblyModulClass {
+ // TODO: make it private and move all builder based methods here
+ public ModuleBuilder Builder;
+ bool m_module_is_unsafe;
+
+ public ModuleClass (bool is_unsafe)
+ {
+ m_module_is_unsafe = is_unsafe;
+ }
+
+ public override AttributeTargets AttributeTargets {
+ get {
+ return AttributeTargets.Module;
+ }
+ }
+
+ public override bool IsClsCompliaceRequired(DeclSpace ds)
+ {
+ return CodeGen.Assembly.IsClsCompliant;
+ }
+
+ public override void Emit (TypeContainer tc)
+ {
+ base.Emit (tc);
+
+ if (!m_module_is_unsafe)
+ return;
+
+ if (TypeManager.unverifiable_code_ctor == null) {
+ Console.WriteLine ("Internal error ! Cannot set unverifiable code attribute.");
+ return;
+ }
+
+ ApplyAttributeBuilder (null, new CustomAttributeBuilder (TypeManager.unverifiable_code_ctor, new object [0]));
+ }
+
+ public override void ApplyAttributeBuilder (Attribute a, CustomAttributeBuilder customBuilder)
+ {
+ if (a != null && a.Type == TypeManager.cls_compliant_attribute_type) {
+ Report.Warning_T (3012, a.Location);
+ return;
+ }
+
+ Builder.SetCustomAttribute (customBuilder);
+ }
+ }
}