// // expression.cs: Expression representation for the IL tree. // // Author: // Miguel de Icaza (miguel@ximian.com) // // (C) 2001 Ximian, Inc. // namespace CIR { using System.Collections; using System.Diagnostics; using System; using System.Reflection; using System.Reflection.Emit; // // The ExprClass class contains the is used to pass the // classification of an expression (value, variable, namespace, // type, method group, property access, event access, indexer access, // nothing). // public enum ExprClass { Invalid, Value, Variable, Namespace, Type, MethodGroup, PropertyAccess, EventAccess, IndexerAccess, Nothing, } // // Base class for expressions // public abstract class Expression { protected ExprClass eclass; protected Type type; public Type Type { get { return type; } set { type = value; } } public ExprClass ExprClass { get { return eclass; } set { eclass = value; } } public abstract Expression Resolve (TypeContainer tc); public abstract void Emit (EmitContext ec); // // Protected constructor. Only derivate types should // be able to be created // protected Expression () { eclass = ExprClass.Invalid; type = null; } // // Returns a fully formed expression after a MemberLookup // static Expression ExprClassFromMemberInfo (MemberInfo mi) { if (mi is EventInfo){ return new EventExpr ((EventInfo) mi); } else if (mi is FieldInfo){ return new FieldExpr ((FieldInfo) mi); } else if (mi is PropertyInfo){ return new PropertyExpr ((PropertyInfo) mi); } else if (mi is Type) return new TypeExpr ((Type) mi); return null; } // // FIXME: Probably implement a cache for (t,name,current_access_set)? // // FIXME: We need to cope with access permissions here, or this wont // work! // // This code could use some optimizations, but we need to do some // measurements. For example, we could use a delegate to `flag' when // something can not any longer be a method-group (because it is something // else). // // Return values: // If the return value is an Array, then it is an array of // MethodBases // // If the return value is an MemberInfo, it is anything, but a Method // // null on error. // // FIXME: When calling MemberLookup inside an `Invocation', we should pass // the arguments here and have MemberLookup return only the methods that // match the argument count/type, unlike we are doing now (we delay this // decision). // // This is so we can catch correctly attempts to invoke instance methods // from a static body (scan for error 120 in ResolveSimpleName). // protected static Expression MemberLookup (RootContext rc, Type t, string name, bool same_type) { MemberTypes mt = MemberTypes.Constructor | MemberTypes.Event | MemberTypes.Field | MemberTypes.Method | MemberTypes.NestedType | MemberTypes.Property; BindingFlags bf = BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance; if (same_type) bf |= BindingFlags.NonPublic; MemberInfo [] mi = rc.TypeManager.FindMembers (t, mt, bf, Type.FilterName, name); if (mi == null) return null; if (mi.Length == 1 && !(mi [0] is MethodBase)) return Expression.ExprClassFromMemberInfo (mi [0]); for (int i = 0; i < mi.Length; i++) if (!(mi [i] is MethodBase)){ rc.Report.Error (-5, "Do not know how to reproduce this case: " + "Methods and non-Method with the same name, report this please"); for (i = 0; i < mi.Length; i++){ Type tt = mi [i].GetType (); Console.WriteLine (i + ": " + mi [i]); while (tt != TypeManager.object_type){ Console.WriteLine (tt); tt = tt.BaseType; } } } return new MethodGroupExpr (mi); } // // Resolves the E in `E.I' side for a member_access // // This is suboptimal and should be merged with ResolveMemberAccess static Expression ResolvePrimary (TypeContainer tc, string name) { int dot_pos = name.LastIndexOf ("."); if (tc.RootContext.IsNamespace (name)) return new NamespaceExpr (name); if (dot_pos != -1){ } else { Type t = tc.LookupType (name, false); if (t != null) return new TypeExpr (t); } return null; } static public Expression ResolveMemberAccess (TypeContainer tc, string name) { Expression left_e; int dot_pos = name.LastIndexOf ("."); string left = name.Substring (0, dot_pos); string right = name.Substring (dot_pos + 1); left_e = ResolvePrimary (tc, left); if (left_e == null) return null; switch (left_e.ExprClass){ case ExprClass.Type: return MemberLookup (tc.RootContext, left_e.Type, right, left_e.Type == tc.TypeBuilder); case ExprClass.Namespace: case ExprClass.PropertyAccess: case ExprClass.IndexerAccess: case ExprClass.Variable: case ExprClass.Value: case ExprClass.Nothing: case ExprClass.EventAccess: case ExprClass.MethodGroup: case ExprClass.Invalid: tc.RootContext.Report.Error (-1000, "Internal compiler error, should have " + "got these handled before"); break; } return null; } static public Expression ImplicitReferenceConversion (Expression expr, Type target_type) { Type expr_type = expr.Type; if (target_type == TypeManager.object_type) { if (expr_type.IsClass) return new EmptyCast (expr, target_type); if (expr_type.IsValueType) return new BoxedCast (expr, target_type); } else if (expr_type.IsSubclassOf (target_type)) return new EmptyCast (expr, target_type); else // FIXME: missing implicit reference conversions: // // from any class-type S to any interface-type T. // from any interface type S to interface-type T. // from an array-type S to an array-type of type T // from an array-type to System.Array // from any delegate type to System.Delegate // from any array-type or delegate type into System.ICloneable. // from the null type to any reference-type. return null; return null; } // // Converts implicitly the resolved expression `expr' into the // `target_type'. It returns a new expression that can be used // in a context that expects a `target_type'. // static public Expression ConvertImplicit (Expression expr, Type target_type) { Type expr_type = expr.Type; if (expr_type == target_type){ Console.WriteLine ("Hey, ConvertImplicit was called with no job to do"); return expr; } // // Step 1: Perform implicit conversions as found on expr.Type // // // Step 2: Built-in conversions. // if (expr_type == TypeManager.sbyte_type){ // // From sbyte to short, int, long, float, double. // if (target_type == TypeManager.int32_type) return new OpcodeCast (expr, target_type, OpCodes.Conv_I4); if (target_type == TypeManager.int64_type) return new OpcodeCast (expr, target_type, OpCodes.Conv_U8); if (target_type == TypeManager.double_type) return new OpcodeCast (expr, target_type, OpCodes.Conv_R8); if (target_type == TypeManager.float_type) return new OpcodeCast (expr, target_type, OpCodes.Conv_R4); if (target_type == TypeManager.short_type) return new OpcodeCast (expr, target_type, OpCodes.Conv_I2); } else if (expr_type == TypeManager.byte_type){ // // From byte to short, ushort, int, uint, long, ulong, float, double // if ((target_type == TypeManager.short_type) || (target_type == TypeManager.ushort_type) || (target_type == TypeManager.int32_type) || (target_type == TypeManager.uint32_type)) return new EmptyCast (expr, target_type); if (target_type == TypeManager.uint64_type) return new OpcodeCast (expr, target_type, OpCodes.Conv_U8); if (target_type == TypeManager.int64_type) return new OpcodeCast (expr, target_type, OpCodes.Conv_I8); if (target_type == TypeManager.float_type) return new OpcodeCast (expr, target_type, OpCodes.Conv_R4); if (target_type == TypeManager.double_type) return new OpcodeCast (expr, target_type, OpCodes.Conv_R8); } else if (expr_type == TypeManager.short_type){ // // From short to int, long, float, double // if (target_type == TypeManager.int32_type) return new EmptyCast (expr, target_type); if (target_type == TypeManager.int64_type) return new OpcodeCast (expr, target_type, OpCodes.Conv_I8); if (target_type == TypeManager.double_type) return new OpcodeCast (expr, target_type, OpCodes.Conv_R8); if (target_type == TypeManager.float_type) return new OpcodeCast (expr, target_type, OpCodes.Conv_R4); } else if (expr_type == TypeManager.ushort_type){ // // From ushort to int, uint, long, ulong, float, double // if ((target_type == TypeManager.uint32_type) || (target_type == TypeManager.uint64_type)) return new EmptyCast (expr, target_type); if (target_type == TypeManager.int32_type) return new OpcodeCast (expr, target_type, OpCodes.Conv_I4); if (target_type == TypeManager.int64_type) return new OpcodeCast (expr, target_type, OpCodes.Conv_I8); if (target_type == TypeManager.double_type) return new OpcodeCast (expr, target_type, OpCodes.Conv_R8); if (target_type == TypeManager.float_type) return new OpcodeCast (expr, target_type, OpCodes.Conv_R4); } else if (expr_type == TypeManager.int32_type){ // // From int to long, float, double // if (target_type == TypeManager.int64_type) return new OpcodeCast (expr, target_type, OpCodes.Conv_I8); if (target_type == TypeManager.double_type) return new OpcodeCast (expr, target_type, OpCodes.Conv_R8); if (target_type == TypeManager.float_type) return new OpcodeCast (expr, target_type, OpCodes.Conv_R4); } else if (expr_type == TypeManager.uint32_type){ // // From uint to long, ulong, float, double // if (target_type == TypeManager.int64_type) return new OpcodeCast (expr, target_type, OpCodes.Conv_I8); if (target_type == TypeManager.uint64_type) return new OpcodeCast (expr, target_type, OpCodes.Conv_U8); if (target_type == TypeManager.double_type) return new OpcodeCast (expr, target_type, OpCodes.Conv_R_Un, OpCodes.Conv_R8); if (target_type == TypeManager.float_type) return new OpcodeCast (expr, target_type, OpCodes.Conv_R_Un, OpCodes.Conv_R4); } else if ((expr_type == TypeManager.uint64_type) || (expr_type == TypeManager.int64_type)){ // // From long to float, double // if (target_type == TypeManager.double_type) return new OpcodeCast (expr, target_type, OpCodes.Conv_R_Un, OpCodes.Conv_R8); if (target_type == TypeManager.float_type) return new OpcodeCast (expr, target_type, OpCodes.Conv_R_Un, OpCodes.Conv_R4); } else if (expr_type == TypeManager.char_type){ // // From char to ushort, int, uint, long, ulong, float, double // if ((target_type == TypeManager.ushort_type) || (target_type == TypeManager.int32_type) || (target_type == TypeManager.uint32_type)) return new EmptyCast (expr, target_type); if (target_type == TypeManager.uint64_type) return new OpcodeCast (expr, target_type, OpCodes.Conv_U8); if (target_type == TypeManager.int64_type) return new OpcodeCast (expr, target_type, OpCodes.Conv_I8); if (target_type == TypeManager.float_type) return new OpcodeCast (expr, target_type, OpCodes.Conv_R4); if (target_type == TypeManager.double_type) return new OpcodeCast (expr, target_type, OpCodes.Conv_R8); } else return ImplicitReferenceConversion (expr, target_type); // // Could not find an implicit cast. // return null; } // // Performs an explicit conversion of the expression `expr' whose // type is expr.Type to `target_type'. // static public Expression ConvertExplicit (Expression expr, Type target_type) { return expr; } } // // This kind of cast is used to encapsulate the child // whose type is child.Type into an expression that is // reported to return "return_type". This is used to encapsulate // expressions which have compatible types, but need to be dealt // at higher levels with. // // For example, a "byte" expression could be encapsulated in one // of these as an "unsigned int". The type for the expression // would be "unsigned int". // // public class EmptyCast : Expression { protected Expression child; public EmptyCast (Expression child, Type return_type) { ExprClass = child.ExprClass; type = return_type; this.child = child; } public override Expression Resolve (TypeContainer tc) { // This should never be invoked, we are born in fully // initialized state. return this; } public override void Emit (EmitContext ec) { child.Emit (ec); } } public class BoxedCast : EmptyCast { public BoxedCast (Expression expr, Type target_type) : base (expr, target_type) { } public override Expression Resolve (TypeContainer tc) { // This should never be invoked, we are born in fully // initialized state. return this; } public override void Emit (EmitContext ec) { base.Emit (ec); ec.ig.Emit (OpCodes.Box, child.Type); } } // // This kind of cast is used to encapsulate a child expression // that can be trivially converted to a target type using one or // two opcodes. The opcodes are passed as arguments. // public class OpcodeCast : EmptyCast { OpCode op, op2; bool second_valid; public OpcodeCast (Expression child, Type return_type, OpCode op) : base (child, return_type) { this.op = op; second_valid = false; } public OpcodeCast (Expression child, Type return_type, OpCode op, OpCode op2) : base (child, return_type) { this.op = op; this.op2 = op2; second_valid = true; } public override Expression Resolve (TypeContainer tc) { // This should never be invoked, we are born in fully // initialized state. return this; } public override void Emit (EmitContext ec) { base.Emit (ec); ec.ig.Emit (op); if (second_valid) ec.ig.Emit (op2); } } public class Unary : Expression { public enum Operator { Plus, Minus, Negate, BitComplement, Indirection, AddressOf, PreIncrement, PreDecrement, PostIncrement, PostDecrement } Operator oper; Expression expr; public Unary (Operator op, Expression expr) { this.oper = op; this.expr = expr; } public Expression Expr { get { return expr; } set { expr = value; } } public Operator Oper { get { return oper; } set { oper = value; } } public override Expression Resolve (TypeContainer tc) { // FIXME: Implement; return this; } public override void Emit (EmitContext ec) { } } public class Probe : Expression { string probe_type; Expression expr; Operator oper; public enum Operator { Is, As } public Probe (Operator oper, Expression expr, string probe_type) { this.oper = oper; this.probe_type = probe_type; this.expr = expr; } public Operator Oper { get { return oper; } } public Expression Expr { get { return expr; } } public string ProbeType { get { return probe_type; } } public override Expression Resolve (TypeContainer tc) { // FIXME: Implement; return this; } public override void Emit (EmitContext ec) { } } public class Cast : Expression { string target_type; Expression expr; public Cast (string cast_type, Expression expr) { this.target_type = target_type; this.expr = expr; } public string TargetType { get { return target_type; } } public Expression Expr { get { return expr; } set { expr = value; } } public override Expression Resolve (TypeContainer tc) { // FIXME: Implement; return this; } public override void Emit (EmitContext ec) { } } public class Binary : Expression { public enum Operator { Multiply, Divide, Modulo, Add, Subtract, ShiftLeft, ShiftRight, LessThan, GreaterThan, LessOrEqual, GreaterOrEqual, Equal, NotEqual, BitwiseAnd, ExclusiveOr, BitwiseOr, LogicalAnd, LogicalOr } Operator oper; Expression left, right; MethodBase method; ArrayList Arguments; public Binary (Operator oper, Expression left, Expression right) { this.oper = oper; this.left = left; this.right = right; } 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; } } // // Retruns a stringified representation of the Operator // string OperName () { switch (oper){ case Operator.Multiply: return "*"; case Operator.Divide: return "/"; case Operator.Modulo: return "%"; case Operator.Add: return "+"; case Operator.Subtract: return "-"; case Operator.ShiftLeft: return "<<"; case Operator.ShiftRight: return ">>"; case Operator.LessThan: return "<"; case Operator.GreaterThan: return ">"; case Operator.LessOrEqual: return "<="; case Operator.GreaterOrEqual: return ">="; case Operator.Equal: return "=="; case Operator.NotEqual: 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 (Expression expr, Type target_type) { if (expr.Type == target_type) return expr; return ConvertImplicit (expr, target_type); } // // Note that handling the case l == Decimal || r == Decimal // is taken care of by the Step 1 Operator Overload resolution. // void DoNumericPromotions (TypeContainer tc, 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 (right, TypeManager.double_type); if (l != TypeManager.double_type) left = ConvertImplicit (left, TypeManager.double_type); 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 (right, TypeManager.float_type); if (l != TypeManager.double_type) left = ConvertImplicit (left, TypeManager.float_type); type = TypeManager.float_type; } else if (l == TypeManager.uint64_type || r == TypeManager.uint64_type){ // // 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 // Type other = null; if (l == TypeManager.uint64_type) other = r; else if (r == TypeManager.uint64_type) other = l; if ((other == TypeManager.sbyte_type) || (other == TypeManager.short_type) || (other == TypeManager.int32_type) || (other == TypeManager.int64_type)){ string oper = OperName (); tc.RootContext.Report.Error (34, "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 (left, TypeManager.int64_type); if (r != TypeManager.int64_type) right = ConvertImplicit (right, TypeManager.int64_type); 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 (left, TypeManager.int64_type); right = ForceConversion (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 (left, TypeManager.uint32_type); right = ForceConversion (left, TypeManager.uint32_type); type = TypeManager.uint32_type; } } else { left = ForceConversion (left, TypeManager.int32_type); right = ForceConversion (right, TypeManager.int32_type); type = TypeManager.int32_type; } } void error19 (TypeContainer tc) { tc.RootContext.Report.Error ( 19, "Operator " + OperName () + " cannot be applied to operands of type `" + TypeManager.CSharpName (left.Type) + "' and `" + TypeManager.CSharpName (right.Type) + "'"); } Expression CheckShiftArguments (TypeContainer tc) { Expression e; Type l = left.Type; Type r = right.Type; e = ForceConversion (right, TypeManager.int32_type); if (e == null){ error19 (tc); return null; } right = e; if (((e = ConvertImplicit (left, TypeManager.int32_type)) != null) || ((e = ConvertImplicit (left, TypeManager.uint32_type)) != null) || ((e = ConvertImplicit (left, TypeManager.int64_type)) != null) || ((e = ConvertImplicit (left, TypeManager.uint64_type)) != null)){ left = e; return this; } error19 (tc); return null; } Expression ResolveOperator (TypeContainer tc) { Type l = left.Type; Type r = right.Type; // // Step 1: Perform Operator Overload location // Expression i, j; string op = "Operator" + oper; i = MemberLookup (tc.RootContext, l, op, false); if (!(i is MethodGroupExpr)){ // FIXME: Find proper error tc.RootContext.Report.Error (118, "Did find something that is not a method"); return null; } j = MemberLookup (tc.RootContext, r, op, false); if (!(j is MethodGroupExpr)){ // FIXME: Find proper error tc.RootContext.Report.Error (118, "Did find something that is not a method"); return null; } // Now we need to form the union of these two sets and then call OverloadResolve // on that. MethodGroupExpr left_set = (MethodGroupExpr) i; MethodGroupExpr right_set = (MethodGroupExpr) j; int length1, length2; length1 = left_set.Methods.Length; length2 = right_set.Methods.Length; MemberInfo [] mi = new MemberInfo [length1 + length2]; left_set.Methods.CopyTo (mi, 0); right_set.Methods.CopyTo (mi, length1); MethodGroupExpr union = new MethodGroupExpr (mi); Arguments = new ArrayList (); Arguments.Add (new Argument (left, Argument.AType.Expression)); Arguments.Add (new Argument (right, Argument.AType.Expression)); method = Invocation.OverloadResolve (tc, union, Arguments); if (method != null) return this; // // Step 2: Default operations on CLI native types. // // Only perform numeric promotions on: // +, -, *, /, %, &, |, ^, ==, !=, <, >, <=, >= // if (oper == Operator.ShiftLeft || oper == Operator.ShiftRight){ return CheckShiftArguments (tc); } else if (oper == Operator.LogicalOr || oper == Operator.LogicalAnd){ if (l != TypeManager.bool_type || r != TypeManager.bool_type) error19 (tc); } else DoNumericPromotions (tc, l, r); if (left == null || right == null) return null; if (oper == Operator.BitwiseAnd || oper == Operator.BitwiseOr || oper == Operator.ExclusiveOr){ if (!((l == TypeManager.int32_type) || (l == TypeManager.uint32_type) || (l == TypeManager.int64_type) || (l == TypeManager.uint64_type))){ error19 (tc); return null; } } if (oper == Operator.Equal || oper == Operator.NotEqual || oper == Operator.LessOrEqual || oper == Operator.LessThan || oper == Operator.GreaterOrEqual || oper == Operator.GreaterThan){ type = TypeManager.bool_type; } return this; } public override Expression Resolve (TypeContainer tc) { left = left.Resolve (tc); right = right.Resolve (tc); if (left == null || right == null) return null; return ResolveOperator (tc); } public bool IsBranchable () { if (oper == Operator.Equal || oper == Operator.NotEqual || oper == Operator.LessThan || oper == Operator.GreaterThan || oper == Operator.LessOrEqual || oper == Operator.GreaterOrEqual){ 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; left.Emit (ec); right.Emit (ec); switch (oper){ case Operator.Equal: if (close_target) opcode = OpCodes.Beq_S; else opcode = OpCodes.Beq; break; case Operator.NotEqual: 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.LessOrEqual: if (close_target) opcode = OpCodes.Ble_S; else opcode = OpCodes.Ble; break; case Operator.GreaterOrEqual: if (close_target) opcode = OpCodes.Bge_S; else opcode = OpCodes.Ble; break; default: throw new Exception ("EmitBranchable called on non-EmitBranchable operator: " + oper.ToString ()); } ec.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) { if (method is MethodInfo) { Invocation.EmitArguments (ec, Arguments); ec.ig.Emit (OpCodes.Call, (MethodInfo) method); 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.Divide: if (l == TypeManager.uint32_type || l == TypeManager.uint64_type) opcode = OpCodes.Div_Un; else opcode = OpCodes.Div; break; case Operator.Modulo: if (l == TypeManager.uint32_type || l == TypeManager.uint64_type) opcode = OpCodes.Rem_Un; else opcode = OpCodes.Rem; break; case Operator.Add: 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.Subtract: 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.ShiftRight: opcode = OpCodes.Shr; break; case Operator.ShiftLeft: opcode = OpCodes.Shl; break; case Operator.Equal: opcode = OpCodes.Ceq; break; case Operator.NotEqual: 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.LessOrEqual: ec.ig.Emit (OpCodes.Cgt); ec.ig.Emit (OpCodes.Ldc_I4_0); opcode = OpCodes.Ceq; break; case Operator.GreaterOrEqual: ec.ig.Emit (OpCodes.Clt); ec.ig.Emit (OpCodes.Ldc_I4_1); opcode = OpCodes.Sub; break; case Operator.LogicalOr: case Operator.BitwiseOr: opcode = OpCodes.Or; break; case Operator.LogicalAnd: 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 ()); } ec.ig.Emit (opcode); } } public class Conditional : Expression { Expression expr, trueExpr, falseExpr; public Conditional (Expression expr, Expression trueExpr, Expression falseExpr) { this.expr = expr; this.trueExpr = trueExpr; this.falseExpr = falseExpr; } public Expression Expr { get { return expr; } } public Expression TrueExpr { get { return trueExpr; } } public Expression FalseExpr { get { return falseExpr; } } public override Expression Resolve (TypeContainer tc) { // FIXME: Implement; return this; } public override void Emit (EmitContext ec) { } } public class SimpleName : Expression { string name; public SimpleName (string name) { this.name = name; } public string Name { get { return name; } } // // Checks whether we are trying to access an instance // property, method or field from a static body. // Expression MemberStaticCheck (Report r, Expression e) { if (e is FieldExpr){ FieldInfo fi = ((FieldExpr) e).FieldInfo; if (!fi.IsStatic){ r.Error (120, "An object reference is required " + "for the non-static field `"+name+"'"); return null; } } else if (e is MethodGroupExpr){ // FIXME: Pending reorganization of MemberLookup // Basically at this point we should have the // best match already selected for us, and // we should only have to check a *single* // Method for its static on/off bit. return e; } else if (e is PropertyExpr){ if (!((PropertyExpr) e).IsStatic){ r.Error (120, "An object reference is required " + "for the non-static property access `"+ name+"'"); return null; } } return e; } // // 7.5.2: Simple Names. // // Local Variables and Parameters are handled at // parse time, so they never occur as SimpleNames. // Expression ResolveSimpleName (TypeContainer tc) { Expression e; Report r = tc.RootContext.Report; e = MemberLookup (tc.RootContext, tc.TypeBuilder, name, true); if (e != null){ if (e is TypeExpr) return e; if ((tc.ModFlags & Modifiers.STATIC) != 0) return MemberStaticCheck (r, e); else return e; } // // Do step 3 of the Simple Name resolution. // // FIXME: implement me. return this; } // // SimpleName needs to handle a multitude of cases: // // simple_names and qualified_identifiers are placed on // the tree equally. // public override Expression Resolve (TypeContainer tc) { if (name.IndexOf (".") != -1) return ResolveMemberAccess (tc, name); else return ResolveSimpleName (tc); } public override void Emit (EmitContext ec) { } } public class LocalVariableReference : Expression { public readonly string Name; public readonly Block Block; public LocalVariableReference (Block block, string name) { Block = block; Name = name; eclass = ExprClass.Variable; } public VariableInfo VariableInfo { get { return Block.GetVariableInfo (Name); } } public override Expression Resolve (TypeContainer tc) { VariableInfo vi = Block.GetVariableInfo (Name); type = vi.VariableType; return this; } public override void Emit (EmitContext ec) { VariableInfo vi = VariableInfo; ILGenerator ig = ec.ig; int idx = vi.Idx; 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, idx); else ig.Emit (OpCodes.Ldloc, idx); break; } } } public class ParameterReference : Expression { public readonly Parameters Pars; public readonly String Name; public readonly int Idx; public ParameterReference (Parameters pars, int idx, string name) { Pars = pars; Idx = idx; Name = name; } public override Expression Resolve (TypeContainer tc) { Type [] types = Pars.GetParameterInfo (tc); type = types [Idx]; return this; } public override void Emit (EmitContext ec) { if (Idx < 255) ec.ig.Emit (OpCodes.Ldarg_S, Idx); else ec.ig.Emit (OpCodes.Ldarg, Idx); } } // // Used for arguments to New(), Invocation() // public class Argument { public enum AType { Expression, Ref, Out }; public readonly AType Type; Expression expr; public Argument (Expression expr, AType type) { this.expr = expr; this.Type = type; } public Expression Expr { get { return expr; } } public bool Resolve (TypeContainer tc) { expr = expr.Resolve (tc); return expr != null; } public void Emit (EmitContext ec) { expr.Emit (ec); } } // // Invocation of methods or delegates. // public class Invocation : Expression { public readonly ArrayList Arguments; 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) { this.expr = expr; Arguments = arguments; } public Expression Expr { get { return expr; } } /// /// Computes whether Argument `a' and the ParameterInfo `pi' are /// compatible, and if so, how good is the match (in terms of /// "better conversions" (7.4.2.3). /// /// 0 is the best possible match. /// -1 represents a type mismatch. /// -2 represents a ref/out mismatch. /// static int Badness (Argument a, Type t) { if (a.Expr.Type == null){ throw new Exception ("Expression of type " + a.Expr + " does not resolve its type"); } if (t == a.Expr.Type) return 0; // FIXME: Implement implicit conversions here. // FIXME: Implement better conversion here. return -1; } // // Returns the Parameters (a ParameterData interface) for the // Method `mb' // static ParameterData GetParameterData (TypeContainer tc, MethodBase mb) { object pd = method_parameter_cache [mb]; if (pd != null) return (ParameterData) pd; if (mb is MethodBuilder || mb is ConstructorBuilder){ MethodCore mc = TypeContainer.LookupMethodByBuilder (mb); InternalParameters ip = mc.ParameterInfo; 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; } } // // Find the Applicable Function Members (7.4.2.1) // public static MethodBase OverloadResolve (TypeContainer tc, MethodGroupExpr me, ArrayList Arguments) { ArrayList afm = new ArrayList (); int best_match = 10000; int best_match_idx = -1; MethodBase method = null; int argument_count; if (Arguments == null) argument_count = 0; else argument_count = Arguments.Count; for (int i = me.Methods.Length; i > 0; ){ i--; MethodBase mb = me.Methods [i]; ParameterData pd; pd = GetParameterData (tc, mb); // // Compute how good this is // if (pd.Count == argument_count){ int badness = 0; for (int j = argument_count; j > 0;){ int x; j--; Argument a = (Argument) Arguments [j]; x = Badness (a, pd.ParameterType (j)); if (x < 0){ badness = best_match; continue; } badness += x; } if (badness < best_match){ best_match = badness; method = me.Methods [i]; best_match_idx = i; } } } if (best_match_idx == -1) return null; return method; } public override Expression Resolve (TypeContainer tc) { // // First, resolve the expression that is used to // trigger the invocation // this.expr = expr.Resolve (tc); if (this.expr == null) return null; if (!(this.expr is MethodGroupExpr)){ tc.RootContext.Report.Error (118, "Denotes a non-method (Detail: ExprClass=" + this.expr.ExprClass+")"); 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 (tc)) return null; } } method = OverloadResolve (tc, (MethodGroupExpr) this.expr, Arguments); if (method == null){ tc.RootContext.Report.Error (-6, "Figure out error: Can not find a good function for this argument list"); return null; } if (method is MethodInfo) type = ((MethodInfo)method).ReturnType; return this; } public static void EmitArguments (EmitContext ec, ArrayList Arguments) { int top; if (Arguments != null) top = Arguments.Count; else top = 0; for (int i = 0; i < top; i++){ Argument a = (Argument) Arguments [i]; a.Emit (ec); } } public override void Emit (EmitContext ec) { bool is_static = method.IsStatic; if (!is_static){ MethodGroupExpr mg = (MethodGroupExpr) this.expr; if (mg.InstanceExpression == null){ Console.WriteLine ("Internal compiler error. Should check in the method groups for static/instance"); } mg.InstanceExpression.Emit (ec); } EmitArguments (ec, Arguments); if (method.IsVirtual){ if (method is MethodInfo) ec.ig.Emit (OpCodes.Callvirt, (MethodInfo) method); else ec.ig.Emit (OpCodes.Callvirt, (MethodInfo) method); } else { if (method is MethodInfo) ec.ig.Emit (OpCodes.Call, (MethodInfo) method); else ec.ig.Emit (OpCodes.Call, (ConstructorInfo) method); } } } public class New : Expression { public enum NType { Object, Array }; public readonly NType NewType; public readonly ArrayList Arguments; public readonly string RequestedType; // These are for the case when we have an array public readonly string Rank; public readonly ArrayList Indices; public readonly ArrayList Initializers; MethodBase method = null; public New (string requested_type, ArrayList arguments) { RequestedType = requested_type; Arguments = arguments; NewType = NType.Object; } public New (string requested_type, ArrayList exprs, string rank, ArrayList initializers) { RequestedType = requested_type; Indices = exprs; Rank = rank; Initializers = initializers; NewType = NType.Array; } public override Expression Resolve (TypeContainer tc) { type = tc.LookupType (RequestedType, false); if (type == null) return null; Expression ml; MemberTypes mt = MemberTypes.Constructor; BindingFlags bf = BindingFlags.Public | BindingFlags.Instance; MemberInfo [] mi = tc.RootContext.TypeManager.FindMembers (type, mt, bf, null, null); Console.WriteLine ("Found: " + mi.Length); for (int i = 0; i < mi.Length; i++) Console.WriteLine (" " + i + ": " + mi [i]); ml = MemberLookup (tc.RootContext, type, ".ctor", false); if (! (ml is MethodGroupExpr)){ // // FIXME: Find proper error // tc.RootContext.Report.Error (118, "Did find something that is not a method"); return null; } if (Arguments != null){ for (int i = Arguments.Count; i > 0;){ --i; Argument a = (Argument) Arguments [i]; if (!a.Resolve (tc)) return null; } } method = Invocation.OverloadResolve (tc, (MethodGroupExpr) ml, Arguments); if (method == null){ tc.RootContext.Report.Error (-6, "New invocation: Can not find a constructor for this argument list"); return null; } return this; } public override void Emit (EmitContext ec) { Invocation.EmitArguments (ec, Arguments); ec.ig.Emit (OpCodes.Newobj, (ConstructorInfo) method); } } public class This : Expression { public override Expression Resolve (TypeContainer tc) { // FIXME: Implement; return this; } public override void Emit (EmitContext ec) { } } public class TypeOf : Expression { public readonly string QueriedType; public TypeOf (string queried_type) { QueriedType = queried_type; } public override Expression Resolve (TypeContainer tc) { // FIXME: Implement; return this; } public override void Emit (EmitContext ec) { } } public class SizeOf : Expression { public readonly string QueriedType; public SizeOf (string queried_type) { this.QueriedType = queried_type; } public override Expression Resolve (TypeContainer tc) { // FIXME: Implement; return this; } public override void Emit (EmitContext ec) { } } public class MemberAccess : Expression { public readonly string Identifier; Expression expr; Expression member_lookup; public MemberAccess (Expression expr, string id) { this.expr = expr; Identifier = id; } public Expression Expr { get { return expr; } } public override Expression Resolve (TypeContainer tc) { Expression new_expression = expr.Resolve (tc); if (new_expression == null) return null; Console.WriteLine ("This is what I figured: " + expr.Type + "/" + expr.ExprClass); member_lookup = MemberLookup (tc.RootContext, expr.Type, Identifier, false); if (member_lookup is MethodGroupExpr){ MethodGroupExpr mg = (MethodGroupExpr) member_lookup; // // Bind the instance expression to it // // FIXME: This is a horrible way of detecting if it is // an instance expression. Figure out how to fix this. // Console.WriteLine ("FIXME: Horrible way of figuring if something is an isntance"); if (expr is LocalVariableReference) mg.InstanceExpression = expr; return member_lookup; } else // // FIXME: This should generate the proper node // ie, for a Property Access, it should like call it // and stuff. return null; } public override void Emit (EmitContext ec) { } } // // Nodes of type Namespace are created during the semantic // analysis to resolve member_access/qualified_identifier/simple_name // accesses. // // They are born `resolved'. // public class NamespaceExpr : Expression { public readonly string Name; public NamespaceExpr (string name) { Name = name; eclass = ExprClass.Namespace; } public override Expression Resolve (TypeContainer tc) { return this; } public override void Emit (EmitContext ec) { } } // // Fully resolved expression that evaluates to a type // public class TypeExpr : Expression { public TypeExpr (Type t) { Type = t; eclass = ExprClass.Type; } override public Expression Resolve (TypeContainer tc) { return this; } override public void Emit (EmitContext ec) { } } // // MethodGroup Expression. // // This is a fully resolved expression that evaluates to a type // public class MethodGroupExpr : Expression { public readonly MethodBase [] Methods; Expression instance_expression = null; public MethodGroupExpr (MemberInfo [] mi) { Methods = new MethodBase [mi.Length]; mi.CopyTo (Methods, 0); eclass = ExprClass.MethodGroup; } // // `A method group may have associated an instance expression' // public Expression InstanceExpression { get { return instance_expression; } set { instance_expression = value; } } override public Expression Resolve (TypeContainer tc) { return this; } override public void Emit (EmitContext ec) { } } public class BuiltinTypeAccess : Expression { public readonly string AccessBase; public readonly string Method; public BuiltinTypeAccess (string type, string method) { System.Console.WriteLine ("DUDE! This type should be fully resolved!"); AccessBase = type; Method = method; } public override Expression Resolve (TypeContainer tc) { // FIXME: Implement; return this; } public override void Emit (EmitContext ec) { } } // Fully resolved expression that evaluates to a Field // public class FieldExpr : Expression { public readonly FieldInfo FieldInfo; public FieldExpr (FieldInfo fi) { FieldInfo = fi; eclass = ExprClass.Variable; type = fi.FieldType; } override public Expression Resolve (TypeContainer tc) { // We are born in resolved state. return this; } override public void Emit (EmitContext ec) { // FIXME: Assert that this should not be reached? } } // // Fully resolved expression that evaluates to a Property // public class PropertyExpr : Expression { public readonly PropertyInfo PropertyInfo; public readonly bool IsStatic; public PropertyExpr (PropertyInfo pi) { PropertyInfo = pi; eclass = ExprClass.PropertyAccess; IsStatic = false; MethodBase [] acc = pi.GetAccessors (); for (int i = 0; i < acc.Length; i++) if (acc [i].IsStatic) IsStatic = true; type = pi.PropertyType; } override public Expression Resolve (TypeContainer tc) { // We are born in resolved state. return this; } override public void Emit (EmitContext ec) { // FIXME: Implement. } } // // Fully resolved expression that evaluates to a Property // public class EventExpr : Expression { public readonly EventInfo EventInfo; public EventExpr (EventInfo ei) { EventInfo = ei; eclass = ExprClass.EventAccess; } override public Expression Resolve (TypeContainer tc) { // We are born in resolved state. return this; } override public void Emit (EmitContext ec) { // FIXME: Implement. } } public class CheckedExpr : Expression { public readonly Expression Expr; public CheckedExpr (Expression e) { Expr = e; } public override Expression Resolve (TypeContainer tc) { // FIXME : Implement ! return this; } public override void Emit (EmitContext ec) { } } public class UnCheckedExpr : Expression { public readonly Expression Expr; public UnCheckedExpr (Expression e) { Expr = e; } public override Expression Resolve (TypeContainer tc) { // FIXME : Implement ! return this; } public override void Emit (EmitContext ec) { } } public class ElementAccess : Expression { public readonly ArrayList Arguments; public readonly Expression Expr; public ElementAccess (Expression e, ArrayList e_list) { Expr = e; Arguments = e_list; } public override Expression Resolve (TypeContainer tc) { // FIXME : Implement return this; } public override void Emit (EmitContext ec) { // FIXME : Implement ! } } public class BaseAccess : Expression { public enum BaseAccessType { Member, Indexer }; public readonly BaseAccessType BAType; public readonly string Member; public readonly ArrayList Arguments; public BaseAccess (BaseAccessType t, string member, ArrayList args) { BAType = t; Member = member; Arguments = args; } public override Expression Resolve (TypeContainer tc) { // FIXME : Implement ! return this; } public override void Emit (EmitContext ec) { } } }