// // expression.cs: Expression representation for the IL tree. // // Author: // Miguel de Icaza (miguel@ximian.com) // // (C) 2001 Ximian, Inc. // // 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, } 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 (); } void error23 (Type t) { Report.Error ( 23, loc, "Operator " + OperName () + " cannot be applied to operand of type `" + TypeManager.CSharpName (t) + "'"); } static Expression TryReduceNegative (Expression expr) { Expression e = null; if (expr is IntLiteral) e = new IntLiteral (-((IntLiteral) expr).Value); else if (expr is UIntLiteral) e = new LongLiteral (-((UIntLiteral) expr).Value); else if (expr is LongLiteral) e = new LongLiteral (-((LongLiteral) expr).Value); else if (expr is FloatLiteral) e = new FloatLiteral (-((FloatLiteral) expr).Value); else if (expr is DoubleLiteral) e = new DoubleLiteral (-((DoubleLiteral) expr).Value); else if (expr is DecimalLiteral) e = new DecimalLiteral (-((DecimalLiteral) expr).Value); return e; } Expression ResolveOperator (EmitContext ec) { Type expr_type = expr.Type; // // Step 1: Perform Operator Overload location // Expression mg; string op_name; op_name = "op_" + oper; mg = MemberLookup (ec, expr_type, op_name, false, loc); if (mg == null && expr_type.BaseType != null) mg = MemberLookup (ec, expr_type.BaseType, op_name, false, loc); if (mg != null) { Expression e = StaticCallExpr.MakeSimpleCall ( ec, (MethodGroupExpr) mg, expr, loc); if (e == null){ error23 (expr_type); return null; } return e; } // // Step 2: Default operations on CLI native types. // // Only perform numeric promotions on: // +, - if (expr_type == null) return null; 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){ // // Fold a "- Constant" into a negative constant // Expression e = null; // // Is this a constant? // e = TryReduceNegative (expr); if (e != null){ e = e.Resolve (ec); return e; } // // Not a constant we can optimize, 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 written 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 written as // decimal integer literal. // error23 (expr_type); return null; } 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.ExprClass != ExprClass.Variable){ Error (211, loc, "Cannot take the address of non-variables"); 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); } public override Expression Reduce (EmitContext ec) { Expression e; // // First, reduce our child. Note that although we handle // expr = expr.Reduce (ec); if (!(expr is Literal)) return expr; switch (oper){ case Operator.UnaryPlus: return expr; case Operator.UnaryNegation: e = TryReduceNegative (expr); if (e == null) break; return e; case Operator.LogicalNot: BoolLiteral b = (BoolLiteral) expr; return new BoolLiteral (!(b.Value)); case Operator.OnesComplement: Type et = expr.Type; if (et == TypeManager.int32_type) return new IntLiteral (~ ((IntLiteral) expr).Value); if (et == TypeManager.uint32_type) return new UIntLiteral (~ ((UIntLiteral) expr).Value); if (et == TypeManager.int64_type) return new LongLiteral (~ ((LongLiteral) expr).Value); if (et == TypeManager.uint64_type) return new ULongLiteral (~ ((ULongLiteral) expr).Value); break; } return this; } } /// /// 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, false, loc); if (mg == null && expr_type.BaseType != null) mg = MemberLookup (ec, expr_type.BaseType, op_name, false, 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.ExprClass == ExprClass.Variable){ if (IsIncrementableNumber (expr_type) || expr_type == TypeManager.decimal_type){ return this; } } else if (expr.ExprClass == 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.ExprClass == 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; 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); 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); 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; public Probe (Expression expr, string probe_type) { ProbeType = probe_type; this.expr = expr; } public Expression Expr { get { return expr; } } public override Expression DoResolve (EmitContext ec) { probe_type = ec.TypeContainer.LookupType (ProbeType, false); 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) : base (expr, probe_type) { } 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) : base (expr, probe_type) { } 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; } } 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.ExprClass != ExprClass.Type){ report118 (loc, target_type, "class"); return null; } type = target_type.Type; eclass = ExprClass.Value; if (type == null) return null; 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; 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 && right is IntLiteral){ e = TryImplicitIntConversion (l, (IntLiteral) right); if (e != null) right = e; } other = right.Type; } else { if (left is IntLiteral){ e = TryImplicitIntConversion (r, (IntLiteral) left); 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) other = r; else if (r == TypeManager.uint32_type) 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, false, loc); if (left_expr == null && l.BaseType != null) left_expr = MemberLookup (ec, l.BaseType, op, false, loc); right_expr = MemberLookup (ec, r, op, false, loc); if (right_expr == null && r.BaseType != null) right_expr = MemberLookup (ec, r.BaseType, op, false, 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 Literal && right is Literal){ StringLiteral ls = (StringLiteral) left; StringLiteral rs = (StringLiteral) right; return new StringLiteral (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; } // // FIXME: is Delegate operator + (D x, D y) handled? // } 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; } } // // We are dealing with numbers // if (!DoNumericPromotions (ec, l, r)){ // Attempt: // // operator != (object a, object b) // operator == (object a, object b) // if (oper == Operator.Equality || oper == Operator.Inequality){ Expression li, ri; li = ConvertImplicit (ec, left, TypeManager.object_type, loc); if (li != null){ ri = ConvertImplicit (ec, right, TypeManager.object_type, loc); if (ri != null){ left = li; right = ri; type = TypeManager.bool_type; return this; } } } 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.IsSubclassOf (TypeManager.enum_type) || !((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) { if (left is StringLiteral && right is StringLiteral){ StringLiteral ls = (StringLiteral) left; StringLiteral rs = (StringLiteral) right; return new StringLiteral (ls.Value + rs.Value); } 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; // // We always need to reduce on Binary operators, as we need to do // constant folding // left = left.Reduce (ec); right = right.Reduce (ec); 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); 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 BoolLiteral){ BoolLiteral bl = (BoolLiteral) expr; if (bl.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); } public override Expression Reduce (EmitContext ec) { expr = expr.Reduce (ec); trueExpr = trueExpr.Reduce (ec); falseExpr = falseExpr.Reduce (ec); if (!(expr is Literal && trueExpr is Literal && falseExpr is Literal)) return this; BoolLiteral bl = (BoolLiteral) expr; if (bl.Value) return trueExpr; else return falseExpr; } } /// /// 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; e = Expression.Reduce (ec, e); if (!(e is Literal)) { 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; 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; } 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) 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) 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.ExprClass != ExprClass.Variable){ Report.Error (206, loc, "A property or indexer can not be passed as an out or ref " + "parameter"); return false; } return expr != null; } public void Emit (EmitContext ec) { if (ArgType == AType.Ref || ArgType == AType.Out) ((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; static Hashtable method_parameter_cache; static Invocation () { method_parameter_cache = new Hashtable (); } // // 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; } } /// /// Tells whether a user defined conversion from Type `from' to /// Type `to' exists. /// /// FIXME: we could implement a cache here. /// static bool ConversionExists (EmitContext ec, Type from, Type to, Location loc) { // Locate user-defined implicit operators Expression mg; mg = MemberLookup (ec, to, "op_Implicit", false, loc); if (mg != null) { MethodGroupExpr me = (MethodGroupExpr) mg; for (int i = me.Methods.Length; i > 0;) { i--; MethodBase mb = me.Methods [i]; ParameterData pd = GetParameterData (mb); if (from == pd.ParameterType (0)) return true; } } mg = MemberLookup (ec, from, "op_Implicit", false, loc); if (mg != null) { MethodGroupExpr me = (MethodGroupExpr) mg; for (int i = me.Methods.Length; i > 0;) { i--; MethodBase mb = me.Methods [i]; MethodInfo mi = (MethodInfo) mb; if (mi.ReturnType == to) return true; } } return false; } /// /// 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, bool use_standard, 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 IntLiteral){ IntLiteral ei = (IntLiteral) 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 LongLiteral){ LongLiteral ll = (LongLiteral) argument_expr; if (p == TypeManager.uint64_type){ if (ll.Value > 0) return 1; } } if (q == null) { Expression tmp; if (use_standard) tmp = ConvertImplicitStandard (ec, argument_expr, p, loc); else tmp = ConvertImplicit (ec, argument_expr, p, loc); if (tmp != null) return 1; else return 0; } if (ConversionExists (ec, p, q, loc) == true && ConversionExists (ec, q, p, loc) == 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 use_standard, 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 (best == null) { if (candidate_pd.Count == argument_count) { int x = 0; for (int j = argument_count; j > 0;) { j--; Argument a = (Argument) args [j]; x = BetterConversion ( ec, a, candidate_pd.ParameterType (j), null, use_standard, loc); if (x <= 0) break; } if (x > 0) return 1; else return 0; } else return 0; } best_pd = GetParameterData (best); if (candidate_pd.Count == argument_count && best_pd.Count == argument_count) { int rating1 = 0, rating2 = 0; for (int j = argument_count; j > 0;) { j--; int x, y; Argument a = (Argument) args [j]; x = BetterConversion (ec, a, candidate_pd.ParameterType (j), best_pd.ParameterType (j), use_standard, loc); y = BetterConversion (ec, a, best_pd.ParameterType (j), candidate_pd.ParameterType (j), use_standard, loc); rating1 += x; rating2 += y; } if (rating1 > rating2) return 1; else return 0; } else return 0; } public static string FullMethodDesc (MethodBase mb) { StringBuilder sb = new StringBuilder (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.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. /// /// use_standard: controls whether OverloadResolve should use the /// ConvertImplicit or ConvertImplicitStandard during overload resolution. /// /// 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, bool use_standard) { 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, use_standard, 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 // if (best_match_idx == -1) { for (int i = me.Methods.Length; i > 0; ) { i--; MethodBase candidate = me.Methods [i]; if (IsParamsMethodApplicable (Arguments, candidate)) { best_match_idx = i; method = me.Methods [best_match_idx]; break; } } } // // 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_count > 1) { Report.Error (121, loc, "Ambiguous call when selecting function due to implicit casts"); return null; } } 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 = candidates.Count; i > 0; ) { i--; MethodBase candidate = (MethodBase) candidates [i]; int x; if (candidate == method) continue; x = BetterFunction (ec, Arguments, method, candidate, use_standard, loc); if (x != 1) { 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); // // Note that we need to compare against the element type // when we have a params method // if (pd.ParameterModifier (pd_count - 1) == Parameter.Modifier.PARAMS) { if (j >= pd_count - 1) parameter_type = pd.ParameterType (pd_count - 1).GetElementType (); } if (a.Type != parameter_type){ Expression conv; if (use_standard) conv = ConvertImplicitStandard ( ec, a_expr, parameter_type, Location.Null); else conv = ConvertImplicit ( 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 (j) != 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 static MethodBase OverloadResolve (EmitContext ec, MethodGroupExpr me, ArrayList Arguments, Location loc) { return OverloadResolve (ec, me, Arguments, loc, false); } public override Expression DoResolve (EmitContext ec) { // // First, resolve the expression that is used to // trigger the invocation // 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)); IntLiteral.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); IntLiteral.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); } } public static void EmitCall (EmitContext ec, bool is_static, Expression instance_expr, MethodBase method, ArrayList Arguments) { ILGenerator ig = ec.ig; bool struct_call = false; if (!is_static){ // // 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)){ struct_call = true; // // 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); } } if (Arguments != null) EmitArguments (ec, method, Arguments); if (is_static || struct_call){ 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, 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 = ec.TypeContainer.LookupType (RequestedType, false); if (type == null) return null; bool IsDelegate = TypeManager.IsDelegateType (type); if (IsDelegate) return (new NewDelegate (type, Arguments, loc)).Resolve (ec); Expression ml; ml = MemberLookup (ec, type, ".ctor", false, MemberTypes.Constructor, AllBindingsFlags, loc); bool is_struct = false; is_struct = type.IsSubclassOf (TypeManager.value_type); 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; } eclass = ExprClass.Value; 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 // // Returns whether a value is left on the stack // bool DoEmit (EmitContext ec, bool need_value_on_stack) { if (method == null){ IMemoryLocation ml = (IMemoryLocation) value_target; ml.AddressOf (ec); } else { Invocation.EmitArguments (ec, method, Arguments); ec.ig.Emit (OpCodes.Newobj, (ConstructorInfo) method); return true; } // // It must be a value type, sanity check // if (value_target != null){ ec.ig.Emit (OpCodes.Initobj, type); if (need_value_on_stack){ value_target.Emit (ec); return true; } return false; } throw new Exception ("No method and no value type"); } 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; 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; Expression e = Expression.Reduce (ec, a.Expr); if (!(e is Literal)) { Report.Error (150, loc, "A constant value is expected"); return false; } int value = (int) ((Literal) e).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; tmp = Expression.Reduce (ec, tmp); // Handle initialization from vars, fields etc. Expression conv = ConvertImplicitRequired (ec, tmp, underlying_type, loc); if (conv == null) return false; if (tmp is StringLiteral) ArrayData.Add (tmp); else if (tmp is Literal) ArrayData.Add (((Literal) tmp).GetValue ()); else ArrayData.Add (tmp); } } return true; } public void UpdateIndices (EmitContext ec) { int i = 0; for (ArrayList probe = Initializers; probe != null;) { if (probe [0] is ArrayList) { Expression e = new IntLiteral (probe.Count); Arguments.Add (new Argument (e, Argument.AType.Expression)); Bounds [i++] = probe.Count; probe = (ArrayList) probe [0]; } else { Expression e = new IntLiteral (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 = ec.TypeContainer.LookupType (RequestedType, false); // // 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; 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 = ec.TypeContainer.LookupType (array_type, false); array_element_type = ec.TypeContainer.LookupType (element_type, false); 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", false, MemberTypes.Constructor, AllBindingsFlags, 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.char_type || underlying_type == TypeManager.bool_type) factor = 1; else if (underlying_type == TypeManager.short_type || underlying_type == TypeManager.ushort_type) factor = 2; else { Report.Error (-100, loc, "Unhandled type in MakeByteBlob!!"); return null; } data = new byte [count * factor]; for (int i = 0; i < count; ++i) { if (underlying_type == TypeManager.int64_type || underlying_type == TypeManager.uint64_type){ long val = 0; if (!(ArrayData [i] is Expression)) val = (long) ArrayData [i]; for (int j = 0; j < factor; ++j) { data [(i * factor) + j] = (byte) (val & 0xFF); val = val >> 8; } } else if (underlying_type == TypeManager.float_type) { unsafe { float val = 0; if (!(ArrayData [i] is Expression)) val = (float) ArrayData [i]; byte *ptr = (byte *) &val; for (int j = 0; j < factor; ++j) data [(i * factor) + j] = (byte) ptr [j]; } } else if (underlying_type == TypeManager.double_type) { unsafe { double val = 0; if (!(ArrayData [i] is Expression)) val = (double) ArrayData [i]; byte *ptr = (byte *) &val; for (int j = 0; j < factor; ++j) data [(i * factor) + j] = (byte) ptr [j]; } } else { int val = 0; if (!(ArrayData [i] is Expression)) val = (int) ArrayData [i]; for (int j = 0; j < factor; ++j) { data [(i * factor) + j] = (byte) (val & 0xFF); val = val >> 8; } } } 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 StringLiteral || !(e is Literal)) { ig.Emit (OpCodes.Ldloc, temp); for (int idx = dims; idx > 0; ) { idx--; IntLiteral.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) 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; public TypeOf (string queried_type) { QueriedType = queried_type; } public override Expression DoResolve (EmitContext ec) { typearg = ec.TypeContainer.LookupType (QueriedType, false); 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"); } public static Expression ResolveMemberAccess (EmitContext ec, Expression member_lookup, Expression left, Location loc) { // // 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 ()){ 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); Expression l = Literalize (o, fi.FieldType); l = l.Resolve (ec); return ((Literal) l); } } 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__", false, loc); Enum en = TypeManager.LookupEnum (decl_type); Expression e; if (en != null) e = Literalize (o, en.UnderlyingType); else e = Literalize (o, enum_member.Type); e.Resolve (ec); return new EnumLiteral (e, decl_type); } Expression exp = Literalize (o, t); exp.Resolve (ec); if (!(left is TypeExpr)) { error176 (loc, fe.FieldInfo.Name); return null; } return exp; } if (left is TypeExpr){ if (!fe.FieldInfo.IsStatic){ error176 (loc, fe.FieldInfo.Name); return null; } return member_lookup; } else { if (fe.FieldInfo.IsStatic){ 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){ error176 (loc, pe.PropertyInfo.Name); return null; } pe.InstanceExpression = left; return pe; } } Console.WriteLine ("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 // 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_type.IsSubclassOf (TypeManager.enum_type)) { Enum en = TypeManager.LookupEnum (expr_type); if (en != null) { object value = en.LookupEnumValue (ec, Identifier, loc); if (value == null) return null; Expression l = Literalize (value, en.UnderlyingType); l = l.Resolve (ec); return new EnumLiteral (l, expr_type); } } member_lookup = MemberLookup (ec, expr.Type, Identifier, false, loc); if (member_lookup == null) return null; return ResolveMemberAccess (ec, member_lookup, expr, loc); } public override void Emit (EmitContext ec) { throw new Exception ("Should not happen I think"); } } /// /// 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.ExprClass; 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.ExprClass; 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 { // // 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.ExprClass != 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) 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 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) ig.Emit (OpCodes.Stelem_I1); else if (t == TypeManager.short_type || t == TypeManager.ushort_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 ig.Emit (OpCodes.Stelem_Ref); } 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 { 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); ig.Emit (OpCodes.Call, get); } } 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); source.Emit (ec); Type t = source.Type; 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); } } } 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, 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, 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; member_lookup = MemberLookup (ec, base_type, member, false, loc); if (member_lookup == null) return null; Expression left; if (ec.IsStatic) left = new TypeExpr (base_type); else left = new This (loc).Resolve (ec); return MemberAccess.ResolveMemberAccess (ec, member_lookup, left, loc); } public override void Emit (EmitContext ec) { throw new Exception ("Should never be called"); } } /// /// The base indexer operator /// public class BaseIndexerAccess : Expression { ArrayList Arguments; public BaseIndexerAccess (ArrayList args) { Arguments = args; } public override Expression DoResolve (EmitContext ec) { // FIXME: Implement; throw new Exception ("Unimplemented"); // return this; } public override void Emit (EmitContext ec) { throw new Exception ("Unimplemented"); } } /// /// 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.ExprClass != ExprClass.Type){ report118 (loc, left, "type"); return null; } type = ec.TypeContainer.LookupType (left.Type.FullName + dim, false); if (type == null) return null; eclass = ExprClass.Type; return this; } public override void Emit (EmitContext ec) { throw new Exception ("This should never be called"); } } }