//
// 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.Generic;
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 (CompilerContext ctx, 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 ModuleCompiled (ctx, 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, CompilerContext ctx)
{
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) {
ctx.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) {
ctx.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)) {
ctx.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)) {
ctx.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) {
ctx.Report.Error (40, "Unexpected debug information initialization error `{0}'",
e.Message);
return false;
}
return true;
}
static public void Save (string name, bool saveDebugInfo, Report Report)
{
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;
}
try {
Assembly.Builder.Save (Basename (name), pekind, machine);
}
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 Emit Context is created for each body of code (from methods,
/// properties bodies, indexer bodies or constructor bodies)
///
public class EmitContext : BuilderContext
{
// TODO: Has to be private
public ILGenerator ig;
///
/// The value that is allowed to be returned or NULL if there is no
/// return type.
///
TypeSpec 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)
///
Dictionary temporary_storage;
///
/// 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;
///
/// 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;
///
/// Whether we are inside an anonymous method.
///
public AnonymousExpression CurrentAnonymousMethod;
public readonly IMemberContext MemberContext;
public EmitContext (IMemberContext rc, ILGenerator ig, TypeSpec return_type)
{
this.MemberContext = rc;
this.ig = ig;
this.return_type = return_type;
}
#region Properties
public TypeSpec CurrentType {
get { return MemberContext.CurrentType; }
}
public TypeParameter[] CurrentTypeParameters {
get { return MemberContext.CurrentTypeParameters; }
}
public MemberCore CurrentTypeDefinition {
get { return MemberContext.CurrentMemberDefinition; }
}
public bool IsStatic {
get { return MemberContext.IsStatic; }
}
bool IsAnonymousStoreyMutateRequired {
get {
return CurrentAnonymousMethod != null &&
CurrentAnonymousMethod.Storey != null &&
CurrentAnonymousMethod.Storey.Mutator != null;
}
}
// Has to be used for emitter errors only
public Report Report {
get { return MemberContext.Compiler.Report; }
}
public TypeSpec ReturnType {
get {
return return_type;
}
}
#endregion
///
/// 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 || HasSet (Options.OmitDebugInfo) || loc.IsNull)
return;
SymbolWriter.MarkSequencePoint (ig, loc);
}
public void DefineLocalVariable (string name, LocalBuilder builder)
{
SymbolWriter.DefineLocalVariable (name, builder);
}
public void BeginCatchBlock (TypeSpec type)
{
ig.BeginCatchBlock (type.GetMetaInfo ());
}
public void BeginExceptionBlock ()
{
ig.BeginExceptionBlock ();
}
public void BeginFinallyBlock ()
{
ig.BeginFinallyBlock ();
}
public void BeginScope ()
{
ig.BeginScope();
SymbolWriter.OpenScope(ig);
}
public void EndExceptionBlock ()
{
ig.EndExceptionBlock ();
}
public void EndScope ()
{
ig.EndScope();
SymbolWriter.CloseScope(ig);
}
public LocalBuilder DeclareLocal (TypeSpec type, bool pinned)
{
if (IsAnonymousStoreyMutateRequired)
type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
return ig.DeclareLocal (type.GetMetaInfo (), pinned);
}
public Label DefineLabel ()
{
return ig.DefineLabel ();
}
public void MarkLabel (Label label)
{
ig.MarkLabel (label);
}
public void Emit (OpCode opcode)
{
ig.Emit (opcode);
}
public void Emit (OpCode opcode, LocalBuilder local)
{
ig.Emit (opcode, local);
}
public void Emit (OpCode opcode, string arg)
{
ig.Emit (opcode, arg);
}
public void Emit (OpCode opcode, double arg)
{
ig.Emit (opcode, arg);
}
public void Emit (OpCode opcode, float arg)
{
ig.Emit (opcode, arg);
}
public void Emit (OpCode opcode, int arg)
{
ig.Emit (opcode, arg);
}
public void Emit (OpCode opcode, byte arg)
{
ig.Emit (opcode, arg);
}
public void Emit (OpCode opcode, Label label)
{
ig.Emit (opcode, label);
}
public void Emit (OpCode opcode, Label[] labels)
{
ig.Emit (opcode, labels);
}
public void Emit (OpCode opcode, TypeSpec type)
{
if (IsAnonymousStoreyMutateRequired)
type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
ig.Emit (opcode, type.GetMetaInfo ());
}
public void Emit (OpCode opcode, FieldSpec field)
{
if (IsAnonymousStoreyMutateRequired)
field = field.Mutate (CurrentAnonymousMethod.Storey.Mutator);
ig.Emit (opcode, field.GetMetaInfo ());
}
public void Emit (OpCode opcode, MethodSpec method)
{
if (IsAnonymousStoreyMutateRequired)
method = method.Mutate (CurrentAnonymousMethod.Storey.Mutator);
if (method.IsConstructor)
ig.Emit (opcode, (ConstructorInfo) method.GetMetaInfo ());
else
ig.Emit (opcode, (MethodInfo) method.GetMetaInfo ());
}
// TODO: REMOVE breaks mutator
public void Emit (OpCode opcode, MethodInfo method)
{
ig.Emit (opcode, method);
}
// TODO: REMOVE breaks mutator
public void Emit (OpCode opcode, FieldBuilder field)
{
ig.Emit (opcode, field);
}
public void Emit (OpCode opcode, MethodSpec method, Type[] vargs)
{
// TODO MemberCache: This should mutate too
ig.EmitCall (opcode, (MethodInfo) method.GetMetaInfo (), vargs);
}
public void EmitArrayNew (ArrayContainer ac)
{
if (ac.Rank == 1) {
Emit (OpCodes.Newarr, ac.Element);
} else {
if (IsAnonymousStoreyMutateRequired)
ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
ig.Emit (OpCodes.Newobj, ac.GetConstructor ());
}
}
//
// Emits the right opcode to load from an array
//
public void EmitArrayLoad (ArrayContainer ac)
{
if (ac.Rank > 1) {
if (IsAnonymousStoreyMutateRequired)
ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
ig.Emit (OpCodes.Call, ac.GetGetMethod ());
return;
}
var type = ac.Element;
if (TypeManager.IsEnumType (type))
type = EnumSpec.GetUnderlyingType (type);
if (type == TypeManager.byte_type || type == TypeManager.bool_type)
Emit (OpCodes.Ldelem_U1);
else if (type == TypeManager.sbyte_type)
Emit (OpCodes.Ldelem_I1);
else if (type == TypeManager.short_type)
Emit (OpCodes.Ldelem_I2);
else if (type == TypeManager.ushort_type || type == TypeManager.char_type)
Emit (OpCodes.Ldelem_U2);
else if (type == TypeManager.int32_type)
Emit (OpCodes.Ldelem_I4);
else if (type == TypeManager.uint32_type)
Emit (OpCodes.Ldelem_U4);
else if (type == TypeManager.uint64_type)
Emit (OpCodes.Ldelem_I8);
else if (type == TypeManager.int64_type)
Emit (OpCodes.Ldelem_I8);
else if (type == TypeManager.float_type)
Emit (OpCodes.Ldelem_R4);
else if (type == TypeManager.double_type)
Emit (OpCodes.Ldelem_R8);
else if (type == TypeManager.intptr_type)
Emit (OpCodes.Ldelem_I);
else if (TypeManager.IsStruct (type)) {
Emit (OpCodes.Ldelema, type);
Emit (OpCodes.Ldobj, type);
} else if (type.IsGenericParameter) {
Emit (OpCodes.Ldelem, type);
} else if (type.IsPointer)
Emit (OpCodes.Ldelem_I);
else
Emit (OpCodes.Ldelem_Ref);
}
//
// Emits the right opcode to store to an array
//
public void EmitArrayStore (ArrayContainer ac)
{
if (ac.Rank > 1) {
if (IsAnonymousStoreyMutateRequired)
ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
ig.Emit (OpCodes.Call, ac.GetSetMethod ());
return;
}
var type = ac.Element;
if (type.IsEnum)
type = EnumSpec.GetUnderlyingType (type);
if (type == TypeManager.byte_type || type == TypeManager.sbyte_type || type == TypeManager.bool_type)
Emit (OpCodes.Stelem_I1);
else if (type == TypeManager.short_type || type == TypeManager.ushort_type || type == TypeManager.char_type)
Emit (OpCodes.Stelem_I2);
else if (type == TypeManager.int32_type || type == TypeManager.uint32_type)
Emit (OpCodes.Stelem_I4);
else if (type == TypeManager.int64_type || type == TypeManager.uint64_type)
Emit (OpCodes.Stelem_I8);
else if (type == TypeManager.float_type)
Emit (OpCodes.Stelem_R4);
else if (type == TypeManager.double_type)
Emit (OpCodes.Stelem_R8);
else if (type == TypeManager.intptr_type)
Emit (OpCodes.Stobj, type);
else if (TypeManager.IsStruct (type))
Emit (OpCodes.Stobj, type);
else if (type.IsGenericParameter)
Emit (OpCodes.Stelem, type);
else if (type.IsPointer)
Emit (OpCodes.Stelem_I);
else
Emit (OpCodes.Stelem_Ref);
}
public void EmitInt (int i)
{
switch (i) {
case -1:
ig.Emit (OpCodes.Ldc_I4_M1);
break;
case 0:
ig.Emit (OpCodes.Ldc_I4_0);
break;
case 1:
ig.Emit (OpCodes.Ldc_I4_1);
break;
case 2:
ig.Emit (OpCodes.Ldc_I4_2);
break;
case 3:
ig.Emit (OpCodes.Ldc_I4_3);
break;
case 4:
ig.Emit (OpCodes.Ldc_I4_4);
break;
case 5:
ig.Emit (OpCodes.Ldc_I4_5);
break;
case 6:
ig.Emit (OpCodes.Ldc_I4_6);
break;
case 7:
ig.Emit (OpCodes.Ldc_I4_7);
break;
case 8:
ig.Emit (OpCodes.Ldc_I4_8);
break;
default:
if (i >= -128 && i <= 127) {
ig.Emit (OpCodes.Ldc_I4_S, (sbyte) i);
} else
ig.Emit (OpCodes.Ldc_I4, i);
break;
}
}
public void EmitLong (long l)
{
if (l >= int.MinValue && l <= int.MaxValue) {
EmitInt (unchecked ((int) l));
ig.Emit (OpCodes.Conv_I8);
return;
}
if (l >= 0 && l <= uint.MaxValue) {
EmitInt (unchecked ((int) l));
ig.Emit (OpCodes.Conv_U8);
return;
}
ig.Emit (OpCodes.Ldc_I8, l);
}
//
// Load the object from the pointer.
//
public void EmitLoadFromPtr (TypeSpec t)
{
if (t == TypeManager.int32_type)
ig.Emit (OpCodes.Ldind_I4);
else if (t == TypeManager.uint32_type)
ig.Emit (OpCodes.Ldind_U4);
else if (t == TypeManager.short_type)
ig.Emit (OpCodes.Ldind_I2);
else if (t == TypeManager.ushort_type)
ig.Emit (OpCodes.Ldind_U2);
else if (t == TypeManager.char_type)
ig.Emit (OpCodes.Ldind_U2);
else if (t == TypeManager.byte_type)
ig.Emit (OpCodes.Ldind_U1);
else if (t == TypeManager.sbyte_type)
ig.Emit (OpCodes.Ldind_I1);
else if (t == TypeManager.uint64_type)
ig.Emit (OpCodes.Ldind_I8);
else if (t == TypeManager.int64_type)
ig.Emit (OpCodes.Ldind_I8);
else if (t == TypeManager.float_type)
ig.Emit (OpCodes.Ldind_R4);
else if (t == TypeManager.double_type)
ig.Emit (OpCodes.Ldind_R8);
else if (t == TypeManager.bool_type)
ig.Emit (OpCodes.Ldind_I1);
else if (t == TypeManager.intptr_type)
ig.Emit (OpCodes.Ldind_I);
else if (t.IsEnum) {
if (t == TypeManager.enum_type)
ig.Emit (OpCodes.Ldind_Ref);
else
EmitLoadFromPtr (EnumSpec.GetUnderlyingType (t));
} else if (TypeManager.IsStruct (t) || TypeManager.IsGenericParameter (t))
Emit (OpCodes.Ldobj, t);
else if (t.IsPointer)
ig.Emit (OpCodes.Ldind_I);
else
ig.Emit (OpCodes.Ldind_Ref);
}
//
// The stack contains the pointer and the value of type `type'
//
public void EmitStoreFromPtr (TypeSpec type)
{
if (type.IsEnum)
type = EnumSpec.GetUnderlyingType (type);
if (type == TypeManager.int32_type || type == TypeManager.uint32_type)
ig.Emit (OpCodes.Stind_I4);
else if (type == TypeManager.int64_type || type == TypeManager.uint64_type)
ig.Emit (OpCodes.Stind_I8);
else if (type == TypeManager.char_type || type == TypeManager.short_type ||
type == TypeManager.ushort_type)
ig.Emit (OpCodes.Stind_I2);
else if (type == TypeManager.float_type)
ig.Emit (OpCodes.Stind_R4);
else if (type == TypeManager.double_type)
ig.Emit (OpCodes.Stind_R8);
else if (type == TypeManager.byte_type || type == TypeManager.sbyte_type ||
type == TypeManager.bool_type)
ig.Emit (OpCodes.Stind_I1);
else if (type == TypeManager.intptr_type)
ig.Emit (OpCodes.Stind_I);
else if (TypeManager.IsStruct (type) || TypeManager.IsGenericParameter (type))
ig.Emit (OpCodes.Stobj, type.GetMetaInfo ());
else
ig.Emit (OpCodes.Stind_Ref);
}
///
/// Returns a temporary storage for a variable of type t as
/// a local variable in the current body.
///
public LocalBuilder GetTemporaryLocal (TypeSpec t)
{
if (temporary_storage != null) {
object o;
if (temporary_storage.TryGetValue (t, out o)) {
if (o is Stack) {
var s = (Stack) o;
o = s.Count == 0 ? null : s.Pop ();
} else {
temporary_storage.Remove (t);
}
}
if (o != null)
return (LocalBuilder) o;
}
return DeclareLocal (t, false);
}
public void FreeTemporaryLocal (LocalBuilder b, TypeSpec t)
{
if (temporary_storage == null) {
temporary_storage = new Dictionary (ReferenceEquality.Default);
temporary_storage.Add (t, b);
return;
}
object o;
if (!temporary_storage.TryGetValue (t, out o)) {
temporary_storage.Add (t, b);
return;
}
var s = o as Stack;
if (s == null) {
s = new Stack ();
s.Push ((LocalBuilder)o);
temporary_storage [t] = s;
}
s.Push (b);
}
///
/// 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 = DeclareLocal (return_type, false);
if (!HasReturnLabel){
ReturnLabel = DefineLabel ();
HasReturnLabel = true;
}
}
return return_value;
}
}
public abstract class CommonAssemblyModulClass : Attributable, IMemberContext
{
public void AddAttributes (List 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 IMemberContext Members
public CompilerContext Compiler {
get { return RootContext.ToplevelTypes.Compiler; }
}
public TypeSpec CurrentType {
get { return null; }
}
public TypeParameter[] CurrentTypeParameters {
get { return null; }
}
public MemberCore CurrentMemberDefinition {
get { return RootContext.ToplevelTypes; }
}
public string GetSignatureForError ()
{
return "";
}
public bool HasUnresolvedConstraints {
get { return false; }
}
public bool IsObsolete {
get { return false; }
}
public bool IsUnsafe {
get { return false; }
}
public bool IsStatic {
get { return false; }
}
public ExtensionMethodGroupExpr LookupExtensionMethod (TypeSpec extensionType, string name, int arity, Location loc)
{
throw new NotImplementedException ();
}
public FullNamedExpression LookupNamespaceOrType (string name, int arity, Location loc, bool ignore_cs0104)
{
return RootContext.ToplevelTypes.LookupNamespaceOrType (name, arity, 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;
Dictionary declarative_security;
bool has_extension_method;
public AssemblyName Name;
MethodInfo add_type_forwarder;
Dictionary emitted_forwarders;
// Module is here just because of error messages
static string[] attribute_targets = new string [] { "assembly", "module" };
public AssemblyClass ()
{
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;
}
Report Report {
get { return Compiler.Report; }
}
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 ("SkipVerification", loc, 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 Dictionary ();
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) {
var val = a.GetPropertyValue ("WrapNonExceptionThrows") as BoolConstant;
if (val != null)
wrap_non_exception_throws = val.Value;
}
}
// 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 {
aname = new AssemblyName (assembly_name);
} 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, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa)
{
if (a.IsValidSecurityAttribute ()) {
if (declarative_security == null)
declarative_security = new Dictionary ();
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) {
TypeSpec t = a.GetArgumentType ();
if (t == null || TypeManager.HasElementType (t)) {
Report.Error (735, a.Location, "Invalid type specified as an argument for TypeForwardedTo attribute");
return;
}
if (emitted_forwarders == null) {
emitted_forwarders = new Dictionary ();
} else if (emitted_forwarders.ContainsKey (t.MemberDefinition)) {
Report.SymbolRelatedToPreviousError(emitted_forwarders[t.MemberDefinition].Location, null);
Report.Error(739, a.Location, "A duplicate type forward of type `{0}'",
TypeManager.CSharpName(t));
return;
}
emitted_forwarders.Add(t.MemberDefinition, a);
if (t.Assembly == Builder) {
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.IsNested) {
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.GetMetaInfo () });
return;
}
if (a.Type == pa.Extension) {
a.Error_MisusedExtensionAttribute ();
return;
}
Builder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), cdata);
}
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))) {
var ci = TypeManager.GetPredefinedConstructor (pa.Type, Location.Null, TypeSpec.EmptyTypes);
PropertyInfo [] pis = new PropertyInfo [1];
pis [0] = TypeManager.GetPredefinedProperty (pa.Type,
"WrapNonExceptionThrows", Location.Null, TypeManager.bool_type).MetaInfo;
object [] pargs = new object [1];
pargs [0] = true;
Builder.SetCustomAttribute (new CustomAttributeBuilder ((ConstructorInfo) ci.GetMetaInfo (), 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) {
var 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);
}
var args = new PermissionSet [3];
declarative_security.TryGetValue (SecurityAction.RequestMinimum, out args [0]);
declarative_security.TryGetValue (SecurityAction.RequestOptional, out args [1]);
declarative_security.TryGetValue (SecurityAction.RequestRefuse, out args [2]);
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;
}
}
}
}