// // expression.cs: Expression representation for the IL tree. // // Author: // Miguel de Icaza (miguel@ximian.com) // // (C) 2001 Ximian, Inc. // // #define USE_OLD namespace Mono.CSharp { using System; using System.Collections; using System.Diagnostics; using System.Reflection; using System.Reflection.Emit; using System.Text; /// /// This is just a helper class, it is generated by Unary, UnaryMutator /// when an overloaded method has been found. It just emits the code for a /// static call. /// public class StaticCallExpr : ExpressionStatement { ArrayList args; MethodInfo mi; StaticCallExpr (MethodInfo m, ArrayList a) { mi = m; args = a; type = m.ReturnType; eclass = ExprClass.Value; } public override Expression DoResolve (EmitContext ec) { // // We are born fully resolved // return this; } public override void Emit (EmitContext ec) { if (args != null) Invocation.EmitArguments (ec, mi, args); ec.ig.Emit (OpCodes.Call, mi); return; } static public Expression MakeSimpleCall (EmitContext ec, MethodGroupExpr mg, Expression e, Location loc) { ArrayList args; MethodBase method; args = new ArrayList (1); args.Add (new Argument (e, Argument.AType.Expression)); method = Invocation.OverloadResolve (ec, (MethodGroupExpr) mg, args, loc); if (method == null) return null; return new StaticCallExpr ((MethodInfo) method, args); } public override void EmitStatement (EmitContext ec) { Emit (ec); if (type != TypeManager.void_type) ec.ig.Emit (OpCodes.Pop); } } /// /// Unary expressions. /// /// /// /// Unary implements unary expressions. It derives from /// ExpressionStatement becuase the pre/post increment/decrement /// operators can be used in a statement context. /// public class Unary : Expression { public enum Operator : byte { UnaryPlus, UnaryNegation, LogicalNot, OnesComplement, Indirection, AddressOf, TOP } Operator oper; Expression expr; Location loc; public Unary (Operator op, Expression expr, Location loc) { this.oper = op; this.expr = expr; this.loc = loc; } public Expression Expr { get { return expr; } set { expr = value; } } public Operator Oper { get { return oper; } set { oper = value; } } /// /// Returns a stringified representation of the Operator /// string OperName () { switch (oper){ case Operator.UnaryPlus: return "+"; case Operator.UnaryNegation: return "-"; case Operator.LogicalNot: return "!"; case Operator.OnesComplement: return "~"; case Operator.AddressOf: return "&"; case Operator.Indirection: return "*"; } return oper.ToString (); } static string [] oper_names; static Unary () { oper_names = new string [(int)Operator.TOP]; oper_names [(int) Operator.UnaryPlus] = "op_UnaryPlus"; oper_names [(int) Operator.UnaryNegation] = "op_UnaryNegation"; oper_names [(int) Operator.LogicalNot] = "op_LogicalNot"; oper_names [(int) Operator.OnesComplement] = "op_OnesComplement"; oper_names [(int) Operator.Indirection] = "op_Indirection"; oper_names [(int) Operator.AddressOf] = "op_AddressOf"; } void error23 (Type t) { Report.Error ( 23, loc, "Operator " + OperName () + " cannot be applied to operand of type `" + TypeManager.CSharpName (t) + "'"); } /// /// The result has been already resolved: /// /// FIXME: a minus constant -128 sbyte cant be turned into a /// constant byte. /// static Expression TryReduceNegative (Expression expr) { Expression e = null; if (expr is IntConstant) e = new IntConstant (-((IntConstant) expr).Value); else if (expr is UIntConstant) e = new LongConstant (-((UIntConstant) expr).Value); else if (expr is LongConstant) e = new LongConstant (-((LongConstant) expr).Value); else if (expr is FloatConstant) e = new FloatConstant (-((FloatConstant) expr).Value); else if (expr is DoubleConstant) e = new DoubleConstant (-((DoubleConstant) expr).Value); else if (expr is DecimalConstant) e = new DecimalConstant (-((DecimalConstant) expr).Value); else if (expr is ShortConstant) e = new IntConstant (-((ShortConstant) expr).Value); else if (expr is UShortConstant) e = new IntConstant (-((UShortConstant) expr).Value); return e; } Expression Reduce (EmitContext ec, Expression e) { Type expr_type = e.Type; switch (oper){ case Operator.UnaryPlus: return e; case Operator.UnaryNegation: return TryReduceNegative (e); case Operator.LogicalNot: if (expr_type != TypeManager.bool_type) { error23 (expr_type); return null; } BoolConstant b = (BoolConstant) e; return new BoolConstant (!(b.Value)); case Operator.OnesComplement: if (!((expr_type == TypeManager.int32_type) || (expr_type == TypeManager.uint32_type) || (expr_type == TypeManager.int64_type) || (expr_type == TypeManager.uint64_type) || (expr_type.IsSubclassOf (TypeManager.enum_type)))){ error23 (expr_type); return null; } if (e is EnumConstant){ EnumConstant enum_constant = (EnumConstant) e; Expression reduced = Reduce (ec, enum_constant.Child); return new EnumConstant ((Constant) reduced, enum_constant.Type); } if (expr_type == TypeManager.int32_type) return new IntConstant (~ ((IntConstant) e).Value); if (expr_type == TypeManager.uint32_type) return new UIntConstant (~ ((UIntConstant) e).Value); if (expr_type == TypeManager.int64_type) return new LongConstant (~ ((LongConstant) e).Value); if (expr_type == TypeManager.uint64_type) return new ULongConstant (~ ((ULongConstant) e).Value); throw new Exception ( "FIXME: Implement constant OnesComplement of:" + expr_type); } throw new Exception ("Can not constant fold"); } Expression ResolveOperator (EmitContext ec) { Type expr_type = expr.Type; // // Step 1: Perform Operator Overload location // Expression mg; string op_name; op_name = oper_names [(int) oper]; mg = MemberLookup (ec, expr_type, op_name, loc); if (mg == null && expr_type.BaseType != null) mg = MemberLookup (ec, expr_type.BaseType, op_name, loc); if (mg != null) { Expression e = StaticCallExpr.MakeSimpleCall ( ec, (MethodGroupExpr) mg, expr, loc); if (e == null){ error23 (expr_type); return null; } return e; } // Only perform numeric promotions on: // +, - if (expr_type == null) return null; // // Step 2: Default operations on CLI native types. // if (expr is Constant) return Reduce (ec, expr); if (oper == Operator.LogicalNot){ if (expr_type != TypeManager.bool_type) { error23 (expr.Type); return null; } type = TypeManager.bool_type; return this; } if (oper == Operator.OnesComplement) { if (!((expr_type == TypeManager.int32_type) || (expr_type == TypeManager.uint32_type) || (expr_type == TypeManager.int64_type) || (expr_type == TypeManager.uint64_type) || (expr_type.IsSubclassOf (TypeManager.enum_type)))){ error23 (expr.Type); return null; } type = expr_type; return this; } if (oper == Operator.UnaryPlus) { // // A plus in front of something is just a no-op, so return the child. // return expr; } // // Deals with -literals // int operator- (int x) // long operator- (long x) // float operator- (float f) // double operator- (double d) // decimal operator- (decimal d) // if (oper == Operator.UnaryNegation){ Expression e = null; // // perform numeric promotions to int, // long, double. // // // The following is inneficient, because we call // ConvertImplicit too many times. // // It is also not clear if we should convert to Float // or Double initially. // if (expr_type == TypeManager.uint32_type){ // // FIXME: handle exception to this rule that // permits the int value -2147483648 (-2^31) to // bt wrote as a decimal interger literal // type = TypeManager.int64_type; expr = ConvertImplicit (ec, expr, type, loc); return this; } if (expr_type == TypeManager.uint64_type){ // // FIXME: Handle exception of `long value' // -92233720368547758087 (-2^63) to be wrote as // decimal integer literal. // error23 (expr_type); return null; } if (expr_type == TypeManager.float_type){ type = expr_type; return this; } e = ConvertImplicit (ec, expr, TypeManager.int32_type, loc); if (e != null){ expr = e; type = e.Type; return this; } e = ConvertImplicit (ec, expr, TypeManager.int64_type, loc); if (e != null){ expr = e; type = e.Type; return this; } e = ConvertImplicit (ec, expr, TypeManager.double_type, loc); if (e != null){ expr = e; type = e.Type; return this; } error23 (expr_type); return null; } if (oper == Operator.AddressOf){ if (expr.eclass != ExprClass.Variable){ Error (211, loc, "Cannot take the address of non-variables"); return null; } if (!ec.InUnsafe) { Error (214, loc, "Pointers may only be used in an unsafe context"); return null; } type = Type.GetType (expr.Type.ToString () + "*"); return this; } Error (187, loc, "No such operator '" + OperName () + "' defined for type '" + TypeManager.CSharpName (expr_type) + "'"); return null; } public override Expression DoResolve (EmitContext ec) { expr = expr.Resolve (ec); if (expr == null) return null; eclass = ExprClass.Value; return ResolveOperator (ec); } public override void Emit (EmitContext ec) { ILGenerator ig = ec.ig; Type expr_type = expr.Type; switch (oper) { case Operator.UnaryPlus: throw new Exception ("This should be caught by Resolve"); case Operator.UnaryNegation: expr.Emit (ec); ig.Emit (OpCodes.Neg); break; case Operator.LogicalNot: expr.Emit (ec); ig.Emit (OpCodes.Ldc_I4_0); ig.Emit (OpCodes.Ceq); break; case Operator.OnesComplement: expr.Emit (ec); ig.Emit (OpCodes.Not); break; case Operator.AddressOf: ((IMemoryLocation)expr).AddressOf (ec); break; case Operator.Indirection: throw new Exception ("Not implemented yet"); default: throw new Exception ("This should not happen: Operator = " + oper.ToString ()); } } /// /// This will emit the child expression for `ec' avoiding the logical /// not. The parent will take care of changing brfalse/brtrue /// public void EmitLogicalNot (EmitContext ec) { if (oper != Operator.LogicalNot) throw new Exception ("EmitLogicalNot can only be called with !expr"); expr.Emit (ec); } } /// /// Unary Mutator expressions (pre and post ++ and --) /// /// /// /// UnaryMutator implements ++ and -- expressions. It derives from /// ExpressionStatement becuase the pre/post increment/decrement /// operators can be used in a statement context. /// /// FIXME: Idea, we could split this up in two classes, one simpler /// for the common case, and one with the extra fields for more complex /// classes (indexers require temporary access; overloaded require method) /// /// Maybe we should have classes PreIncrement, PostIncrement, PreDecrement, /// PostDecrement, that way we could save the `Mode' byte as well. /// public class UnaryMutator : ExpressionStatement { public enum Mode : byte { PreIncrement, PreDecrement, PostIncrement, PostDecrement } Mode mode; Location loc; Expression expr; LocalTemporary temp_storage; // // This is expensive for the simplest case. // Expression method; public UnaryMutator (Mode m, Expression e, Location l) { mode = m; loc = l; expr = e; } string OperName () { return (mode == Mode.PreIncrement || mode == Mode.PostIncrement) ? "++" : "--"; } void error23 (Type t) { Report.Error ( 23, loc, "Operator " + OperName () + " cannot be applied to operand of type `" + TypeManager.CSharpName (t) + "'"); } /// /// Returns whether an object of type `t' can be incremented /// or decremented with add/sub (ie, basically whether we can /// use pre-post incr-decr operations on it, but it is not a /// System.Decimal, which we require operator overloading to catch) /// static bool IsIncrementableNumber (Type t) { return (t == TypeManager.sbyte_type) || (t == TypeManager.byte_type) || (t == TypeManager.short_type) || (t == TypeManager.ushort_type) || (t == TypeManager.int32_type) || (t == TypeManager.uint32_type) || (t == TypeManager.int64_type) || (t == TypeManager.uint64_type) || (t == TypeManager.char_type) || (t.IsSubclassOf (TypeManager.enum_type)) || (t == TypeManager.float_type) || (t == TypeManager.double_type); } Expression ResolveOperator (EmitContext ec) { Type expr_type = expr.Type; // // Step 1: Perform Operator Overload location // Expression mg; string op_name; if (mode == Mode.PreIncrement || mode == Mode.PostIncrement) op_name = "op_Increment"; else op_name = "op_Decrement"; mg = MemberLookup (ec, expr_type, op_name, loc); if (mg == null && expr_type.BaseType != null) mg = MemberLookup (ec, expr_type.BaseType, op_name, loc); if (mg != null) { method = StaticCallExpr.MakeSimpleCall ( ec, (MethodGroupExpr) mg, expr, loc); type = method.Type; return this; } // // The operand of the prefix/postfix increment decrement operators // should be an expression that is classified as a variable, // a property access or an indexer access // type = expr_type; if (expr.eclass == ExprClass.Variable){ if (IsIncrementableNumber (expr_type) || expr_type == TypeManager.decimal_type){ return this; } } else if (expr.eclass == ExprClass.IndexerAccess){ IndexerAccess ia = (IndexerAccess) expr; temp_storage = new LocalTemporary (ec, expr.Type); expr = ia.ResolveLValue (ec, temp_storage); if (expr == null) return null; return this; } else if (expr.eclass == ExprClass.PropertyAccess){ PropertyExpr pe = (PropertyExpr) expr; if (pe.VerifyAssignable ()) return this; return null; } else { report118 (loc, expr, "variable, indexer or property access"); return null; } Error (187, loc, "No such operator '" + OperName () + "' defined for type '" + TypeManager.CSharpName (expr_type) + "'"); return null; } public override Expression DoResolve (EmitContext ec) { expr = expr.Resolve (ec); if (expr == null) return null; eclass = ExprClass.Value; return ResolveOperator (ec); } // // FIXME: We need some way of avoiding the use of temp_storage // for some types of storage (parameters, local variables, // static fields) and single-dimension array access. // void EmitCode (EmitContext ec, bool is_expr) { ILGenerator ig = ec.ig; IAssignMethod ia = (IAssignMethod) expr; Type expr_type = expr.Type; if (temp_storage == null) temp_storage = new LocalTemporary (ec, expr_type); switch (mode){ case Mode.PreIncrement: case Mode.PreDecrement: if (method == null){ expr.Emit (ec); if (expr_type == TypeManager.uint64_type || expr_type == TypeManager.int64_type) ig.Emit (OpCodes.Ldc_I8, 1L); else if (expr_type == TypeManager.double_type) ig.Emit (OpCodes.Ldc_R8, 1.0); else if (expr_type == TypeManager.float_type) ig.Emit (OpCodes.Ldc_R4, 1.0F); else ig.Emit (OpCodes.Ldc_I4_1); if (mode == Mode.PreDecrement) ig.Emit (OpCodes.Sub); else ig.Emit (OpCodes.Add); } else method.Emit (ec); temp_storage.Store (ec); ia.EmitAssign (ec, temp_storage); if (is_expr) temp_storage.Emit (ec); break; case Mode.PostIncrement: case Mode.PostDecrement: if (is_expr) expr.Emit (ec); if (method == null){ if (!is_expr) expr.Emit (ec); else ig.Emit (OpCodes.Dup); if (expr_type == TypeManager.uint64_type || expr_type == TypeManager.int64_type) ig.Emit (OpCodes.Ldc_I8, 1L); else if (expr_type == TypeManager.double_type) ig.Emit (OpCodes.Ldc_R8, 1.0); else if (expr_type == TypeManager.float_type) ig.Emit (OpCodes.Ldc_R4, 1.0F); else ig.Emit (OpCodes.Ldc_I4_1); if (mode == Mode.PostDecrement) ig.Emit (OpCodes.Sub); else ig.Emit (OpCodes.Add); } else { method.Emit (ec); } temp_storage.Store (ec); ia.EmitAssign (ec, temp_storage); break; } } public override void Emit (EmitContext ec) { EmitCode (ec, true); } public override void EmitStatement (EmitContext ec) { EmitCode (ec, false); } } /// /// Base class for the `Is' and `As' classes. /// /// /// /// FIXME: Split this in two, and we get to save the `Operator' Oper /// size. /// public abstract class Probe : Expression { public readonly string ProbeType; protected Expression expr; protected Type probe_type; Location loc; public Probe (Expression expr, string probe_type, Location l) { ProbeType = probe_type; loc = l; this.expr = expr; } public Expression Expr { get { return expr; } } public override Expression DoResolve (EmitContext ec) { probe_type = RootContext.LookupType (ec.TypeContainer, ProbeType, false, loc); if (probe_type == null) return null; expr = expr.Resolve (ec); return this; } } /// /// Implementation of the `is' operator. /// public class Is : Probe { public Is (Expression expr, string probe_type, Location l) : base (expr, probe_type, l) { } public override void Emit (EmitContext ec) { ILGenerator ig = ec.ig; expr.Emit (ec); ig.Emit (OpCodes.Isinst, probe_type); ig.Emit (OpCodes.Ldnull); ig.Emit (OpCodes.Cgt_Un); } public override Expression DoResolve (EmitContext ec) { Expression e = base.DoResolve (ec); if (e == null) return null; type = TypeManager.bool_type; eclass = ExprClass.Value; return this; } } /// /// Implementation of the `as' operator. /// public class As : Probe { public As (Expression expr, string probe_type, Location l) : base (expr, probe_type, l) { } public override void Emit (EmitContext ec) { ILGenerator ig = ec.ig; expr.Emit (ec); ig.Emit (OpCodes.Isinst, probe_type); } public override Expression DoResolve (EmitContext ec) { Expression e = base.DoResolve (ec); if (e == null) return null; type = probe_type; eclass = ExprClass.Value; return this; } } /// /// This represents a typecast in the source language. /// /// FIXME: Cast expressions have an unusual set of parsing /// rules, we need to figure those out. /// public class Cast : Expression { Expression target_type; Expression expr; Location loc; public Cast (Expression cast_type, Expression expr, Location loc) { this.target_type = cast_type; this.expr = expr; this.loc = loc; } public Expression TargetType { get { return target_type; } } public Expression Expr { get { return expr; } set { expr = value; } } /// /// Attempts to do a compile-time folding of a constant cast. /// Expression TryReduce (EmitContext ec, Type target_type) { if (expr is ByteConstant){ byte v = ((ByteConstant) expr).Value; if (target_type == TypeManager.sbyte_type) return new SByteConstant ((sbyte) v); if (target_type == TypeManager.short_type) return new ShortConstant ((short) v); if (target_type == TypeManager.ushort_type) return new UShortConstant ((ushort) v); if (target_type == TypeManager.int32_type) return new IntConstant ((int) v); if (target_type == TypeManager.uint32_type) return new UIntConstant ((uint) v); if (target_type == TypeManager.int64_type) return new LongConstant ((long) v); if (target_type == TypeManager.uint64_type) return new ULongConstant ((ulong) v); if (target_type == TypeManager.float_type) return new FloatConstant ((float) v); if (target_type == TypeManager.double_type) return new DoubleConstant ((double) v); } if (expr is SByteConstant){ sbyte v = ((SByteConstant) expr).Value; if (target_type == TypeManager.byte_type) return new ByteConstant ((byte) v); if (target_type == TypeManager.short_type) return new ShortConstant ((short) v); if (target_type == TypeManager.ushort_type) return new UShortConstant ((ushort) v); if (target_type == TypeManager.int32_type) return new IntConstant ((int) v); if (target_type == TypeManager.uint32_type) return new UIntConstant ((uint) v); if (target_type == TypeManager.int64_type) return new LongConstant ((long) v); if (target_type == TypeManager.uint64_type) return new ULongConstant ((ulong) v); if (target_type == TypeManager.float_type) return new FloatConstant ((float) v); if (target_type == TypeManager.double_type) return new DoubleConstant ((double) v); } if (expr is ShortConstant){ short v = ((ShortConstant) expr).Value; if (target_type == TypeManager.byte_type) return new ByteConstant ((byte) v); if (target_type == TypeManager.sbyte_type) return new SByteConstant ((sbyte) v); if (target_type == TypeManager.ushort_type) return new UShortConstant ((ushort) v); if (target_type == TypeManager.int32_type) return new IntConstant ((int) v); if (target_type == TypeManager.uint32_type) return new UIntConstant ((uint) v); if (target_type == TypeManager.int64_type) return new LongConstant ((long) v); if (target_type == TypeManager.uint64_type) return new ULongConstant ((ulong) v); if (target_type == TypeManager.float_type) return new FloatConstant ((float) v); if (target_type == TypeManager.double_type) return new DoubleConstant ((double) v); } if (expr is UShortConstant){ ushort v = ((UShortConstant) expr).Value; if (target_type == TypeManager.byte_type) return new ByteConstant ((byte) v); if (target_type == TypeManager.sbyte_type) return new SByteConstant ((sbyte) v); if (target_type == TypeManager.short_type) return new ShortConstant ((short) v); if (target_type == TypeManager.int32_type) return new IntConstant ((int) v); if (target_type == TypeManager.uint32_type) return new UIntConstant ((uint) v); if (target_type == TypeManager.int64_type) return new LongConstant ((long) v); if (target_type == TypeManager.uint64_type) return new ULongConstant ((ulong) v); if (target_type == TypeManager.float_type) return new FloatConstant ((float) v); if (target_type == TypeManager.double_type) return new DoubleConstant ((double) v); } if (expr is IntConstant){ int v = ((IntConstant) expr).Value; if (target_type == TypeManager.byte_type) return new ByteConstant ((byte) v); if (target_type == TypeManager.sbyte_type) return new SByteConstant ((sbyte) v); if (target_type == TypeManager.short_type) return new ShortConstant ((short) v); if (target_type == TypeManager.ushort_type) return new UShortConstant ((ushort) v); if (target_type == TypeManager.uint32_type) return new UIntConstant ((uint) v); if (target_type == TypeManager.int64_type) return new LongConstant ((long) v); if (target_type == TypeManager.uint64_type) return new ULongConstant ((ulong) v); if (target_type == TypeManager.float_type) return new FloatConstant ((float) v); if (target_type == TypeManager.double_type) return new DoubleConstant ((double) v); } if (expr is UIntConstant){ uint v = ((UIntConstant) expr).Value; if (target_type == TypeManager.byte_type) return new ByteConstant ((byte) v); if (target_type == TypeManager.sbyte_type) return new SByteConstant ((sbyte) v); if (target_type == TypeManager.short_type) return new ShortConstant ((short) v); if (target_type == TypeManager.ushort_type) return new UShortConstant ((ushort) v); if (target_type == TypeManager.int32_type) return new IntConstant ((int) v); if (target_type == TypeManager.int64_type) return new LongConstant ((long) v); if (target_type == TypeManager.uint64_type) return new ULongConstant ((ulong) v); if (target_type == TypeManager.float_type) return new FloatConstant ((float) v); if (target_type == TypeManager.double_type) return new DoubleConstant ((double) v); } if (expr is LongConstant){ long v = ((LongConstant) expr).Value; if (target_type == TypeManager.byte_type) return new ByteConstant ((byte) v); if (target_type == TypeManager.sbyte_type) return new SByteConstant ((sbyte) v); if (target_type == TypeManager.short_type) return new ShortConstant ((short) v); if (target_type == TypeManager.ushort_type) return new UShortConstant ((ushort) v); if (target_type == TypeManager.int32_type) return new IntConstant ((int) v); if (target_type == TypeManager.uint32_type) return new UIntConstant ((uint) v); if (target_type == TypeManager.uint64_type) return new ULongConstant ((ulong) v); if (target_type == TypeManager.float_type) return new FloatConstant ((float) v); if (target_type == TypeManager.double_type) return new DoubleConstant ((double) v); } if (expr is ULongConstant){ ulong v = ((ULongConstant) expr).Value; if (target_type == TypeManager.byte_type) return new ByteConstant ((byte) v); if (target_type == TypeManager.sbyte_type) return new SByteConstant ((sbyte) v); if (target_type == TypeManager.short_type) return new ShortConstant ((short) v); if (target_type == TypeManager.ushort_type) return new UShortConstant ((ushort) v); if (target_type == TypeManager.int32_type) return new IntConstant ((int) v); if (target_type == TypeManager.uint32_type) return new UIntConstant ((uint) v); if (target_type == TypeManager.int64_type) return new LongConstant ((long) v); if (target_type == TypeManager.float_type) return new FloatConstant ((float) v); if (target_type == TypeManager.double_type) return new DoubleConstant ((double) v); } if (expr is FloatConstant){ float v = ((FloatConstant) expr).Value; if (target_type == TypeManager.byte_type) return new ByteConstant ((byte) v); if (target_type == TypeManager.sbyte_type) return new SByteConstant ((sbyte) v); if (target_type == TypeManager.short_type) return new ShortConstant ((short) v); if (target_type == TypeManager.ushort_type) return new UShortConstant ((ushort) v); if (target_type == TypeManager.int32_type) return new IntConstant ((int) v); if (target_type == TypeManager.uint32_type) return new UIntConstant ((uint) v); if (target_type == TypeManager.int64_type) return new LongConstant ((long) v); if (target_type == TypeManager.uint64_type) return new ULongConstant ((ulong) v); if (target_type == TypeManager.double_type) return new DoubleConstant ((double) v); } if (expr is DoubleConstant){ double v = ((DoubleConstant) expr).Value; if (target_type == TypeManager.byte_type) return new ByteConstant ((byte) v); if (target_type == TypeManager.sbyte_type) return new SByteConstant ((sbyte) v); if (target_type == TypeManager.short_type) return new ShortConstant ((short) v); if (target_type == TypeManager.ushort_type) return new UShortConstant ((ushort) v); if (target_type == TypeManager.int32_type) return new IntConstant ((int) v); if (target_type == TypeManager.uint32_type) return new UIntConstant ((uint) v); if (target_type == TypeManager.int64_type) return new LongConstant ((long) v); if (target_type == TypeManager.uint64_type) return new ULongConstant ((ulong) v); if (target_type == TypeManager.float_type) return new FloatConstant ((float) v); } return null; } public override Expression DoResolve (EmitContext ec) { expr = expr.Resolve (ec); if (expr == null) return null; target_type = target_type.Resolve (ec); if (target_type == null) return null; if (target_type.eclass != ExprClass.Type){ report118 (loc, target_type, "class"); return null; } type = target_type.Type; eclass = ExprClass.Value; if (type == null) return null; if (expr is Constant){ Expression e = TryReduce (ec, type); if (e != null) return e; } expr = ConvertExplicit (ec, expr, type, loc); return expr; } public override void Emit (EmitContext ec) { // // This one will never happen // throw new Exception ("Should not happen"); } } /// /// Binary operators /// public class Binary : Expression { public enum Operator : byte { Multiply, Division, Modulus, Addition, Subtraction, LeftShift, RightShift, LessThan, GreaterThan, LessThanOrEqual, GreaterThanOrEqual, Equality, Inequality, BitwiseAnd, ExclusiveOr, BitwiseOr, LogicalAnd, LogicalOr } Operator oper; Expression left, right; MethodBase method; ArrayList Arguments; Location loc; bool DelegateOperation; public Binary (Operator oper, Expression left, Expression right, Location loc) { this.oper = oper; this.left = left; this.right = right; this.loc = loc; } public Operator Oper { get { return oper; } set { oper = value; } } public Expression Left { get { return left; } set { left = value; } } public Expression Right { get { return right; } set { right = value; } } /// /// Returns a stringified representation of the Operator /// string OperName () { switch (oper){ case Operator.Multiply: return "*"; case Operator.Division: return "/"; case Operator.Modulus: return "%"; case Operator.Addition: return "+"; case Operator.Subtraction: return "-"; case Operator.LeftShift: return "<<"; case Operator.RightShift: return ">>"; case Operator.LessThan: return "<"; case Operator.GreaterThan: return ">"; case Operator.LessThanOrEqual: return "<="; case Operator.GreaterThanOrEqual: return ">="; case Operator.Equality: return "=="; case Operator.Inequality: return "!="; case Operator.BitwiseAnd: return "&"; case Operator.BitwiseOr: return "|"; case Operator.ExclusiveOr: return "^"; case Operator.LogicalOr: return "||"; case Operator.LogicalAnd: return "&&"; } return oper.ToString (); } Expression ForceConversion (EmitContext ec, Expression expr, Type target_type) { if (expr.Type == target_type) return expr; return ConvertImplicit (ec, expr, target_type, new Location (-1)); } // // Note that handling the case l == Decimal || r == Decimal // is taken care of by the Step 1 Operator Overload resolution. // bool DoNumericPromotions (EmitContext ec, Type l, Type r) { if (l == TypeManager.double_type || r == TypeManager.double_type){ // // If either operand is of type double, the other operand is // conveted to type double. // if (r != TypeManager.double_type) right = ConvertImplicit (ec, right, TypeManager.double_type, loc); if (l != TypeManager.double_type) left = ConvertImplicit (ec, left, TypeManager.double_type, loc); type = TypeManager.double_type; } else if (l == TypeManager.float_type || r == TypeManager.float_type){ // // if either operand is of type float, th eother operand is // converd to type float. // if (r != TypeManager.double_type) right = ConvertImplicit (ec, right, TypeManager.float_type, loc); if (l != TypeManager.double_type) left = ConvertImplicit (ec, left, TypeManager.float_type, loc); type = TypeManager.float_type; } else if (l == TypeManager.uint64_type || r == TypeManager.uint64_type){ Expression e; Type other; // // If either operand is of type ulong, the other operand is // converted to type ulong. or an error ocurrs if the other // operand is of type sbyte, short, int or long // if (l == TypeManager.uint64_type){ if (r != TypeManager.uint64_type){ if (right is IntConstant){ IntConstant ic = (IntConstant) right; e = TryImplicitIntConversion (l, ic); if (e != null) right = e; } else if (right is LongConstant){ long ll = ((LongConstant) right).Value; if (ll > 0) right = new ULongConstant ((ulong) ll); } else { e = ImplicitNumericConversion (ec, right, l, loc); if (e != null) right = e; } } other = right.Type; } else { if (left is IntConstant){ e = TryImplicitIntConversion (r, (IntConstant) left); if (e != null) left = e; } else if (left is LongConstant){ long ll = ((LongConstant) left).Value; if (ll > 0) left = new ULongConstant ((ulong) ll); } else { e = ImplicitNumericConversion (ec, left, r, loc); if (e != null) left = e; } other = left.Type; } if ((other == TypeManager.sbyte_type) || (other == TypeManager.short_type) || (other == TypeManager.int32_type) || (other == TypeManager.int64_type)){ string oper = OperName (); Error (34, loc, "Operator `" + OperName () + "' is ambiguous on operands of type `" + TypeManager.CSharpName (l) + "' " + "and `" + TypeManager.CSharpName (r) + "'"); } type = TypeManager.uint64_type; } else if (l == TypeManager.int64_type || r == TypeManager.int64_type){ // // If either operand is of type long, the other operand is converted // to type long. // if (l != TypeManager.int64_type) left = ConvertImplicit (ec, left, TypeManager.int64_type, loc); if (r != TypeManager.int64_type) right = ConvertImplicit (ec, right, TypeManager.int64_type, loc); type = TypeManager.int64_type; } else if (l == TypeManager.uint32_type || r == TypeManager.uint32_type){ // // If either operand is of type uint, and the other // operand is of type sbyte, short or int, othe operands are // converted to type long. // Type other = null; if (l == TypeManager.uint32_type){ if (right is IntConstant){ IntConstant ic = (IntConstant) right; int val = ic.Value; if (val >= 0) right = new UIntConstant ((uint) val); type = l; return true; } other = r; } else if (r == TypeManager.uint32_type){ if (left is IntConstant){ IntConstant ic = (IntConstant) left; int val = ic.Value; if (val >= 0) left = new UIntConstant ((uint) val); type = r; return true; } other = l; } if ((other == TypeManager.sbyte_type) || (other == TypeManager.short_type) || (other == TypeManager.int32_type)){ left = ForceConversion (ec, left, TypeManager.int64_type); right = ForceConversion (ec, right, TypeManager.int64_type); type = TypeManager.int64_type; } else { // // if either operand is of type uint, the other // operand is converd to type uint // left = ForceConversion (ec, left, TypeManager.uint32_type); right = ForceConversion (ec, right, TypeManager.uint32_type); type = TypeManager.uint32_type; } } else if (l == TypeManager.decimal_type || r == TypeManager.decimal_type){ if (l != TypeManager.decimal_type) left = ConvertImplicit (ec, left, TypeManager.decimal_type, loc); if (r != TypeManager.decimal_type) right = ConvertImplicit (ec, right, TypeManager.decimal_type, loc); type = TypeManager.decimal_type; } else { Expression l_tmp, r_tmp; l_tmp = ForceConversion (ec, left, TypeManager.int32_type); if (l_tmp == null) return false; r_tmp = ForceConversion (ec, right, TypeManager.int32_type); if (r_tmp == null) return false; left = l_tmp; right = r_tmp; type = TypeManager.int32_type; } return true; } void error19 () { Error (19, loc, "Operator " + OperName () + " cannot be applied to operands of type `" + TypeManager.CSharpName (left.Type) + "' and `" + TypeManager.CSharpName (right.Type) + "'"); } Expression CheckShiftArguments (EmitContext ec) { Expression e; Type l = left.Type; Type r = right.Type; e = ForceConversion (ec, right, TypeManager.int32_type); if (e == null){ error19 (); return null; } right = e; if (((e = ConvertImplicit (ec, left, TypeManager.int32_type, loc)) != null) || ((e = ConvertImplicit (ec, left, TypeManager.uint32_type, loc)) != null) || ((e = ConvertImplicit (ec, left, TypeManager.int64_type, loc)) != null) || ((e = ConvertImplicit (ec, left, TypeManager.uint64_type, loc)) != null)){ left = e; type = e.Type; return this; } error19 (); return null; } Expression ResolveOperator (EmitContext ec) { Type l = left.Type; Type r = right.Type; // // Step 1: Perform Operator Overload location // Expression left_expr, right_expr; string op = "op_" + oper; left_expr = MemberLookup (ec, l, op, loc); if (left_expr == null && l.BaseType != null) left_expr = MemberLookup (ec, l.BaseType, op, loc); right_expr = MemberLookup (ec, r, op, loc); if (right_expr == null && r.BaseType != null) right_expr = MemberLookup (ec, r.BaseType, op, loc); MethodGroupExpr union = Invocation.MakeUnionSet (left_expr, right_expr); if (union != null) { Arguments = new ArrayList (); Arguments.Add (new Argument (left, Argument.AType.Expression)); Arguments.Add (new Argument (right, Argument.AType.Expression)); method = Invocation.OverloadResolve (ec, union, Arguments, loc); if (method != null) { MethodInfo mi = (MethodInfo) method; type = mi.ReturnType; return this; } else { error19 (); return null; } } // // Step 2: Default operations on CLI native types. // // Only perform numeric promotions on: // +, -, *, /, %, &, |, ^, ==, !=, <, >, <=, >= // if (oper == Operator.Addition){ // // If any of the arguments is a string, cast to string // if (l == TypeManager.string_type){ if (r == TypeManager.string_type){ if (left is Constant && right is Constant){ StringConstant ls = (StringConstant) left; StringConstant rs = (StringConstant) right; return new StringConstant ( ls.Value + rs.Value); } // string + string method = TypeManager.string_concat_string_string; } else { // string + object method = TypeManager.string_concat_object_object; right = ConvertImplicit (ec, right, TypeManager.object_type, loc); } type = TypeManager.string_type; Arguments = new ArrayList (); Arguments.Add (new Argument (left, Argument.AType.Expression)); Arguments.Add (new Argument (right, Argument.AType.Expression)); return this; } else if (r == TypeManager.string_type){ // object + string method = TypeManager.string_concat_object_object; Arguments = new ArrayList (); Arguments.Add (new Argument (left, Argument.AType.Expression)); Arguments.Add (new Argument (right, Argument.AType.Expression)); left = ConvertImplicit (ec, left, TypeManager.object_type, loc); type = TypeManager.string_type; return this; } } if (oper == Operator.Addition || oper == Operator.Subtraction) { if (l.IsSubclassOf (TypeManager.delegate_type) && r.IsSubclassOf (TypeManager.delegate_type)) { Arguments = new ArrayList (); Arguments.Add (new Argument (left, Argument.AType.Expression)); Arguments.Add (new Argument (right, Argument.AType.Expression)); if (oper == Operator.Addition) method = TypeManager.delegate_combine_delegate_delegate; else method = TypeManager.delegate_remove_delegate_delegate; DelegateOperation = true; type = l; return this; } } // // Enumeration operators // bool lie = TypeManager.IsEnumType (l); bool rie = TypeManager.IsEnumType (r); if (lie || rie){ Expression temp; if (!rie){ temp = ConvertImplicit (ec, right, l, loc); if (temp != null) right = temp; } if (!lie){ temp = ConvertImplicit (ec, left, r, loc); if (temp != null){ left = temp; l = r; } } if (oper == Operator.Equality || oper == Operator.Inequality || oper == Operator.LessThanOrEqual || oper == Operator.LessThan || oper == Operator.GreaterThanOrEqual || oper == Operator.GreaterThan){ type = TypeManager.bool_type; return this; } if (oper == Operator.BitwiseAnd || oper == Operator.BitwiseOr || oper == Operator.ExclusiveOr){ type = l; return this; } } if (oper == Operator.LeftShift || oper == Operator.RightShift) return CheckShiftArguments (ec); if (oper == Operator.LogicalOr || oper == Operator.LogicalAnd){ if (l != TypeManager.bool_type || r != TypeManager.bool_type){ error19 (); return null; } type = TypeManager.bool_type; return this; } if (oper == Operator.Equality || oper == Operator.Inequality){ if (l == TypeManager.bool_type || r == TypeManager.bool_type){ if (r != TypeManager.bool_type || l != TypeManager.bool_type){ error19 (); return null; } type = TypeManager.bool_type; return this; } // // operator != (object a, object b) // operator == (object a, object b) // // For this to be used, both arguments have to be reference-types. // Read the rationale on the spec (14.9.6) // // Also, if at compile time we know that the classes do not inherit // one from the other, then we catch the error there. // if (!(l.IsValueType || r.IsValueType)){ type = TypeManager.bool_type; if (l == r) return this; if (l.IsSubclassOf (r) || r.IsSubclassOf (l)) return this; // // We are going to have to convert to an object to compare // if (l != TypeManager.object_type) left = new EmptyCast (left, TypeManager.object_type); if (r != TypeManager.object_type) right = new EmptyCast (right, TypeManager.object_type); return this; } } // // We are dealing with numbers // if (!DoNumericPromotions (ec, l, r)){ error19 (); return null; } if (left == null || right == null) return null; // // reload our cached types if required // l = left.Type; r = right.Type; if (oper == Operator.BitwiseAnd || oper == Operator.BitwiseOr || oper == Operator.ExclusiveOr){ if (l == r){ if (!((l == TypeManager.int32_type) || (l == TypeManager.uint32_type) || (l == TypeManager.int64_type) || (l == TypeManager.uint64_type))) type = l; } else { error19 (); return null; } } if (oper == Operator.Equality || oper == Operator.Inequality || oper == Operator.LessThanOrEqual || oper == Operator.LessThan || oper == Operator.GreaterThanOrEqual || oper == Operator.GreaterThan){ type = TypeManager.bool_type; } return this; } /// /// Constant expression reducer for binary operations /// public Expression ConstantFold (EmitContext ec) { object l = ((Constant) left).GetValue (); object r = ((Constant) right).GetValue (); if (l is string && r is string) return new StringConstant ((string) l + (string) r); Type result_type = null; // // Enumerator folding // if (left.Type == right.Type && left is EnumConstant) result_type = left.Type; switch (oper){ case Operator.BitwiseOr: if ((l is int) && (r is int)){ IntConstant v; int res = (int)l | (int)r; v = new IntConstant (res); if (result_type == null) return v; else return new EnumConstant (v, result_type); } break; case Operator.BitwiseAnd: if ((l is int) && (r is int)){ IntConstant v; int res = (int)l & (int)r; v = new IntConstant (res); if (result_type == null) return v; else return new EnumConstant (v, result_type); } break; } return null; } public override Expression DoResolve (EmitContext ec) { left = left.Resolve (ec); right = right.Resolve (ec); if (left == null || right == null) return null; if (left.Type == null) throw new Exception ( "Resolve returned non null, but did not set the type! (" + left + ") at Line: " + loc.Row); if (right.Type == null) throw new Exception ( "Resolve returned non null, but did not set the type! (" + right + ") at Line: "+ loc.Row); eclass = ExprClass.Value; if (left is Constant && right is Constant){ // // This is temporary until we do the full folding // Expression e = ConstantFold (ec); if (e != null) return e; } return ResolveOperator (ec); } public bool IsBranchable () { if (oper == Operator.Equality || oper == Operator.Inequality || oper == Operator.LessThan || oper == Operator.GreaterThan || oper == Operator.LessThanOrEqual || oper == Operator.GreaterThanOrEqual){ return true; } else return false; } /// /// This entry point is used by routines that might want /// to emit a brfalse/brtrue after an expression, and instead /// they could use a more compact notation. /// /// Typically the code would generate l.emit/r.emit, followed /// by the comparission and then a brtrue/brfalse. The comparissions /// are sometimes inneficient (there are not as complete as the branches /// look for the hacks in Emit using double ceqs). /// /// So for those cases we provide EmitBranchable that can emit the /// branch with the test /// public void EmitBranchable (EmitContext ec, int target) { OpCode opcode; bool close_target = false; ILGenerator ig = ec.ig; // // short-circuit operators // if (oper == Operator.LogicalAnd){ left.Emit (ec); ig.Emit (OpCodes.Brfalse, target); right.Emit (ec); ig.Emit (OpCodes.Brfalse, target); } else if (oper == Operator.LogicalOr){ left.Emit (ec); ig.Emit (OpCodes.Brtrue, target); right.Emit (ec); ig.Emit (OpCodes.Brfalse, target); } left.Emit (ec); right.Emit (ec); switch (oper){ case Operator.Equality: if (close_target) opcode = OpCodes.Beq_S; else opcode = OpCodes.Beq; break; case Operator.Inequality: if (close_target) opcode = OpCodes.Bne_Un_S; else opcode = OpCodes.Bne_Un; break; case Operator.LessThan: if (close_target) opcode = OpCodes.Blt_S; else opcode = OpCodes.Blt; break; case Operator.GreaterThan: if (close_target) opcode = OpCodes.Bgt_S; else opcode = OpCodes.Bgt; break; case Operator.LessThanOrEqual: if (close_target) opcode = OpCodes.Ble_S; else opcode = OpCodes.Ble; break; case Operator.GreaterThanOrEqual: if (close_target) opcode = OpCodes.Bge_S; else opcode = OpCodes.Ble; break; default: throw new Exception ("EmitBranchable called on non-EmitBranchable operator: " + oper.ToString ()); } ig.Emit (opcode, target); } public override void Emit (EmitContext ec) { ILGenerator ig = ec.ig; Type l = left.Type; Type r = right.Type; OpCode opcode; if (method != null) { // Note that operators are static anyway if (Arguments != null) Invocation.EmitArguments (ec, method, Arguments); if (method is MethodInfo) ig.Emit (OpCodes.Call, (MethodInfo) method); else ig.Emit (OpCodes.Call, (ConstructorInfo) method); if (DelegateOperation) ig.Emit (OpCodes.Castclass, type); return; } // // Handle short-circuit operators differently // than the rest // if (oper == Operator.LogicalAnd){ Label load_zero = ig.DefineLabel (); Label end = ig.DefineLabel (); left.Emit (ec); ig.Emit (OpCodes.Brfalse, load_zero); right.Emit (ec); ig.Emit (OpCodes.Br, end); ig.MarkLabel (load_zero); ig.Emit (OpCodes.Ldc_I4_0); ig.MarkLabel (end); return; } else if (oper == Operator.LogicalOr){ Label load_one = ig.DefineLabel (); Label end = ig.DefineLabel (); left.Emit (ec); ig.Emit (OpCodes.Brtrue, load_one); right.Emit (ec); ig.Emit (OpCodes.Br, end); ig.MarkLabel (load_one); ig.Emit (OpCodes.Ldc_I4_1); ig.MarkLabel (end); return; } left.Emit (ec); right.Emit (ec); switch (oper){ case Operator.Multiply: if (ec.CheckState){ if (l == TypeManager.int32_type || l == TypeManager.int64_type) opcode = OpCodes.Mul_Ovf; else if (l==TypeManager.uint32_type || l==TypeManager.uint64_type) opcode = OpCodes.Mul_Ovf_Un; else opcode = OpCodes.Mul; } else opcode = OpCodes.Mul; break; case Operator.Division: if (l == TypeManager.uint32_type || l == TypeManager.uint64_type) opcode = OpCodes.Div_Un; else opcode = OpCodes.Div; break; case Operator.Modulus: if (l == TypeManager.uint32_type || l == TypeManager.uint64_type) opcode = OpCodes.Rem_Un; else opcode = OpCodes.Rem; break; case Operator.Addition: if (ec.CheckState){ if (l == TypeManager.int32_type || l == TypeManager.int64_type) opcode = OpCodes.Add_Ovf; else if (l==TypeManager.uint32_type || l==TypeManager.uint64_type) opcode = OpCodes.Add_Ovf_Un; else opcode = OpCodes.Mul; } else opcode = OpCodes.Add; break; case Operator.Subtraction: if (ec.CheckState){ if (l == TypeManager.int32_type || l == TypeManager.int64_type) opcode = OpCodes.Sub_Ovf; else if (l==TypeManager.uint32_type || l==TypeManager.uint64_type) opcode = OpCodes.Sub_Ovf_Un; else opcode = OpCodes.Sub; } else opcode = OpCodes.Sub; break; case Operator.RightShift: opcode = OpCodes.Shr; break; case Operator.LeftShift: opcode = OpCodes.Shl; break; case Operator.Equality: opcode = OpCodes.Ceq; break; case Operator.Inequality: ec.ig.Emit (OpCodes.Ceq); ec.ig.Emit (OpCodes.Ldc_I4_0); opcode = OpCodes.Ceq; break; case Operator.LessThan: opcode = OpCodes.Clt; break; case Operator.GreaterThan: opcode = OpCodes.Cgt; break; case Operator.LessThanOrEqual: ec.ig.Emit (OpCodes.Cgt); ec.ig.Emit (OpCodes.Ldc_I4_0); opcode = OpCodes.Ceq; break; case Operator.GreaterThanOrEqual: ec.ig.Emit (OpCodes.Clt); ec.ig.Emit (OpCodes.Ldc_I4_1); opcode = OpCodes.Sub; break; case Operator.BitwiseOr: opcode = OpCodes.Or; break; case Operator.BitwiseAnd: opcode = OpCodes.And; break; case Operator.ExclusiveOr: opcode = OpCodes.Xor; break; default: throw new Exception ("This should not happen: Operator = " + oper.ToString ()); } ig.Emit (opcode); } } /// /// Implements the ternary conditiona operator (?:) /// public class Conditional : Expression { Expression expr, trueExpr, falseExpr; Location loc; public Conditional (Expression expr, Expression trueExpr, Expression falseExpr, Location l) { this.expr = expr; this.trueExpr = trueExpr; this.falseExpr = falseExpr; this.loc = l; } public Expression Expr { get { return expr; } } public Expression TrueExpr { get { return trueExpr; } } public Expression FalseExpr { get { return falseExpr; } } public override Expression DoResolve (EmitContext ec) { expr = expr.Resolve (ec); if (expr.Type != TypeManager.bool_type) expr = Expression.ConvertImplicitRequired ( ec, expr, TypeManager.bool_type, loc); trueExpr = trueExpr.Resolve (ec); falseExpr = falseExpr.Resolve (ec); if (expr == null || trueExpr == null || falseExpr == null) return null; if (trueExpr.Type == falseExpr.Type) type = trueExpr.Type; else { Expression conv; // // First, if an implicit conversion exists from trueExpr // to falseExpr, then the result type is of type falseExpr.Type // conv = ConvertImplicit (ec, trueExpr, falseExpr.Type, loc); if (conv != null){ type = falseExpr.Type; trueExpr = conv; } else if ((conv = ConvertImplicit(ec, falseExpr,trueExpr.Type,loc))!= null){ type = trueExpr.Type; falseExpr = conv; } else { Error (173, loc, "The type of the conditional expression can " + "not be computed because there is no implicit conversion" + " from `" + TypeManager.CSharpName (trueExpr.Type) + "'" + " and `" + TypeManager.CSharpName (falseExpr.Type) + "'"); return null; } } if (expr is BoolConstant){ BoolConstant bc = (BoolConstant) expr; if (bc.Value) return trueExpr; else return falseExpr; } eclass = ExprClass.Value; return this; } public override void Emit (EmitContext ec) { ILGenerator ig = ec.ig; Label false_target = ig.DefineLabel (); Label end_target = ig.DefineLabel (); expr.Emit (ec); ig.Emit (OpCodes.Brfalse, false_target); trueExpr.Emit (ec); ig.Emit (OpCodes.Br, end_target); ig.MarkLabel (false_target); falseExpr.Emit (ec); ig.MarkLabel (end_target); } } /// /// Local variables /// public class LocalVariableReference : Expression, IAssignMethod, IMemoryLocation { public readonly string Name; public readonly Block Block; Location loc; VariableInfo variable_info; public LocalVariableReference (Block block, string name, Location l) { Block = block; Name = name; loc = l; eclass = ExprClass.Variable; } public VariableInfo VariableInfo { get { if (variable_info == null) variable_info = Block.GetVariableInfo (Name); return variable_info; } } public override Expression DoResolve (EmitContext ec) { VariableInfo vi = VariableInfo; if (Block.IsConstant (Name)) { Expression e = Block.GetConstantExpression (Name); e = e.Resolve (ec); if (e == null) return null; if (!(e is Constant)) { Report.Error (150, loc, "A constant value is expected"); return null; } vi.Used = true; return e; } type = vi.VariableType; return this; } override public Expression DoResolveLValue (EmitContext ec, Expression right_side) { Expression e = DoResolve (ec); if (e == null) return null; VariableInfo vi = VariableInfo; if (vi.ReadOnly){ if (vi.Assigned){ Report.Error ( 1604, loc, "cannot assign to `" + Name + "' because it is readonly"); return null; } } return this; } public override void Emit (EmitContext ec) { VariableInfo vi = VariableInfo; ILGenerator ig = ec.ig; int idx = vi.Idx; vi.Used = true; switch (idx){ case 0: ig.Emit (OpCodes.Ldloc_0); break; case 1: ig.Emit (OpCodes.Ldloc_1); break; case 2: ig.Emit (OpCodes.Ldloc_2); break; case 3: ig.Emit (OpCodes.Ldloc_3); break; default: if (idx <= 255) ig.Emit (OpCodes.Ldloc_S, (byte) idx); else ig.Emit (OpCodes.Ldloc, idx); break; } } public static void Store (ILGenerator ig, int idx) { switch (idx){ case 0: ig.Emit (OpCodes.Stloc_0); break; case 1: ig.Emit (OpCodes.Stloc_1); break; case 2: ig.Emit (OpCodes.Stloc_2); break; case 3: ig.Emit (OpCodes.Stloc_3); break; default: if (idx <= 255) ig.Emit (OpCodes.Stloc_S, (byte) idx); else ig.Emit (OpCodes.Stloc, idx); break; } } public void EmitAssign (EmitContext ec, Expression source) { ILGenerator ig = ec.ig; VariableInfo vi = VariableInfo; vi.Assigned = true; source.Emit (ec); // Funny seems the code below generates optimal code for us, but // seems to take too long to generate what we need. // ig.Emit (OpCodes.Stloc, vi.LocalBuilder); Store (ig, vi.Idx); } public void AddressOf (EmitContext ec) { VariableInfo vi = VariableInfo; int idx = vi.Idx; vi.Used = true; vi.Assigned = true; if (idx <= 255) ec.ig.Emit (OpCodes.Ldloca_S, (byte) idx); else ec.ig.Emit (OpCodes.Ldloca, idx); } } /// /// This represents a reference to a parameter in the intermediate /// representation. /// public class ParameterReference : Expression, IAssignMethod, IMemoryLocation { Parameters pars; String name; int idx; public bool is_ref; public ParameterReference (Parameters pars, int idx, string name) { this.pars = pars; this.idx = idx; this.name = name; eclass = ExprClass.Variable; } // // Notice that for ref/out parameters, the type exposed is not the // same type exposed externally. // // for "ref int a": // externally we expose "int&" // here we expose "int". // // We record this in "is_ref". This means that the type system can treat // the type as it is expected, but when we generate the code, we generate // the alternate kind of code. // public override Expression DoResolve (EmitContext ec) { type = pars.GetParameterInfo (ec.TypeContainer, idx, out is_ref); eclass = ExprClass.Variable; return this; } // // This method is used by parameters that are references, that are // being passed as references: we only want to pass the pointer (that // is already stored in the parameter, not the address of the pointer, // and not the value of the variable). // public void EmitLoad (EmitContext ec) { ILGenerator ig = ec.ig; int arg_idx = idx; if (!ec.IsStatic) arg_idx++; if (arg_idx <= 255) ig.Emit (OpCodes.Ldarg_S, (byte) arg_idx); else ig.Emit (OpCodes.Ldarg, arg_idx); } public override void Emit (EmitContext ec) { ILGenerator ig = ec.ig; int arg_idx = idx; if (!ec.IsStatic) arg_idx++; if (arg_idx <= 255) ig.Emit (OpCodes.Ldarg_S, (byte) arg_idx); else ig.Emit (OpCodes.Ldarg, arg_idx); if (!is_ref) return; // // If we are a reference, we loaded on the stack a pointer // Now lets load the real value // if (type == TypeManager.int32_type) ig.Emit (OpCodes.Ldind_I4); else if (type == TypeManager.uint32_type) ig.Emit (OpCodes.Ldind_U4); else if (type == TypeManager.int64_type || type == TypeManager.uint64_type) ig.Emit (OpCodes.Ldind_I8); else if (type == TypeManager.char_type) ig.Emit (OpCodes.Ldind_U2); else if (type == TypeManager.short_type) ig.Emit (OpCodes.Ldind_I2); else if (type == TypeManager.ushort_type) ig.Emit (OpCodes.Ldind_U2); else if (type == TypeManager.float_type) ig.Emit (OpCodes.Ldind_R4); else if (type == TypeManager.double_type) ig.Emit (OpCodes.Ldind_R8); else if (type == TypeManager.byte_type) ig.Emit (OpCodes.Ldind_U1); else if (type == TypeManager.sbyte_type || type == TypeManager.bool_type) ig.Emit (OpCodes.Ldind_I1); else if (type == TypeManager.intptr_type) ig.Emit (OpCodes.Ldind_I); else ig.Emit (OpCodes.Ldind_Ref); } public void EmitAssign (EmitContext ec, Expression source) { ILGenerator ig = ec.ig; int arg_idx = idx; if (!ec.IsStatic) arg_idx++; if (is_ref){ // Load the pointer if (arg_idx <= 255) ig.Emit (OpCodes.Ldarg_S, (byte) arg_idx); else ig.Emit (OpCodes.Ldarg, arg_idx); } source.Emit (ec); if (is_ref){ 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 ig.Emit (OpCodes.Stind_Ref); } else { if (arg_idx <= 255) ig.Emit (OpCodes.Starg_S, (byte) arg_idx); else ig.Emit (OpCodes.Starg, arg_idx); } } public void AddressOf (EmitContext ec) { int arg_idx = idx; if (!ec.IsStatic) arg_idx++; if (arg_idx <= 255) ec.ig.Emit (OpCodes.Ldarga_S, (byte) arg_idx); else ec.ig.Emit (OpCodes.Ldarga, arg_idx); } } /// /// Used for arguments to New(), Invocation() /// public class Argument { public enum AType : byte { Expression, Ref, Out }; public readonly AType ArgType; public Expression expr; public Argument (Expression expr, AType type) { this.expr = expr; this.ArgType = type; } public Expression Expr { get { return expr; } set { expr = value; } } public Type Type { get { return expr.Type; } } public Parameter.Modifier GetParameterModifier () { if (ArgType == AType.Ref) return Parameter.Modifier.REF; if (ArgType == AType.Out) return Parameter.Modifier.OUT; return Parameter.Modifier.NONE; } public static string FullDesc (Argument a) { return (a.ArgType == AType.Ref ? "ref " : (a.ArgType == AType.Out ? "out " : "")) + TypeManager.CSharpName (a.Expr.Type); } public bool Resolve (EmitContext ec, Location loc) { expr = expr.Resolve (ec); if (ArgType == AType.Expression) return expr != null; if (expr.eclass != ExprClass.Variable){ // // We just probe to match the CSC output // if (expr.eclass == ExprClass.PropertyAccess || expr.eclass == ExprClass.IndexerAccess){ Report.Error ( 206, loc, "A property or indexer can not be passed as an out or ref " + "parameter"); } else { Report.Error ( 1510, loc, "An lvalue is required as an argument to out or ref"); } return false; } return expr != null; } public void Emit (EmitContext ec) { // // Ref and Out parameters need to have their addresses taken. // // ParameterReferences might already be references, so we want // to pass just the value // if (ArgType == AType.Ref || ArgType == AType.Out){ if (expr is ParameterReference){ ParameterReference pr = (ParameterReference) expr; if (pr.is_ref) pr.EmitLoad (ec); else pr.AddressOf (ec); } else ((IMemoryLocation)expr).AddressOf (ec); } else expr.Emit (ec); } } /// /// Invocation of methods or delegates. /// public class Invocation : ExpressionStatement { public readonly ArrayList Arguments; Location loc; Expression expr; MethodBase method = null; bool is_base; static Hashtable method_parameter_cache; static Invocation () { method_parameter_cache = new PtrHashtable (); } // // arguments is an ArrayList, but we do not want to typecast, // as it might be null. // // FIXME: only allow expr to be a method invocation or a // delegate invocation (7.5.5) // public Invocation (Expression expr, ArrayList arguments, Location l) { this.expr = expr; Arguments = arguments; loc = l; } public Expression Expr { get { return expr; } } /// /// Returns the Parameters (a ParameterData interface) for the /// Method `mb' /// public static ParameterData GetParameterData (MethodBase mb) { object pd = method_parameter_cache [mb]; object ip; if (pd != null) return (ParameterData) pd; ip = TypeManager.LookupParametersByBuilder (mb); if (ip != null){ method_parameter_cache [mb] = ip; return (ParameterData) ip; } else { ParameterInfo [] pi = mb.GetParameters (); ReflectionParameters rp = new ReflectionParameters (pi); method_parameter_cache [mb] = rp; return (ParameterData) rp; } } /// /// Determines "better conversion" as specified in 7.4.2.3 /// Returns : 1 if a->p is better /// 0 if a->q or neither is better /// static int BetterConversion (EmitContext ec, Argument a, Type p, Type q, Location loc) { Type argument_type = a.Type; Expression argument_expr = a.Expr; if (argument_type == null) throw new Exception ("Expression of type " + a.Expr + " does not resolve its type"); if (p == q) return 0; if (argument_type == p) return 1; if (argument_type == q) return 0; // // Now probe whether an implicit constant expression conversion // can be used. // // An implicit constant expression conversion permits the following // conversions: // // * A constant-expression of type `int' can be converted to type // sbyte, byute, short, ushort, uint, ulong provided the value of // of the expression is withing the range of the destination type. // // * A constant-expression of type long can be converted to type // ulong, provided the value of the constant expression is not negative // // FIXME: Note that this assumes that constant folding has // taken place. We dont do constant folding yet. // if (argument_expr is IntConstant){ IntConstant ei = (IntConstant) argument_expr; int value = ei.Value; if (p == TypeManager.sbyte_type){ if (value >= SByte.MinValue && value <= SByte.MaxValue) return 1; } else if (p == TypeManager.byte_type){ if (Byte.MinValue >= 0 && value <= Byte.MaxValue) return 1; } else if (p == TypeManager.short_type){ if (value >= Int16.MinValue && value <= Int16.MaxValue) return 1; } else if (p == TypeManager.ushort_type){ if (value >= UInt16.MinValue && value <= UInt16.MaxValue) return 1; } else if (p == TypeManager.uint32_type){ // // we can optimize this case: a positive int32 // always fits on a uint32 // if (value >= 0) return 1; } else if (p == TypeManager.uint64_type){ // // we can optimize this case: a positive int32 // always fits on a uint64 // if (value >= 0) return 1; } } else if (argument_type == TypeManager.int64_type && argument_expr is LongConstant){ LongConstant lc = (LongConstant) argument_expr; if (p == TypeManager.uint64_type){ if (lc.Value > 0) return 1; } } if (q == null) { Expression tmp = ConvertImplicitStandard (ec, argument_expr, p, loc); if (tmp != null) return 1; else return 0; } if (StandardConversionExists (p, q) == true && StandardConversionExists (q, p) == false) return 1; if (p == TypeManager.sbyte_type) if (q == TypeManager.byte_type || q == TypeManager.ushort_type || q == TypeManager.uint32_type || q == TypeManager.uint64_type) return 1; if (p == TypeManager.short_type) if (q == TypeManager.ushort_type || q == TypeManager.uint32_type || q == TypeManager.uint64_type) return 1; if (p == TypeManager.int32_type) if (q == TypeManager.uint32_type || q == TypeManager.uint64_type) return 1; if (p == TypeManager.int64_type) if (q == TypeManager.uint64_type) return 1; return 0; } /// /// Determines "Better function" /// /// /// and returns an integer indicating : /// 0 if candidate ain't better /// 1 if candidate is better than the current best match /// static int BetterFunction (EmitContext ec, ArrayList args, MethodBase candidate, MethodBase best, bool expanded_form, Location loc) { ParameterData candidate_pd = GetParameterData (candidate); ParameterData best_pd; int argument_count; if (args == null) argument_count = 0; else argument_count = args.Count; if (candidate_pd.Count == 0 && argument_count == 0) return 1; if (candidate_pd.ParameterModifier (candidate_pd.Count - 1) != Parameter.Modifier.PARAMS) if (candidate_pd.Count != argument_count) return 0; if (best == null) { int x = 0; for (int j = argument_count; j > 0;) { j--; Argument a = (Argument) args [j]; Type t = candidate_pd.ParameterType (j); if (candidate_pd.ParameterModifier (j) == Parameter.Modifier.PARAMS) if (expanded_form) t = t.GetElementType (); x = BetterConversion (ec, a, t, null, loc); if (x <= 0) break; } if (x > 0) return 1; else return 0; } best_pd = GetParameterData (best); int rating1 = 0, rating2 = 0; for (int j = 0; j < argument_count; ++j) { int x, y; Argument a = (Argument) args [j]; Type ct = candidate_pd.ParameterType (j); Type bt = best_pd.ParameterType (j); if (candidate_pd.ParameterModifier (j) == Parameter.Modifier.PARAMS) if (expanded_form) ct = ct.GetElementType (); if (best_pd.ParameterModifier (j) == Parameter.Modifier.PARAMS) if (expanded_form) bt = bt.GetElementType (); x = BetterConversion (ec, a, ct, bt, loc); y = BetterConversion (ec, a, bt, ct, loc); if (x < y) break; rating1 += x; rating2 += y; } if (rating1 > rating2) return 1; else return 0; } public static string FullMethodDesc (MethodBase mb) { string ret_type = ""; if (mb is MethodInfo) ret_type = TypeManager.CSharpName (((MethodInfo) mb).ReturnType); StringBuilder sb = new StringBuilder (ret_type + " " + mb.Name); ParameterData pd = GetParameterData (mb); int count = pd.Count; sb.Append (" ("); for (int i = count; i > 0; ) { i--; sb.Append (pd.ParameterDesc (count - i - 1)); if (i != 0) sb.Append (", "); } sb.Append (")"); return sb.ToString (); } public static MethodGroupExpr MakeUnionSet (Expression mg1, Expression mg2) { MemberInfo [] miset; MethodGroupExpr union; if (mg1 != null && mg2 != null) { MethodGroupExpr left_set = null, right_set = null; int length1 = 0, length2 = 0; left_set = (MethodGroupExpr) mg1; length1 = left_set.Methods.Length; right_set = (MethodGroupExpr) mg2; length2 = right_set.Methods.Length; ArrayList common = new ArrayList (); for (int i = 0; i < left_set.Methods.Length; i++) { for (int j = 0; j < right_set.Methods.Length; j++) { if (left_set.Methods [i] == right_set.Methods [j]) common.Add (left_set.Methods [i]); } } miset = new MemberInfo [length1 + length2 - common.Count]; left_set.Methods.CopyTo (miset, 0); int k = 0; for (int j = 0; j < right_set.Methods.Length; j++) if (!common.Contains (right_set.Methods [j])) miset [length1 + k++] = right_set.Methods [j]; union = new MethodGroupExpr (miset); return union; } else if (mg1 == null && mg2 != null) { MethodGroupExpr me = (MethodGroupExpr) mg2; miset = new MemberInfo [me.Methods.Length]; me.Methods.CopyTo (miset, 0); union = new MethodGroupExpr (miset); return union; } else if (mg2 == null && mg1 != null) { MethodGroupExpr me = (MethodGroupExpr) mg1; miset = new MemberInfo [me.Methods.Length]; me.Methods.CopyTo (miset, 0); union = new MethodGroupExpr (miset); return union; } return null; } /// /// Determines is the candidate method, if a params method, is applicable /// in its expanded form to the given set of arguments /// static bool IsParamsMethodApplicable (ArrayList arguments, MethodBase candidate) { int arg_count; if (arguments == null) arg_count = 0; else arg_count = arguments.Count; ParameterData pd = GetParameterData (candidate); int pd_count = pd.Count; if (pd_count == 0) return false; if (pd.ParameterModifier (pd_count - 1) != Parameter.Modifier.PARAMS) return false; if (pd_count - 1 > arg_count) return false; // // If we have come this far, the case which remains is when the number of parameters // is less than or equal to the argument count. So, we now check if the element type // of the params array is compatible with each argument type // Type element_type = pd.ParameterType (pd_count - 1).GetElementType (); for (int i = pd_count - 1; i < arg_count - 1; i++) { Argument a = (Argument) arguments [i]; if (!StandardConversionExists (a.Type, element_type)) return false; } return true; } /// /// Determines if the candidate method is applicable (section 14.4.2.1) /// to the given set of arguments /// static bool IsApplicable (ArrayList arguments, MethodBase candidate) { int arg_count; if (arguments == null) arg_count = 0; else arg_count = arguments.Count; ParameterData pd = GetParameterData (candidate); int pd_count = pd.Count; if (arg_count != pd.Count) return false; for (int i = arg_count; i > 0; ) { i--; Argument a = (Argument) arguments [i]; Parameter.Modifier a_mod = a.GetParameterModifier (); Parameter.Modifier p_mod = pd.ParameterModifier (i); if (a_mod == p_mod) { if (a_mod == Parameter.Modifier.NONE) if (!StandardConversionExists (a.Type, pd.ParameterType (i))) return false; if (a_mod == Parameter.Modifier.REF || a_mod == Parameter.Modifier.OUT) if (pd.ParameterType (i) != a.Type) return false; } else return false; } return true; } /// /// Find the Applicable Function Members (7.4.2.1) /// /// me: Method Group expression with the members to select. /// it might contain constructors or methods (or anything /// that maps to a method). /// /// Arguments: ArrayList containing resolved Argument objects. /// /// loc: The location if we want an error to be reported, or a Null /// location for "probing" purposes. /// /// Returns: The MethodBase (either a ConstructorInfo or a MethodInfo) /// that is the best match of me on Arguments. /// /// public static MethodBase OverloadResolve (EmitContext ec, MethodGroupExpr me, ArrayList Arguments, Location loc) { ArrayList afm = new ArrayList (); int best_match_idx = -1; MethodBase method = null; int argument_count; ArrayList candidates = new ArrayList (); for (int i = me.Methods.Length; i > 0; ){ i--; MethodBase candidate = me.Methods [i]; int x; // Check if candidate is applicable (section 14.4.2.1) if (!IsApplicable (Arguments, candidate)) continue; candidates.Add (candidate); x = BetterFunction (ec, Arguments, candidate, method, false, loc); if (x == 0) continue; else { best_match_idx = i; method = me.Methods [best_match_idx]; } } if (Arguments == null) argument_count = 0; else argument_count = Arguments.Count; // // Now we see if we can find params functions, applicable in their expanded form // since if they were applicable in their normal form, they would have been selected // above anyways // bool chose_params_expanded = false; if (best_match_idx == -1) { candidates = new ArrayList (); for (int i = me.Methods.Length; i > 0; ) { i--; MethodBase candidate = me.Methods [i]; if (!IsParamsMethodApplicable (Arguments, candidate)) continue; candidates.Add (candidate); int x = BetterFunction (ec, Arguments, candidate, method, true, loc); if (x == 0) continue; else { best_match_idx = i; method = me.Methods [best_match_idx]; chose_params_expanded = true; } } } // // Now we see if we can at least find a method with the same number of arguments // ParameterData pd; int method_count = 0; if (best_match_idx == -1) { for (int i = me.Methods.Length; i > 0;) { i--; MethodBase mb = me.Methods [i]; pd = GetParameterData (mb); if (pd.Count == argument_count) { best_match_idx = i; method = me.Methods [best_match_idx]; method_count++; } else continue; } } if (method == null) return null; // // Now check that there are no ambiguities i.e the selected method // should be better than all the others // for (int i = 0; i < candidates.Count; ++i) { MethodBase candidate = (MethodBase) candidates [i]; if (candidate == method) continue; // // If a normal method is applicable in the sense that it has the same // number of arguments, then the expanded params method is never applicable // so we debar the params method. // if (IsParamsMethodApplicable (Arguments, candidate) && IsApplicable (Arguments, method)) continue; int x = BetterFunction (ec, Arguments, method, candidate, chose_params_expanded, loc); if (x != 1) { Console.WriteLine ("Candidate : " + FullMethodDesc (candidate)); Console.WriteLine ("Best : " + FullMethodDesc (method)); Report.Error ( 121, loc, "Ambiguous call when selecting function due to implicit casts"); return null; } } // And now convert implicitly, each argument to the required type pd = GetParameterData (method); int pd_count = pd.Count; for (int j = 0; j < argument_count; j++) { Argument a = (Argument) Arguments [j]; Expression a_expr = a.Expr; Type parameter_type = pd.ParameterType (j); if (pd.ParameterModifier (j) == Parameter.Modifier.PARAMS && chose_params_expanded) parameter_type = parameter_type.GetElementType (); if (a.Type != parameter_type){ Expression conv; conv = ConvertImplicitStandard (ec, a_expr, parameter_type, Location.Null); if (conv == null) { if (!Location.IsNull (loc)) { Error (1502, loc, "The best overloaded match for method '" + FullMethodDesc (method) + "' has some invalid arguments"); Error (1503, loc, "Argument " + (j+1) + ": Cannot convert from '" + Argument.FullDesc (a) + "' to '" + pd.ParameterDesc (j) + "'"); } return null; } // // Update the argument with the implicit conversion // if (a_expr != conv) a.Expr = conv; // FIXME : For the case of params methods, we need to actually instantiate // an array and initialize it with the argument values etc etc. } if (a.GetParameterModifier () != pd.ParameterModifier (j) && pd.ParameterModifier (pd_count - 1) != Parameter.Modifier.PARAMS) { if (!Location.IsNull (loc)) { Error (1502, loc, "The best overloaded match for method '" + FullMethodDesc (method)+ "' has some invalid arguments"); Error (1503, loc, "Argument " + (j+1) + ": Cannot convert from '" + Argument.FullDesc (a) + "' to '" + pd.ParameterDesc (j) + "'"); } return null; } } return method; } public override Expression DoResolve (EmitContext ec) { // // First, resolve the expression that is used to // trigger the invocation // if (expr is BaseAccess) is_base = true; expr = expr.Resolve (ec); if (expr == null) return null; if (!(expr is MethodGroupExpr)) { Type expr_type = expr.Type; if (expr_type != null){ bool IsDelegate = TypeManager.IsDelegateType (expr_type); if (IsDelegate) return (new DelegateInvocation ( this.expr, Arguments, loc)).Resolve (ec); } } if (!(expr is MethodGroupExpr)){ report118 (loc, this.expr, "method group"); return null; } // // Next, evaluate all the expressions in the argument list // if (Arguments != null){ for (int i = Arguments.Count; i > 0;){ --i; Argument a = (Argument) Arguments [i]; if (!a.Resolve (ec, loc)) return null; } } method = OverloadResolve (ec, (MethodGroupExpr) this.expr, Arguments, loc); if (method == null){ Error (-6, loc, "Could not find any applicable function for this argument list"); return null; } if (method is MethodInfo) type = ((MethodInfo)method).ReturnType; eclass = ExprClass.Value; return this; } // // Emits the list of arguments as an array // static void EmitParams (EmitContext ec, int idx, ArrayList arguments) { ILGenerator ig = ec.ig; int count = arguments.Count - idx; Argument a = (Argument) arguments [idx]; Type t = a.expr.Type; string array_type = t.FullName + "[]"; LocalBuilder array; array = ig.DeclareLocal (Type.GetType (array_type)); IntConstant.EmitInt (ig, count); ig.Emit (OpCodes.Newarr, t); ig.Emit (OpCodes.Stloc, array); int top = arguments.Count; for (int j = idx; j < top; j++){ a = (Argument) arguments [j]; ig.Emit (OpCodes.Ldloc, array); IntConstant.EmitInt (ig, j - idx); a.Emit (ec); ArrayAccess.EmitStoreOpcode (ig, t); } ig.Emit (OpCodes.Ldloc, array); } /// /// Emits a list of resolved Arguments that are in the arguments /// ArrayList. /// /// The MethodBase argument might be null if the /// emission of the arguments is known not to contain /// a `params' field (for example in constructors or other routines /// that keep their arguments in this structure /// public static void EmitArguments (EmitContext ec, MethodBase mb, ArrayList arguments) { ParameterData pd = null; int top; if (arguments != null) top = arguments.Count; else top = 0; if (mb != null) pd = GetParameterData (mb); for (int i = 0; i < top; i++){ Argument a = (Argument) arguments [i]; if (pd != null){ if (pd.ParameterModifier (i) == Parameter.Modifier.PARAMS){ EmitParams (ec, i, arguments); return; } } a.Emit (ec); } } /// /// is_base tells whether we want to force the use of the `call' /// opcode instead of using callvirt. Call is required to call /// a specific method, while callvirt will always use the most /// recent method in the vtable. /// /// is_static tells whether this is an invocation on a static method /// /// instance_expr is an expression that represents the instance /// it must be non-null if is_static is false. /// /// method is the method to invoke. /// /// Arguments is the list of arguments to pass to the method or constructor. /// public static void EmitCall (EmitContext ec, bool is_base, bool is_static, Expression instance_expr, MethodBase method, ArrayList Arguments) { ILGenerator ig = ec.ig; bool struct_call = false; if (!is_static){ if (method.DeclaringType.IsValueType) struct_call = true; // // If this is ourselves, push "this" // if (instance_expr == null){ ig.Emit (OpCodes.Ldarg_0); } else { // // Push the instance expression // if (instance_expr.Type.IsSubclassOf (TypeManager.value_type)){ // // Special case: calls to a function declared in a // reference-type with a value-type argument need // to have their value boxed. struct_call = true; if (method.DeclaringType.IsValueType){ // // If the expression implements IMemoryLocation, then // we can optimize and use AddressOf on the // return. // // If not we have to use some temporary storage for // it. if (instance_expr is IMemoryLocation){ ((IMemoryLocation)instance_expr). AddressOf (ec); } else { Type t = instance_expr.Type; instance_expr.Emit (ec); LocalBuilder temp = ig.DeclareLocal (t); ig.Emit (OpCodes.Stloc, temp); ig.Emit (OpCodes.Ldloca, temp); } } else { instance_expr.Emit (ec); ig.Emit (OpCodes.Box, instance_expr.Type); } } else instance_expr.Emit (ec); } } if (Arguments != null) EmitArguments (ec, method, Arguments); if (method is MethodInfo){ MethodInfo mi = (MethodInfo) method; if (!mi.IsVirtual) is_static = true; } if (is_static || struct_call || is_base){ if (method is MethodInfo) ig.Emit (OpCodes.Call, (MethodInfo) method); else ig.Emit (OpCodes.Call, (ConstructorInfo) method); } else { if (method is MethodInfo) ig.Emit (OpCodes.Callvirt, (MethodInfo) method); else ig.Emit (OpCodes.Callvirt, (ConstructorInfo) method); } } public override void Emit (EmitContext ec) { MethodGroupExpr mg = (MethodGroupExpr) this.expr; EmitCall (ec, is_base, method.IsStatic, mg.InstanceExpression, method, Arguments); } public override void EmitStatement (EmitContext ec) { Emit (ec); // // Pop the return value if there is one // if (method is MethodInfo){ if (((MethodInfo)method).ReturnType != TypeManager.void_type) ec.ig.Emit (OpCodes.Pop); } } } /// /// Implements the new expression /// public class New : ExpressionStatement { public readonly ArrayList Arguments; public readonly string RequestedType; Location loc; MethodBase method = null; // // If set, the new expression is for a value_target, and // we will not leave anything on the stack. // Expression value_target; public New (string requested_type, ArrayList arguments, Location l) { RequestedType = requested_type; Arguments = arguments; loc = l; } public Expression ValueTypeVariable { get { return value_target; } set { value_target = value; } } public override Expression DoResolve (EmitContext ec) { type = RootContext.LookupType (ec.TypeContainer, RequestedType, false, loc); if (type == null) return null; bool IsDelegate = TypeManager.IsDelegateType (type); if (IsDelegate) return (new NewDelegate (type, Arguments, loc)).Resolve (ec); bool is_struct = false; is_struct = type.IsSubclassOf (TypeManager.value_type); eclass = ExprClass.Value; // // SRE returns a match for .ctor () on structs (the object constructor), // so we have to manually ignore it. // if (is_struct && Arguments == null) return this; Expression ml; ml = MemberLookup (ec, type, ".ctor", MemberTypes.Constructor, AllBindingFlags, loc); if (! (ml is MethodGroupExpr)){ if (!is_struct){ report118 (loc, ml, "method group"); return null; } } if (ml != null) { if (Arguments != null){ for (int i = Arguments.Count; i > 0;){ --i; Argument a = (Argument) Arguments [i]; if (!a.Resolve (ec, loc)) return null; } } method = Invocation.OverloadResolve (ec, (MethodGroupExpr) ml, Arguments, loc); } if (method == null && !is_struct) { Error (-6, loc, "New invocation: Can not find a constructor for " + "this argument list"); return null; } return this; } // // This DoEmit can be invoked in two contexts: // * As a mechanism that will leave a value on the stack (new object) // * As one that wont (init struct) // // You can control whether a value is required on the stack by passing // need_value_on_stack. The code *might* leave a value on the stack // so it must be popped manually // // If we are dealing with a ValueType, we have a few // situations to deal with: // // * The target is a ValueType, and we have been provided // the instance (this is easy, we are being assigned). // // * The target of New is being passed as an argument, // to a boxing operation or a function that takes a // ValueType. // // In this case, we need to create a temporary variable // that is the argument of New. // // Returns whether a value is left on the stack // bool DoEmit (EmitContext ec, bool need_value_on_stack) { bool is_value_type = type.IsSubclassOf (TypeManager.value_type); ILGenerator ig = ec.ig; if (is_value_type){ IMemoryLocation ml; if (value_target == null) value_target = new LocalTemporary (ec, type); ml = (IMemoryLocation) value_target; ml.AddressOf (ec); } if (method != null) Invocation.EmitArguments (ec, method, Arguments); if (is_value_type){ if (method == null) ig.Emit (OpCodes.Initobj, type); else ig.Emit (OpCodes.Call, (ConstructorInfo) method); if (need_value_on_stack){ value_target.Emit (ec); return true; } return false; } else { ig.Emit (OpCodes.Newobj, (ConstructorInfo) method); return true; } } public override void Emit (EmitContext ec) { DoEmit (ec, true); } public override void EmitStatement (EmitContext ec) { if (DoEmit (ec, false)) ec.ig.Emit (OpCodes.Pop); } } /// /// Represents an array creation expression. /// /// /// /// There are two possible scenarios here: one is an array creation /// expression that specifies the dimensions and optionally the /// initialization data and the other which does not need dimensions /// specified but where initialization data is mandatory. /// public class ArrayCreation : ExpressionStatement { string RequestedType; string Rank; ArrayList Initializers; Location loc; ArrayList Arguments; MethodBase method = null; Type array_element_type; bool IsOneDimensional = false; bool IsBuiltinType = false; bool ExpectInitializers = false; int dimensions = 0; Type underlying_type; ArrayList ArrayData; Hashtable Bounds; // // The number of array initializers that we can handle // via the InitializeArray method - through EmitStaticInitializers // int num_automatic_initializers; public ArrayCreation (string requested_type, ArrayList exprs, string rank, ArrayList initializers, Location l) { RequestedType = requested_type; Rank = rank; Initializers = initializers; loc = l; Arguments = new ArrayList (); foreach (Expression e in exprs) Arguments.Add (new Argument (e, Argument.AType.Expression)); } public ArrayCreation (string requested_type, string rank, ArrayList initializers, Location l) { RequestedType = requested_type; Initializers = initializers; loc = l; Rank = rank.Substring (0, rank.LastIndexOf ("[")); string tmp = rank.Substring (rank.LastIndexOf ("[")); dimensions = tmp.Length - 1; ExpectInitializers = true; } public static string FormArrayType (string base_type, int idx_count, string rank) { StringBuilder sb = new StringBuilder (base_type); sb.Append (rank); sb.Append ("["); for (int i = 1; i < idx_count; i++) sb.Append (","); sb.Append ("]"); return sb.ToString (); } public static string FormElementType (string base_type, int idx_count, string rank) { StringBuilder sb = new StringBuilder (base_type); sb.Append ("["); for (int i = 1; i < idx_count; i++) sb.Append (","); sb.Append ("]"); sb.Append (rank); string val = sb.ToString (); return val.Substring (0, val.LastIndexOf ("[")); } void error178 () { Report.Error (178, loc, "Incorrectly structured array initializer"); } public bool CheckIndices (EmitContext ec, ArrayList probe, int idx, bool specified_dims) { if (specified_dims) { Argument a = (Argument) Arguments [idx]; if (!a.Resolve (ec, loc)) return false; if (!(a.Expr is Constant)) { Report.Error (150, loc, "A constant value is expected"); return false; } int value = (int) ((Constant) a.Expr).GetValue (); if (value != probe.Count) { error178 (); return false; } Bounds [idx] = value; } foreach (object o in probe) { if (o is ArrayList) { bool ret = CheckIndices (ec, (ArrayList) o, idx + 1, specified_dims); if (!ret) return false; } else { Expression tmp = (Expression) o; tmp = tmp.Resolve (ec); if (tmp == null) continue; // Handle initialization from vars, fields etc. Expression conv = ConvertImplicitRequired ( ec, tmp, underlying_type, loc); if (conv == null) return false; if (conv is StringConstant) ArrayData.Add (conv); else if (conv is Constant) { ArrayData.Add (conv); num_automatic_initializers++; } else ArrayData.Add (conv); } } return true; } public void UpdateIndices (EmitContext ec) { int i = 0; for (ArrayList probe = Initializers; probe != null;) { if (probe.Count > 0 && probe [0] is ArrayList) { Expression e = new IntConstant (probe.Count); Arguments.Add (new Argument (e, Argument.AType.Expression)); Bounds [i++] = probe.Count; probe = (ArrayList) probe [0]; } else { Expression e = new IntConstant (probe.Count); Arguments.Add (new Argument (e, Argument.AType.Expression)); Bounds [i++] = probe.Count; probe = null; } } } public bool ValidateInitializers (EmitContext ec) { if (Initializers == null) { if (ExpectInitializers) return false; else return true; } underlying_type = RootContext.LookupType ( ec.TypeContainer, RequestedType, false, loc); // // We use this to store all the date values in the order in which we // will need to store them in the byte blob later // ArrayData = new ArrayList (); Bounds = new Hashtable (); bool ret; if (Arguments != null) { ret = CheckIndices (ec, Initializers, 0, true); return ret; } else { Arguments = new ArrayList (); ret = CheckIndices (ec, Initializers, 0, false); if (!ret) return false; UpdateIndices (ec); if (Arguments.Count != dimensions) { error178 (); return false; } return ret; } } public override Expression DoResolve (EmitContext ec) { int arg_count; // // First step is to validate the initializers and fill // in any missing bits // if (!ValidateInitializers (ec)) return null; if (Arguments == null) arg_count = 0; else { arg_count = Arguments.Count; for (int i = arg_count; i > 0;){ --i; Argument a = (Argument) Arguments [i]; if (!a.Resolve (ec, loc)) return null; } } string array_type = FormArrayType (RequestedType, arg_count, Rank); string element_type = FormElementType (RequestedType, arg_count, Rank); type = RootContext.LookupType (ec.TypeContainer, array_type, false, loc); array_element_type = RootContext.LookupType ( ec.TypeContainer, element_type, false, loc); if (type == null) return null; if (arg_count == 1) { IsOneDimensional = true; eclass = ExprClass.Value; return this; } IsBuiltinType = TypeManager.IsBuiltinType (type); if (IsBuiltinType) { Expression ml; ml = MemberLookup (ec, type, ".ctor", MemberTypes.Constructor, AllBindingFlags, loc); if (!(ml is MethodGroupExpr)){ report118 (loc, ml, "method group"); return null; } if (ml == null) { Report.Error (-6, loc, "New invocation: Can not find a constructor for " + "this argument list"); return null; } method = Invocation.OverloadResolve (ec, (MethodGroupExpr) ml, Arguments, loc); if (method == null) { Report.Error (-6, loc, "New invocation: Can not find a constructor for " + "this argument list"); return null; } eclass = ExprClass.Value; return this; } else { ModuleBuilder mb = RootContext.ModuleBuilder; ArrayList args = new ArrayList (); if (Arguments != null){ for (int i = arg_count; i > 0;){ --i; Argument a = (Argument) Arguments [i]; args.Add (a.Type); } } Type [] arg_types = null; if (args.Count > 0) arg_types = new Type [args.Count]; args.CopyTo (arg_types, 0); method = mb.GetArrayMethod (type, ".ctor", CallingConventions.HasThis, null, arg_types); if (method == null) { Report.Error (-6, loc, "New invocation: Can not find a constructor for " + "this argument list"); return null; } eclass = ExprClass.Value; return this; } } public static byte [] MakeByteBlob (ArrayList ArrayData, Type underlying_type, Location loc) { int factor; byte [] data; int count = ArrayData.Count; if (underlying_type == TypeManager.int32_type || underlying_type == TypeManager.uint32_type || underlying_type == TypeManager.float_type) factor = 4; else if (underlying_type == TypeManager.int64_type || underlying_type == TypeManager.uint64_type || underlying_type == TypeManager.double_type) factor = 8; else if (underlying_type == TypeManager.byte_type || underlying_type == TypeManager.sbyte_type || underlying_type == TypeManager.bool_type) factor = 1; else if (underlying_type == TypeManager.short_type || underlying_type == TypeManager.char_type || underlying_type == TypeManager.ushort_type) factor = 2; else return null; data = new byte [(count * factor + 4) & ~3]; int idx = 0; for (int i = 0; i < count; ++i) { object v = ArrayData [i]; if (v is EnumConstant) v = ((EnumConstant) v).Child; if (v is Constant && !(v is StringConstant)) v = ((Constant) v).GetValue (); else { idx += factor; continue; } if (underlying_type == TypeManager.int64_type){ if (!(v is Expression)){ long val = (long) v; for (int j = 0; j < factor; ++j) { data [idx + j] = (byte) (val & 0xFF); val = (val >> 8); } } } else if (underlying_type == TypeManager.uint64_type){ if (!(v is Expression)){ ulong val = (ulong) v; for (int j = 0; j < factor; ++j) { data [idx + j] = (byte) (val & 0xFF); val = (val >> 8); } } } else if (underlying_type == TypeManager.float_type) { #if __MonoCS__ #else unsafe { if (!(v is Expression)){ float val = (float) v; byte *ptr = (byte *) &val; for (int j = 0; j < factor; ++j) data [idx + j] = (byte) ptr [j]; } } #endif } else if (underlying_type == TypeManager.double_type) { #if __MonoCS__ #else unsafe { if (!(v is Expression)){ double val = (double) v; byte *ptr = (byte *) &val; for (int j = 0; j < factor; ++j) data [idx + j] = (byte) ptr [j]; } } #endif } else if (underlying_type == TypeManager.char_type){ if (!(v is Expression)){ int val = (int) ((char) v); data [idx] = (byte) (val & 0xff); data [idx+1] = (byte) (val >> 8); } } else if (underlying_type == TypeManager.short_type){ if (!(v is Expression)){ int val = (int) ((short) v); data [idx] = (byte) (val & 0xff); data [idx+1] = (byte) (val >> 8); } } else if (underlying_type == TypeManager.ushort_type){ if (!(v is Expression)){ int val = (int) ((ushort) v); data [idx] = (byte) (val & 0xff); data [idx+1] = (byte) (val >> 8); } } else if (underlying_type == TypeManager.int32_type) { if (!(v is Expression)){ int val = (int) v; data [idx] = (byte) (val & 0xff); data [idx+1] = (byte) ((val >> 8) & 0xff); data [idx+2] = (byte) ((val >> 16) & 0xff); data [idx+3] = (byte) (val >> 24); } } else if (underlying_type == TypeManager.uint32_type) { if (!(v is Expression)){ uint val = (uint) v; data [idx] = (byte) (val & 0xff); data [idx+1] = (byte) ((val >> 8) & 0xff); data [idx+2] = (byte) ((val >> 16) & 0xff); data [idx+3] = (byte) (val >> 24); } } else if (underlying_type == TypeManager.sbyte_type) { if (!(v is Expression)){ sbyte val = (sbyte) v; data [idx] = (byte) val; } } else if (underlying_type == TypeManager.byte_type) { if (!(v is Expression)){ byte val = (byte) v; data [idx] = (byte) val; } } else throw new Exception ("Unrecognized type in MakeByteBlob"); idx += factor; } return data; } // // Emits the initializers for the array // void EmitStaticInitializers (EmitContext ec, bool is_expression) { // // First, the static data // FieldBuilder fb; ILGenerator ig = ec.ig; byte [] data = MakeByteBlob (ArrayData, underlying_type, loc); if (data != null) { fb = RootContext.MakeStaticData (data); if (is_expression) ig.Emit (OpCodes.Dup); ig.Emit (OpCodes.Ldtoken, fb); ig.Emit (OpCodes.Call, TypeManager.void_initializearray_array_fieldhandle); } } // // Emits pieces of the array that can not be computed at compile // time (variables and string locations). // // This always expect the top value on the stack to be the array // void EmitDynamicInitializers (EmitContext ec, bool is_expression) { ILGenerator ig = ec.ig; int dims = Bounds.Count; int [] current_pos = new int [dims]; int top = ArrayData.Count; LocalBuilder temp = ig.DeclareLocal (type); ig.Emit (OpCodes.Stloc, temp); MethodInfo set = null; if (dims != 1){ Type [] args; ModuleBuilder mb = null; mb = RootContext.ModuleBuilder; args = new Type [dims + 1]; int j; for (j = 0; j < dims; j++) args [j] = TypeManager.int32_type; args [j] = array_element_type; set = mb.GetArrayMethod ( type, "Set", CallingConventions.HasThis | CallingConventions.Standard, TypeManager.void_type, args); } for (int i = 0; i < top; i++){ Expression e = null; if (ArrayData [i] is Expression) e = (Expression) ArrayData [i]; if (e != null) { // // Basically we do this for string literals and // other non-literal expressions // if (e is StringConstant || !(e is Constant) || num_automatic_initializers <= 2) { ig.Emit (OpCodes.Ldloc, temp); for (int idx = dims; idx > 0; ) { idx--; IntConstant.EmitInt (ig, current_pos [idx]); } e.Emit (ec); if (dims == 1) ArrayAccess.EmitStoreOpcode (ig, array_element_type); else ig.Emit (OpCodes.Call, set); } } // // Advance counter // for (int j = 0; j < dims; j++){ current_pos [j]++; if (current_pos [j] < (int) Bounds [j]) break; current_pos [j] = 0; } } if (is_expression) ig.Emit (OpCodes.Ldloc, temp); } void DoEmit (EmitContext ec, bool is_statement) { ILGenerator ig = ec.ig; if (IsOneDimensional) { Invocation.EmitArguments (ec, null, Arguments); ig.Emit (OpCodes.Newarr, array_element_type); } else { Invocation.EmitArguments (ec, null, Arguments); if (IsBuiltinType) ig.Emit (OpCodes.Newobj, (ConstructorInfo) method); else ig.Emit (OpCodes.Newobj, (MethodInfo) method); } if (Initializers != null){ // // FIXME: Set this variable correctly. // bool dynamic_initializers = true; if (underlying_type != TypeManager.string_type && underlying_type != TypeManager.object_type) { if (num_automatic_initializers > 2) EmitStaticInitializers (ec, dynamic_initializers || !is_statement); } if (dynamic_initializers) EmitDynamicInitializers (ec, !is_statement); } } public override void Emit (EmitContext ec) { DoEmit (ec, false); } public override void EmitStatement (EmitContext ec) { DoEmit (ec, true); } } /// /// Represents the `this' construct /// public class This : Expression, IAssignMethod, IMemoryLocation { Location loc; public This (Location loc) { this.loc = loc; } public override Expression DoResolve (EmitContext ec) { eclass = ExprClass.Variable; type = ec.TypeContainer.TypeBuilder; if (ec.IsStatic){ Report.Error (26, loc, "Keyword this not valid in static code"); return null; } return this; } override public Expression DoResolveLValue (EmitContext ec, Expression right_side) { DoResolve (ec); if (ec.TypeContainer is Class){ Report.Error (1604, loc, "Cannot assign to `this'"); return null; } return this; } public override void Emit (EmitContext ec) { ec.ig.Emit (OpCodes.Ldarg_0); } public void EmitAssign (EmitContext ec, Expression source) { source.Emit (ec); ec.ig.Emit (OpCodes.Starg, 0); } public void AddressOf (EmitContext ec) { ec.ig.Emit (OpCodes.Ldarg_0); // FIMXE // FIGURE OUT WHY LDARG_S does not work // // consider: struct X { int val; int P { set { val = value; }}} // // Yes, this looks very bad. Look at `NOTAS' for // an explanation. // ec.ig.Emit (OpCodes.Ldarga_S, (byte) 0); } } /// /// Implements the typeof operator /// public class TypeOf : Expression { public readonly string QueriedType; Type typearg; Location loc; public TypeOf (string queried_type, Location l) { QueriedType = queried_type; loc = l; } public override Expression DoResolve (EmitContext ec) { typearg = RootContext.LookupType ( ec.TypeContainer, QueriedType, false, loc); if (typearg == null) return null; type = TypeManager.type_type; eclass = ExprClass.Type; return this; } public override void Emit (EmitContext ec) { ec.ig.Emit (OpCodes.Ldtoken, typearg); ec.ig.Emit (OpCodes.Call, TypeManager.system_type_get_type_from_handle); } } /// /// Implements the sizeof expression /// public class SizeOf : Expression { public readonly string QueriedType; public SizeOf (string queried_type) { this.QueriedType = queried_type; } public override Expression DoResolve (EmitContext ec) { // FIXME: Implement; throw new Exception ("Unimplemented"); // return this; } public override void Emit (EmitContext ec) { throw new Exception ("Implement me"); } } /// /// Implements the member access expression /// public class MemberAccess : Expression { public readonly string Identifier; Expression expr; Expression member_lookup; Location loc; public MemberAccess (Expression expr, string id, Location l) { this.expr = expr; Identifier = id; loc = l; } public Expression Expr { get { return expr; } } static void error176 (Location loc, string name) { Report.Error (176, loc, "Static member `" + name + "' cannot be accessed " + "with an instance reference, qualify with a " + "type name instead"); } static bool IdenticalNameAndTypeName (EmitContext ec, Expression left_original, Location loc) { if (left_original == null) return false; if (!(left_original is SimpleName)) return false; SimpleName sn = (SimpleName) left_original; Type t = RootContext.LookupType (ec.TypeContainer, sn.Name, true, loc); if (t != null) return true; return false; } public static Expression ResolveMemberAccess (EmitContext ec, Expression member_lookup, Expression left, Location loc, Expression left_original) { // // Method Groups // if (member_lookup is MethodGroupExpr){ MethodGroupExpr mg = (MethodGroupExpr) member_lookup; // // Type.MethodGroup // if (left is TypeExpr){ if (!mg.RemoveInstanceMethods ()){ SimpleName.Error120 (loc, mg.Methods [0].Name); return null; } return member_lookup; } // // Instance.MethodGroup // if (!mg.RemoveStaticMethods ()){ if (IdenticalNameAndTypeName (ec, left_original, loc)){ if (!mg.RemoveInstanceMethods ()){ SimpleName.Error120 (loc, mg.Methods [0].Name); return null; } return member_lookup; } error176 (loc, mg.Methods [0].Name); return null; } mg.InstanceExpression = left; return member_lookup; } if (member_lookup is FieldExpr){ FieldExpr fe = (FieldExpr) member_lookup; FieldInfo fi = fe.FieldInfo; if (fi is FieldBuilder) { Const c = TypeManager.LookupConstant ((FieldBuilder) fi); if (c != null) { object o = c.LookupConstantValue (ec); object real_value = ((Constant) c.Expr).GetValue (); return Constantify (real_value, fi.FieldType); } } if (fi.IsLiteral) { Type t = fi.FieldType; Type decl_type = fi.DeclaringType; object o; if (fi is FieldBuilder) o = TypeManager.GetValue ((FieldBuilder) fi); else o = fi.GetValue (fi); if (decl_type.IsSubclassOf (TypeManager.enum_type)) { Expression enum_member = MemberLookup ( ec, decl_type, "value__", loc); Enum en = TypeManager.LookupEnum (decl_type); Constant c; if (en != null) c = Constantify (o, en.UnderlyingType); else c = Constantify (o, enum_member.Type); return new EnumConstant (c, decl_type); } Expression exp = Constantify (o, t); if (!(left is TypeExpr)) { error176 (loc, fe.FieldInfo.Name); return null; } return exp; } if (left is TypeExpr){ // and refers to a type name or an if (!fe.FieldInfo.IsStatic){ error176 (loc, fe.FieldInfo.Name); return null; } return member_lookup; } else { if (fe.FieldInfo.IsStatic){ if (IdenticalNameAndTypeName (ec, left_original, loc)) return member_lookup; error176 (loc, fe.FieldInfo.Name); return null; } fe.InstanceExpression = left; return fe; } } if (member_lookup is PropertyExpr){ PropertyExpr pe = (PropertyExpr) member_lookup; if (left is TypeExpr){ if (!pe.IsStatic){ SimpleName.Error120 (loc, pe.PropertyInfo.Name); return null; } return pe; } else { if (pe.IsStatic){ if (IdenticalNameAndTypeName (ec, left_original, loc)) return member_lookup; error176 (loc, pe.PropertyInfo.Name); return null; } pe.InstanceExpression = left; return pe; } } if (member_lookup is EventExpr) { EventExpr ee = (EventExpr) member_lookup; // // If the event is local to this class, we transform ourselves into // a FieldExpr // Expression ml = MemberLookup ( ec, ec.TypeContainer.TypeBuilder, ee.EventInfo.Name, MemberTypes.Event, AllBindingFlags, loc); if (ml != null) { MemberInfo mi = ec.TypeContainer.GetFieldFromEvent ((EventExpr) ml); if (mi == null) { // // If this happens, then we have an event with its own // accessors and private field etc so there's no need // to transform ourselves : we should instead flag an error // Assign.error70 (ee.EventInfo, loc); return null; } ml = ExprClassFromMemberInfo (ec, mi, loc); if (ml == null) { Report.Error (-200, loc, "Internal error!!"); return null; } return ResolveMemberAccess (ec, ml, left, loc, left_original); } if (left is TypeExpr) { if (!ee.IsStatic) { SimpleName.Error120 (loc, ee.EventInfo.Name); return null; } return ee; } else { if (ee.IsStatic) { if (IdenticalNameAndTypeName (ec, left_original, loc)) return ee; error176 (loc, ee.EventInfo.Name); return null; } ee.InstanceExpression = left; return ee; } } if (member_lookup is TypeExpr){ member_lookup.Resolve (ec); return member_lookup; } Console.WriteLine ("Left is: " + left); Report.Error (-100, loc, "Support for [" + member_lookup + "] is not present yet"); Environment.Exit (0); return null; } public override Expression DoResolve (EmitContext ec) { // // We are the sole users of ResolveWithSimpleName (ie, the only // ones that can cope with it // Expression original = expr; expr = expr.ResolveWithSimpleName (ec); if (expr == null) return null; if (expr is SimpleName){ SimpleName child_expr = (SimpleName) expr; expr = new SimpleName (child_expr.Name + "." + Identifier, loc); return expr.ResolveWithSimpleName (ec); } // // Handle enums here when they are in transit. // Note that we cannot afford to hit MemberLookup in this case because // it will fail to find any members at all // Type expr_type = expr.Type; if ((expr is TypeExpr) && (expr_type.IsSubclassOf (TypeManager.enum_type))){ Enum en = TypeManager.LookupEnum (expr_type); if (en != null) { object value = en.LookupEnumValue (ec, Identifier, loc); if (value != null){ Constant c = Constantify (value, en.UnderlyingType); return new EnumConstant (c, expr_type); } } } member_lookup = MemberLookup (ec, expr_type, Identifier, loc); if (member_lookup == null) return null; return ResolveMemberAccess (ec, member_lookup, expr, loc, original); } public override void Emit (EmitContext ec) { throw new Exception ("Should not happen"); } } /// /// Implements checked expressions /// public class CheckedExpr : Expression { public Expression Expr; public CheckedExpr (Expression e) { Expr = e; } public override Expression DoResolve (EmitContext ec) { Expr = Expr.Resolve (ec); if (Expr == null) return null; eclass = Expr.eclass; type = Expr.Type; return this; } public override void Emit (EmitContext ec) { bool last_check = ec.CheckState; ec.CheckState = true; Expr.Emit (ec); ec.CheckState = last_check; } } /// /// Implements the unchecked expression /// public class UnCheckedExpr : Expression { public Expression Expr; public UnCheckedExpr (Expression e) { Expr = e; } public override Expression DoResolve (EmitContext ec) { Expr = Expr.Resolve (ec); if (Expr == null) return null; eclass = Expr.eclass; type = Expr.Type; return this; } public override void Emit (EmitContext ec) { bool last_check = ec.CheckState; ec.CheckState = false; Expr.Emit (ec); ec.CheckState = last_check; } } /// /// An Element Access expression. /// /// During semantic analysis these are transformed into /// IndexerAccess or ArrayAccess /// public class ElementAccess : Expression { public ArrayList Arguments; public Expression Expr; public Location loc; public ElementAccess (Expression e, ArrayList e_list, Location l) { Expr = e; loc = l; if (e_list == null) return; Arguments = new ArrayList (); foreach (Expression tmp in e_list) Arguments.Add (new Argument (tmp, Argument.AType.Expression)); } bool CommonResolve (EmitContext ec) { Expr = Expr.Resolve (ec); if (Expr == null) return false; if (Arguments == null) return false; for (int i = Arguments.Count; i > 0;){ --i; Argument a = (Argument) Arguments [i]; if (!a.Resolve (ec, loc)) return false; } return true; } public override Expression DoResolve (EmitContext ec) { if (!CommonResolve (ec)) return null; // // We perform some simple tests, and then to "split" the emit and store // code we create an instance of a different class, and return that. // // I am experimenting with this pattern. // if (Expr.Type.IsSubclassOf (TypeManager.array_type)) return (new ArrayAccess (this)).Resolve (ec); else return (new IndexerAccess (this)).Resolve (ec); } public override Expression DoResolveLValue (EmitContext ec, Expression right_side) { if (!CommonResolve (ec)) return null; if (Expr.Type.IsSubclassOf (TypeManager.array_type)) return (new ArrayAccess (this)).ResolveLValue (ec, right_side); else return (new IndexerAccess (this)).ResolveLValue (ec, right_side); } public override void Emit (EmitContext ec) { throw new Exception ("Should never be reached"); } } /// /// Implements array access /// public class ArrayAccess : Expression, IAssignMethod, IMemoryLocation { // // Points to our "data" repository // ElementAccess ea; public ArrayAccess (ElementAccess ea_data) { ea = ea_data; eclass = ExprClass.Variable; } public override Expression DoResolve (EmitContext ec) { if (ea.Expr.eclass != ExprClass.Variable) { report118 (ea.loc, ea.Expr, "variable"); return null; } Type t = ea.Expr.Type; if (t.GetArrayRank () != ea.Arguments.Count){ Report.Error (22, ea.loc, "Incorrect number of indexes for array " + " expected: " + t.GetArrayRank () + " got: " + ea.Arguments.Count); return null; } type = t.GetElementType (); eclass = ExprClass.Variable; return this; } /// /// Emits the right opcode to load an object of Type `t' /// from an array of T /// static public void EmitLoadOpcode (ILGenerator ig, Type type) { if (type == TypeManager.byte_type || type == TypeManager.bool_type) ig.Emit (OpCodes.Ldelem_I1); else if (type == TypeManager.sbyte_type) ig.Emit (OpCodes.Ldelem_U1); else if (type == TypeManager.short_type) ig.Emit (OpCodes.Ldelem_I2); else if (type == TypeManager.ushort_type) ig.Emit (OpCodes.Ldelem_U2); else if (type == TypeManager.int32_type) ig.Emit (OpCodes.Ldelem_I4); else if (type == TypeManager.uint32_type) ig.Emit (OpCodes.Ldelem_U4); else if (type == TypeManager.uint64_type) ig.Emit (OpCodes.Ldelem_I8); else if (type == TypeManager.int64_type) ig.Emit (OpCodes.Ldelem_I8); else if (type == TypeManager.float_type) ig.Emit (OpCodes.Ldelem_R4); else if (type == TypeManager.double_type) ig.Emit (OpCodes.Ldelem_R8); else if (type == TypeManager.intptr_type) ig.Emit (OpCodes.Ldelem_I); else if (type.IsValueType){ ig.Emit (OpCodes.Ldelema, type); ig.Emit (OpCodes.Ldobj, type); } else ig.Emit (OpCodes.Ldelem_Ref); } /// /// Emits the right opcode to store an object of Type `t' /// from an array of T. /// static public void EmitStoreOpcode (ILGenerator ig, Type t) { if (t == TypeManager.byte_type || t == TypeManager.sbyte_type || t == TypeManager.bool_type) ig.Emit (OpCodes.Stelem_I1); else if (t == TypeManager.short_type || t == TypeManager.ushort_type || t == TypeManager.char_type) ig.Emit (OpCodes.Stelem_I2); else if (t == TypeManager.int32_type || t == TypeManager.uint32_type) ig.Emit (OpCodes.Stelem_I4); else if (t == TypeManager.int64_type || t == TypeManager.uint64_type) ig.Emit (OpCodes.Stelem_I8); else if (t == TypeManager.float_type) ig.Emit (OpCodes.Stelem_R4); else if (t == TypeManager.double_type) ig.Emit (OpCodes.Stelem_R8); else if (t == TypeManager.intptr_type) ig.Emit (OpCodes.Stelem_I); else if (t.IsValueType) ig.Emit (OpCodes.Stobj, t); else ig.Emit (OpCodes.Stelem_Ref); } MethodInfo FetchGetMethod () { ModuleBuilder mb = RootContext.ModuleBuilder; Type [] args = new Type [ea.Arguments.Count]; MethodInfo get; int i = 0; foreach (Argument a in ea.Arguments) args [i++] = a.Type; get = mb.GetArrayMethod ( ea.Expr.Type, "Get", CallingConventions.HasThis | CallingConventions.Standard, type, args); return get; } MethodInfo FetchAddressMethod () { ModuleBuilder mb = RootContext.ModuleBuilder; Type [] args = new Type [ea.Arguments.Count]; MethodInfo address; string ptr_type_name; Type ret_type; int i = 0; ptr_type_name = type.FullName + "&"; ret_type = Type.GetType (ptr_type_name); // // It is a type defined by the source code we are compiling // if (ret_type == null){ ret_type = mb.GetType (ptr_type_name); } foreach (Argument a in ea.Arguments) args [i++] = a.Type; address = mb.GetArrayMethod ( ea.Expr.Type, "Address", CallingConventions.HasThis | CallingConventions.Standard, ret_type, args); return address; } public override void Emit (EmitContext ec) { int rank = ea.Expr.Type.GetArrayRank (); ILGenerator ig = ec.ig; ea.Expr.Emit (ec); foreach (Argument a in ea.Arguments) a.Expr.Emit (ec); if (rank == 1) EmitLoadOpcode (ig, type); else { MethodInfo method; method = FetchGetMethod (); ig.Emit (OpCodes.Call, method); } } public void EmitAssign (EmitContext ec, Expression source) { int rank = ea.Expr.Type.GetArrayRank (); ILGenerator ig = ec.ig; ea.Expr.Emit (ec); foreach (Argument a in ea.Arguments) a.Expr.Emit (ec); Type t = source.Type; // // The stobj opcode used by value types will need // an address on the stack, not really an array/array // pair // if (rank == 1){ if (t.IsValueType && !TypeManager.IsBuiltinType (t)) ig.Emit (OpCodes.Ldelema, t); } source.Emit (ec); if (rank == 1) EmitStoreOpcode (ig, t); else { ModuleBuilder mb = RootContext.ModuleBuilder; Type [] args = new Type [ea.Arguments.Count + 1]; MethodInfo set; int i = 0; foreach (Argument a in ea.Arguments) args [i++] = a.Type; args [i] = type; set = mb.GetArrayMethod ( ea.Expr.Type, "Set", CallingConventions.HasThis | CallingConventions.Standard, TypeManager.void_type, args); ig.Emit (OpCodes.Call, set); } } public void AddressOf (EmitContext ec) { int rank = ea.Expr.Type.GetArrayRank (); ILGenerator ig = ec.ig; ea.Expr.Emit (ec); foreach (Argument a in ea.Arguments) a.Expr.Emit (ec); if (rank == 1){ ig.Emit (OpCodes.Ldelema, type); } else { MethodInfo address = FetchAddressMethod (); ig.Emit (OpCodes.Call, address); } } } class Indexers { public ArrayList getters, setters; static Hashtable map; static Indexers () { map = new Hashtable (); } Indexers (MemberInfo [] mi) { foreach (PropertyInfo property in mi){ MethodInfo get, set; get = property.GetGetMethod (true); if (get != null){ if (getters == null) getters = new ArrayList (); getters.Add (get); } set = property.GetSetMethod (true); if (set != null){ if (setters == null) setters = new ArrayList (); setters.Add (set); } } } static public Indexers GetIndexersForType (Type t, TypeManager tm, Location loc) { Indexers ix = (Indexers) map [t]; string p_name = TypeManager.IndexerPropertyName (t); if (ix != null) return ix; MemberInfo [] mi = tm.FindMembers ( t, MemberTypes.Property, BindingFlags.Public | BindingFlags.Instance, Type.FilterName, p_name); if (mi == null || mi.Length == 0){ Report.Error (21, loc, "Type `" + TypeManager.CSharpName (t) + "' does not have " + "any indexers defined"); return null; } ix = new Indexers (mi); map [t] = ix; return ix; } } /// /// Expressions that represent an indexer call. /// public class IndexerAccess : Expression, IAssignMethod { // // Points to our "data" repository // ElementAccess ea; MethodInfo get, set; Indexers ilist; ArrayList set_arguments; public IndexerAccess (ElementAccess ea_data) { ea = ea_data; eclass = ExprClass.Value; } public override Expression DoResolve (EmitContext ec) { Type indexer_type = ea.Expr.Type; // // Step 1: Query for all `Item' *properties*. Notice // that the actual methods are pointed from here. // // This is a group of properties, piles of them. if (ilist == null) ilist = Indexers.GetIndexersForType ( indexer_type, RootContext.TypeManager, ea.loc); // // Step 2: find the proper match // if (ilist != null && ilist.getters != null && ilist.getters.Count > 0) get = (MethodInfo) Invocation.OverloadResolve ( ec, new MethodGroupExpr (ilist.getters), ea.Arguments, ea.loc); if (get == null){ Report.Error (154, ea.loc, "indexer can not be used in this context, because " + "it lacks a `get' accessor"); return null; } type = get.ReturnType; eclass = ExprClass.IndexerAccess; return this; } public override Expression DoResolveLValue (EmitContext ec, Expression right_side) { Type indexer_type = ea.Expr.Type; Type right_type = right_side.Type; if (ilist == null) ilist = Indexers.GetIndexersForType ( indexer_type, RootContext.TypeManager, ea.loc); if (ilist != null && ilist.setters != null && ilist.setters.Count > 0){ set_arguments = (ArrayList) ea.Arguments.Clone (); set_arguments.Add (new Argument (right_side, Argument.AType.Expression)); set = (MethodInfo) Invocation.OverloadResolve ( ec, new MethodGroupExpr (ilist.setters), set_arguments, ea.loc); } if (set == null){ Report.Error (200, ea.loc, "indexer X.this [" + TypeManager.CSharpName (right_type) + "] lacks a `set' accessor"); return null; } type = TypeManager.void_type; eclass = ExprClass.IndexerAccess; return this; } public override void Emit (EmitContext ec) { Invocation.EmitCall (ec, false, false, ea.Expr, get, ea.Arguments); } // // source is ignored, because we already have a copy of it from the // LValue resolution and we have already constructed a pre-cached // version of the arguments (ea.set_arguments); // public void EmitAssign (EmitContext ec, Expression source) { Invocation.EmitCall (ec, false, false, ea.Expr, set, set_arguments); } } /// /// The base operator for method names /// public class BaseAccess : Expression { string member; Location loc; public BaseAccess (string member, Location l) { this.member = member; loc = l; } public override Expression DoResolve (EmitContext ec) { Expression member_lookup; Type current_type = ec.TypeContainer.TypeBuilder; Type base_type = current_type.BaseType; Expression e; if (ec.IsStatic){ Report.Error (1511, loc, "Keyword base is not allowed in static method"); return null; } member_lookup = MemberLookup (ec, base_type, member, loc); if (member_lookup == null) return null; Expression left; if (ec.IsStatic) left = new TypeExpr (base_type); else left = ec.This; e = MemberAccess.ResolveMemberAccess (ec, member_lookup, left, loc, null); if (e is PropertyExpr){ PropertyExpr pe = (PropertyExpr) e; pe.IsBase = true; } return e; } public override void Emit (EmitContext ec) { throw new Exception ("Should never be called"); } } /// /// The base indexer operator /// public class BaseIndexerAccess : Expression { ArrayList Arguments; Location loc; public BaseIndexerAccess (ArrayList args, Location l) { Arguments = args; loc = l; } public override Expression DoResolve (EmitContext ec) { Type current_type = ec.TypeContainer.TypeBuilder; Type base_type = current_type.BaseType; Expression member_lookup; if (ec.IsStatic){ Report.Error (1511, loc, "Keyword base is not allowed in static method"); return null; } member_lookup = MemberLookup (ec, base_type, "get_Item", loc); if (member_lookup == null) return null; return MemberAccess.ResolveMemberAccess (ec, member_lookup, ec.This, loc, null); } public override void Emit (EmitContext ec) { throw new Exception ("Should never be called"); } } /// /// This class exists solely to pass the Type around and to be a dummy /// that can be passed to the conversion functions (this is used by /// foreach implementation to typecast the object return value from /// get_Current into the proper type. All code has been generated and /// we only care about the side effect conversions to be performed /// public class EmptyExpression : Expression { public EmptyExpression () { type = TypeManager.object_type; eclass = ExprClass.Value; } public EmptyExpression (Type t) { type = t; eclass = ExprClass.Value; } public override Expression DoResolve (EmitContext ec) { return this; } public override void Emit (EmitContext ec) { // nothing, as we only exist to not do anything. } // // This is just because we might want to reuse this bad boy // instead of creating gazillions of EmptyExpressions. // (CanConvertImplicit uses it) // public void SetType (Type t) { type = t; } } public class UserCast : Expression { MethodBase method; Expression source; public UserCast (MethodInfo method, Expression source) { this.method = method; this.source = source; type = method.ReturnType; eclass = ExprClass.Value; } public override Expression DoResolve (EmitContext ec) { // // We are born fully resolved // return this; } public override void Emit (EmitContext ec) { ILGenerator ig = ec.ig; source.Emit (ec); if (method is MethodInfo) ig.Emit (OpCodes.Call, (MethodInfo) method); else ig.Emit (OpCodes.Call, (ConstructorInfo) method); } } // // This class is used to "construct" the type during a typecast // operation. Since the Type.GetType class in .NET can parse // the type specification, we just use this to construct the type // one bit at a time. // public class ComposedCast : Expression { Expression left; string dim; Location loc; public ComposedCast (Expression left, string dim, Location l) { this.left = left; this.dim = dim; loc = l; } public override Expression DoResolve (EmitContext ec) { left = left.Resolve (ec); if (left == null) return null; if (left.eclass != ExprClass.Type){ report118 (loc, left, "type"); return null; } type = RootContext.LookupType ( ec.TypeContainer, left.Type.FullName + dim, false, loc); if (type == null) return null; eclass = ExprClass.Type; return this; } public override void Emit (EmitContext ec) { throw new Exception ("This should never be called"); } } }