// // expression.cs: Expression representation for the IL tree. // // Author: // Miguel de Icaza (miguel@ximian.com) // Manjula GHM (mmanjula@novell.com) // Satya Sudha K (ksathyasudha@novell.com) // // (C) 2001 Ximian, Inc. // // #define USE_OLD namespace Mono.MonoBASIC { using System; using System.Collections; 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, Location l) { mi = m; args = a; type = m.ReturnType; eclass = ExprClass.Value; loc = l; } 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, loc); } public override void EmitStatement (EmitContext ec) { Emit (ec); if (TypeManager.TypeToCoreType (type) != TypeManager.void_type) ec.ig.Emit (OpCodes.Pop); } } /// /// Unary expressions. /// /// /// /// Unary implements unary expressions. It derives from /// ExpressionStatement becuase the pre/post increment/decrement /// operators can be used in a statement context. /// public class Unary : Expression { public enum Operator : byte { UnaryPlus, UnaryNegation, LogicalNot, OnesComplement, Indirection, AddressOf, TOP } public Operator Oper; public Expression Expr; public Unary (Operator op, Expression expr, Location loc) { this.Oper = op; this.Expr = expr; this.loc = loc; } /// /// Returns a stringified representation of the Operator /// static public string OperName (Operator oper) { switch (oper){ case Operator.UnaryPlus: return "+"; case Operator.UnaryNegation: return "-"; case Operator.LogicalNot: return "Not"; case Operator.OnesComplement: return "~"; case Operator.AddressOf: return "AddressOf"; case Operator.Indirection: return "*"; } return oper.ToString (); } static string [] oper_names; static Unary () { oper_names = new string [(int)Operator.TOP]; oper_names [(int) Operator.UnaryPlus] = "op_UnaryPlus"; oper_names [(int) Operator.UnaryNegation] = "op_UnaryNegation"; oper_names [(int) Operator.LogicalNot] = "op_LogicalNot"; oper_names [(int) Operator.OnesComplement] = "op_OnesComplement"; oper_names [(int) Operator.Indirection] = "op_Indirection"; oper_names [(int) Operator.AddressOf] = "op_AddressOf"; } void Error23 (Type t) { Error ( 30311, "Operator " + OperName (Oper) + " cannot be applied to operand of type '" + TypeManager.MonoBASIC_Name (t) + "'"); } /// /// The result has been already resolved: /// /// FIXME: a minus constant -128 sbyte cant be turned into a /// constant byte. /// static Expression TryReduceNegative (Constant expr) { Expression e = null; if (expr is IntConstant) e = new IntConstant (-((IntConstant) expr).Value); else if (expr is UIntConstant){ uint value = ((UIntConstant) expr).Value; if (value < 2147483649) return new IntConstant (-(int)value); else e = new LongConstant (value); } else if (expr is LongConstant) e = new LongConstant (-((LongConstant) expr).Value); else if (expr is ULongConstant){ ulong value = ((ULongConstant) expr).Value; if (value < 9223372036854775809) return new LongConstant(-(long)value); } else if (expr is FloatConstant) e = new FloatConstant (-((FloatConstant) expr).Value); else if (expr is DoubleConstant) e = new DoubleConstant (-((DoubleConstant) expr).Value); else if (expr is DecimalConstant) e = new DecimalConstant (-((DecimalConstant) expr).Value); else if (expr is ShortConstant) e = new IntConstant (-((ShortConstant) expr).Value); else if (expr is UShortConstant) e = new IntConstant (-((UShortConstant) expr).Value); return e; } // // This routine will attempt to simplify the unary expression when the // argument is a constant. The result is returned in 'result' and the // function returns true or false depending on whether a reduction // was performed or not // bool Reduce (EmitContext ec, Constant e, out Expression result) { Type expr_type = e.Type; switch (Oper){ case Operator.UnaryPlus: result = e; return true; case Operator.UnaryNegation: result = TryReduceNegative (e); return true; case Operator.LogicalNot: if (expr_type != TypeManager.bool_type) { result = null; Error23 (expr_type); return false; } BoolConstant b = (BoolConstant) e; result = new BoolConstant (!(b.Value)); return true; case Operator.OnesComplement: if (!((expr_type == TypeManager.int32_type) || (expr_type == TypeManager.uint32_type) || (expr_type == TypeManager.int64_type) || (expr_type == TypeManager.uint64_type) || (expr_type.IsSubclassOf (TypeManager.enum_type)))){ result = null; Error23 (expr_type); return false; } if (e is EnumConstant){ EnumConstant enum_constant = (EnumConstant) e; Expression reduced; if (Reduce (ec, enum_constant.Child, out reduced)){ result = new EnumConstant ((Constant) reduced, enum_constant.Type); return true; } else { result = null; return false; } } if (expr_type == TypeManager.int32_type){ result = new IntConstant (~ ((IntConstant) e).Value); } else if (expr_type == TypeManager.uint32_type){ result = new UIntConstant (~ ((UIntConstant) e).Value); } else if (expr_type == TypeManager.int64_type){ result = new LongConstant (~ ((LongConstant) e).Value); } else if (expr_type == TypeManager.uint64_type){ result = new ULongConstant (~ ((ULongConstant) e).Value); } else { result = null; Error23 (expr_type); return false; } return true; case Operator.AddressOf: result = this; return false; case Operator.Indirection: result = this; return false; } throw new Exception ("Can not constant fold: " + Oper.ToString()); } Expression ResolveOperator (EmitContext ec) { Type expr_type = Expr.Type; Expression mg; string op_name; if (Oper == Operator.LogicalNot && expr_type != TypeManager.bool_type) Oper = Operator.OnesComplement; if ((expr_type == TypeManager.string_type) && (Oper == Operator.UnaryPlus || Oper == Operator.UnaryNegation)) { Expr = ConvertImplicit (ec, Expr, TypeManager.double_type, loc); if (Expr == null) { Error23 (expr_type); return null; } expr_type = Expr.Type; } // // Step 1: Perform Operator Overload location // op_name = oper_names [(int) Oper]; mg = MemberLookup (ec, expr_type, op_name, MemberTypes.Method, AllBindingFlags, loc); if (mg != null) { Expression e = StaticCallExpr.MakeSimpleCall ( ec, (MethodGroupExpr) mg, Expr, loc); if (e == null){ Error23 (expr_type); return null; } return e; } // Only perform numeric promotions on: // +, - if (expr_type == null) return null; // // Step 2: Default operations on CLI native types. // // Attempt to use a constant folding operation. if (Expr is Constant){ Expression result; if (Reduce (ec, (Constant) Expr, out result)) return result; } switch (Oper){ case Operator.LogicalNot: if (expr_type != TypeManager.bool_type) { Error23 (Expr.Type); return null; } type = TypeManager.bool_type; return this; case Operator.OnesComplement: if (expr_type == TypeManager.string_type || expr_type == TypeManager.decimal_type || expr_type == TypeManager.double_type || expr_type == TypeManager.float_type) { if ((Expr = ConvertImplicit (ec, Expr, TypeManager.int64_type, loc)) == null) { Error23 (Expr.Type); return null; } expr_type = Expr.Type; } if (expr_type == TypeManager.int64_type) { type = expr_type; return this; } if (expr_type == TypeManager.byte_type || expr_type == TypeManager.short_type || expr_type == TypeManager.int32_type) { type = TypeManager.int32_type; return this; } if (expr_type == TypeManager.object_type) { Expression etmp = Parser.DecomposeQI ("Microsoft.VisualBasic.CompilerServices.ObjectType.NotObj", Location.Null); ArrayList arguments = new ArrayList (); arguments.Add (new Argument (Expr, Argument.AType.Expression)); Expression e = new Invocation (etmp, arguments, loc); return e.Resolve (ec); } break; case Operator.AddressOf: // Not required in VB ?? if (Expr.eclass != ExprClass.Variable){ Error (211, "Cannot take the address of non-variables"); return null; } if (!ec.InUnsafe) { UnsafeError (loc); return null; } if (!TypeManager.VerifyUnManaged (Expr.Type, loc)){ return null; } string ptr_type_name = Expr.Type.FullName + "*"; type = TypeManager.LookupType (ptr_type_name); return this; case Operator.Indirection: if (!ec.InUnsafe){ UnsafeError (loc); return null; } if (!expr_type.IsPointer){ Error ( 193, "The * or -> operator can only be applied to pointers"); return null; } // // We create an Indirection expression, because // it can implement the IMemoryLocation. // return new Indirection (Expr, loc); case Operator.UnaryPlus: // // A plus in front of something is just a no-op, so return the child. // if (expr_type == TypeManager.string_type) { Expression e = ConvertImplicit (ec, Expr, TypeManager.double_type, loc); if (e != null){ type = TypeManager.double_type; return e; } } if (expr_type == TypeManager.bool_type) { Expression e = ConvertImplicit (ec, Expr, TypeManager.short_type, loc); if (e != null){ type = TypeManager.int32_type; return e; } } return Expr; case Operator.UnaryNegation: // // Deals with -literals // int operator- (int x) // long operator- (long x) // float operator- (float f) // double operator- (double d) // decimal operator- (decimal d) // // // transform - - expr into expr // if (Expr is Unary){ Unary unary = (Unary) Expr; if (unary.Oper == Operator.UnaryNegation) return unary.Expr; } // // 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.string_type) { Expression e = ConvertImplicit (ec, Expr, TypeManager.double_type, loc); if (e != null){ Expr = e; type = TypeManager.double_type; return this; } } if (expr_type == TypeManager.bool_type) { Expression e = ConvertImplicit (ec, Expr, TypeManager.short_type, loc); if (e != null){ Expr = e; type = TypeManager.int32_type; return this; } } if (expr_type == TypeManager.float_type || expr_type == TypeManager.double_type) { type = expr_type; return this; } if (expr_type == TypeManager.short_type || expr_type == TypeManager.byte_type) { type = TypeManager.int32_type; return this; } if (expr_type == TypeManager.int32_type || expr_type == TypeManager.int64_type) { Expression e = new Binary (Binary.Operator.Subtraction, new IntLiteral (0), Expr, loc); return e.Resolve (ec); } Error23 (expr_type); return null; } Error (187, "No such operator '" + OperName (Oper) + "' defined for type '" + TypeManager.MonoBASIC_Name (expr_type) + "'"); return null; } public override Expression DoResolve (EmitContext ec) { if (Oper == Operator.AddressOf) Expr = Expr.ResolveLValue (ec, new EmptyExpression ()); else Expr = Expr.Resolve (ec); if (Expr == null) return null; Type init_type = Expr.Type; eclass = ExprClass.Value; Expression etmp = ResolveOperator (ec); // Convert the result to byte/short if operands are of type byte/short/boolean if (etmp.Type != init_type && (init_type == TypeManager.byte_type || init_type == TypeManager.short_type || init_type == TypeManager.bool_type)) { Expression conv_exp = null; if (init_type == TypeManager.byte_type && Oper != Operator.UnaryNegation) { return new OpcodeCast (etmp, TypeManager.byte_type, OpCodes.Conv_U1); } else conv_exp = ConvertImplicit (ec, etmp, TypeManager.short_type, loc); if (conv_exp != null) return conv_exp; } return etmp; } public override void Emit (EmitContext ec) { ILGenerator ig = ec.ig; 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, AddressOp.LoadStore); break; 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 string ToString () { return "Unary (" + Oper + ", " + Expr + ")"; } } // // Unary operators are turned into Indirection expressions // after semantic analysis (this is so we can take the address // of an indirection). // public class Indirection : Expression, IMemoryLocation, IAssignMethod { Expression expr; LocalTemporary temporary; bool have_temporary; public Indirection (Expression expr, Location l) { this.expr = expr; this.type = TypeManager.TypeToCoreType (expr.Type.GetElementType ()); eclass = ExprClass.Variable; loc = l; } /* void LoadExprValue (EmitContext ec) { } */ public override void Emit (EmitContext ec) { ILGenerator ig = ec.ig; if (temporary != null){ if (have_temporary){ temporary.Emit (ec); return; } expr.Emit (ec); ec.ig.Emit (OpCodes.Dup); temporary.Store (ec); have_temporary = true; } else expr.Emit (ec); LoadFromPtr (ig, Type); } public void EmitAssign (EmitContext ec, Expression source) { if (temporary != null){ if (have_temporary){ temporary.Emit (ec); return; } expr.Emit (ec); ec.ig.Emit (OpCodes.Dup); temporary.Store (ec); have_temporary = true; } else expr.Emit (ec); source.Emit (ec); StoreFromPtr (ec.ig, type); } public void AddressOf (EmitContext ec, AddressOp Mode) { if (temporary != null){ if (have_temporary){ temporary.Emit (ec); return; } expr.Emit (ec); ec.ig.Emit (OpCodes.Dup); temporary.Store (ec); have_temporary = true; } else expr.Emit (ec); } public override Expression DoResolve (EmitContext ec) { // // Born fully resolved // return this; } public new void CacheTemporaries (EmitContext ec) { temporary = new LocalTemporary (ec, type); } } /// /// 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; 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; } static string OperName (Mode mode) { return (mode == Mode.PreIncrement || mode == Mode.PostIncrement) ? "++" : "--"; } /// /// 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) || (t.IsPointer && t != TypeManager.void_ptr_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, MemberTypes.Method, AllBindingFlags, loc); if (mg == null && expr_type.BaseType != null) mg = MemberLookup (ec, expr_type.BaseType, op_name, MemberTypes.Method, AllBindingFlags, loc); if (mg != null) { method = StaticCallExpr.MakeSimpleCall ( ec, (MethodGroupExpr) mg, expr, loc); type = method.Type; return this; } // // The operand of the prefix/postfix increment decrement operators // should be an expression that is classified as a variable, // a property access or an indexer access // type = expr_type; if (expr.eclass == ExprClass.Variable){ if (IsIncrementableNumber (expr_type) || expr_type == TypeManager.decimal_type){ return this; } } else if (expr.eclass == ExprClass.IndexerAccess){ IndexerAccess ia = (IndexerAccess) expr; temp_storage = new LocalTemporary (ec, expr.Type); expr = ia.ResolveLValue (ec, temp_storage); if (expr == null) return null; return this; } else if (expr.eclass == ExprClass.PropertyAccess){ PropertyExpr pe = (PropertyExpr) expr; if (pe.VerifyAssignable ()) return this; return null; } else { expr.Error118 ("variable, indexer or property access"); return null; } Error (187, "No such operator '" + OperName (mode) + "' defined for type '" + TypeManager.MonoBASIC_Name (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); } static int PtrTypeSize (Type t) { return GetTypeSize (t.GetElementType ()); } // // Loads the proper "1" into the stack based on the type // static void LoadOne (ILGenerator ig, Type t) { if (t == TypeManager.uint64_type || t == TypeManager.int64_type) ig.Emit (OpCodes.Ldc_I8, 1L); else if (t == TypeManager.double_type) ig.Emit (OpCodes.Ldc_R8, 1.0); else if (t == TypeManager.float_type) ig.Emit (OpCodes.Ldc_R4, 1.0F); else if (t.IsPointer){ int n = PtrTypeSize (t); if (n == 0) ig.Emit (OpCodes.Sizeof, t); else IntConstant.EmitInt (ig, n); } else ig.Emit (OpCodes.Ldc_I4_1); } // // FIXME: We need some way of avoiding the use of temp_storage // for some types of storage (parameters, local variables, // static fields) and single-dimension array access. // void EmitCode (EmitContext ec, bool is_expr) { ILGenerator ig = ec.ig; IAssignMethod ia = (IAssignMethod) expr; Type expr_type = expr.Type; if (temp_storage == null) temp_storage = new LocalTemporary (ec, expr_type); ia.CacheTemporaries (ec); ig.Emit (OpCodes.Nop); switch (mode){ case Mode.PreIncrement: case Mode.PreDecrement: if (method == null){ expr.Emit (ec); LoadOne (ig, expr_type); // // Select the opcode based on the check state (then the type) // and the actual operation // if (ec.CheckState){ if (expr_type == TypeManager.int32_type || expr_type == TypeManager.int64_type){ if (mode == Mode.PreDecrement) ig.Emit (OpCodes.Sub_Ovf); else ig.Emit (OpCodes.Add_Ovf); } else if (expr_type == TypeManager.uint32_type || expr_type == TypeManager.uint64_type){ if (mode == Mode.PreDecrement) ig.Emit (OpCodes.Sub_Ovf_Un); else ig.Emit (OpCodes.Add_Ovf_Un); } else { if (mode == Mode.PreDecrement) ig.Emit (OpCodes.Sub_Ovf); else ig.Emit (OpCodes.Add_Ovf); } } else { 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); LoadOne (ig, expr_type); if (ec.CheckState){ if (expr_type == TypeManager.int32_type || expr_type == TypeManager.int64_type){ if (mode == Mode.PostDecrement) ig.Emit (OpCodes.Sub_Ovf); else ig.Emit (OpCodes.Add_Ovf); } else if (expr_type == TypeManager.uint32_type || expr_type == TypeManager.uint64_type){ if (mode == Mode.PostDecrement) ig.Emit (OpCodes.Sub_Ovf_Un); else ig.Emit (OpCodes.Add_Ovf_Un); } else { if (mode == Mode.PostDecrement) ig.Emit (OpCodes.Sub_Ovf); else ig.Emit (OpCodes.Add_Ovf); } } else { 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 Expression ProbeType; protected Expression expr; protected Type probe_type; public Probe (Expression expr, Expression probe_type, Location l) { ProbeType = probe_type; loc = l; this.expr = expr; } public Expression Expr { get { return expr; } } public override Expression DoResolve (EmitContext ec) { probe_type = ec.DeclSpace.ResolveType (ProbeType, false, loc); if (probe_type == null) return null; expr = expr.Resolve (ec); return this; } } /// /// Implementation of the 'is' operator. /// public class Is : Probe { public Is (Expression expr, Expression probe_type, Location l) : base (expr, probe_type, l) { } enum Action { AlwaysTrue, AlwaysNull, AlwaysFalse, LeaveOnStack, Probe } Action action; public override void Emit (EmitContext ec) { ILGenerator ig = ec.ig; expr.Emit (ec); switch (action){ case Action.AlwaysFalse: ig.Emit (OpCodes.Pop); IntConstant.EmitInt (ig, 0); return; case Action.AlwaysTrue: ig.Emit (OpCodes.Pop); ig.Emit (OpCodes.Nop); IntConstant.EmitInt (ig, 1); return; case Action.LeaveOnStack: // the 'e != null' rule. return; case Action.Probe: ig.Emit (OpCodes.Isinst, probe_type); ig.Emit (OpCodes.Ldnull); ig.Emit (OpCodes.Cgt_Un); return; } throw new Exception ("never reached"); } public override Expression DoResolve (EmitContext ec) { Expression e = base.DoResolve (ec); if ((e == null) || (expr == null)) return null; Type etype = expr.Type; bool warning_always_matches = false; bool warning_never_matches = false; type = TypeManager.bool_type; eclass = ExprClass.Value; // // First case, if at compile time, there is an implicit conversion // then e != null (objects) or true (value types) // e = ConvertImplicitStandard (ec, expr, probe_type, loc); if (e != null){ expr = e; if (etype.IsValueType) action = Action.AlwaysTrue; else action = Action.LeaveOnStack; warning_always_matches = true; } else if (ExplicitReferenceConversionExists (etype, probe_type)){ // // Second case: explicit reference convresion // if (expr is NullLiteral) action = Action.AlwaysFalse; else action = Action.Probe; } else { action = Action.AlwaysFalse; warning_never_matches = true; } if (RootContext.WarningLevel >= 1){ if (warning_always_matches) Warning ( 183, "The expression is always of type '" + TypeManager.MonoBASIC_Name (probe_type) + "'"); else if (warning_never_matches){ if (!(probe_type.IsInterface || expr.Type.IsInterface)) Warning ( 184, "The expression is never of type '" + TypeManager.MonoBASIC_Name (probe_type) + "'"); } } return this; } } /// /// Implementation of the 'as' operator. /// public class As : Probe { public As (Expression expr, Expression probe_type, Location l) : base (expr, probe_type, l) { } bool do_isinst = false; public override void Emit (EmitContext ec) { ILGenerator ig = ec.ig; expr.Emit (ec); if (do_isinst) ig.Emit (OpCodes.Isinst, probe_type); } static void Error_CannotConvertType (Type source, Type target, Location loc) { Report.Error ( 39, loc, "as operator can not convert from '" + TypeManager.MonoBASIC_Name (source) + "' to '" + TypeManager.MonoBASIC_Name (target) + "'"); } public override Expression DoResolve (EmitContext ec) { Expression e = base.DoResolve (ec); if (e == null) return null; type = probe_type; eclass = ExprClass.Value; Type etype = expr.Type; if (TypeManager.IsValueType (probe_type)){ Report.Error (77, loc, "The as operator should be used with a reference type only (" + TypeManager.MonoBASIC_Name (probe_type) + " is a value type)"); return null; } e = ConvertImplicit (ec, expr, probe_type, loc); if (e != null){ expr = e; do_isinst = false; return this; } if (ExplicitReferenceConversionExists (etype, probe_type)){ do_isinst = true; return this; } Error_CannotConvertType (etype, probe_type, loc); return null; } } /// /// 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; bool runtime_cast; public Cast (Expression cast_type, Expression expr, Location loc) { this.target_type = cast_type; this.expr = expr; this.loc = loc; runtime_cast = false; } public Expression TargetType { get { return target_type; } } public Expression Expr { get { return expr; } set { expr = value; } } public bool IsRuntimeCast { get { return runtime_cast; } set{ runtime_cast = value; } } /// /// Attempts to do a compile-time folding of a constant cast. /// Expression TryReduce (EmitContext ec, Type target_type) { if (expr is ByteConstant){ byte v = ((ByteConstant) expr).Value; if (target_type == TypeManager.sbyte_type) return new SByteConstant ((sbyte) v); if (target_type == TypeManager.short_type) return new ShortConstant ((short) v); if (target_type == TypeManager.ushort_type) return new UShortConstant ((ushort) v); if (target_type == TypeManager.int32_type) return new IntConstant ((int) v); if (target_type == TypeManager.uint32_type) return new UIntConstant ((uint) v); if (target_type == TypeManager.int64_type) return new LongConstant ((long) v); if (target_type == TypeManager.uint64_type) return new ULongConstant ((ulong) v); if (target_type == TypeManager.float_type) return new FloatConstant ((float) v); if (target_type == TypeManager.double_type) return new DoubleConstant ((double) v); if (target_type == TypeManager.char_type) return new CharConstant ((char) v); if (target_type == TypeManager.decimal_type) return new DecimalConstant ((decimal) v); } if (expr is SByteConstant){ sbyte v = ((SByteConstant) expr).Value; if (target_type == TypeManager.byte_type) return new ByteConstant ((byte) v); if (target_type == TypeManager.short_type) return new ShortConstant ((short) v); if (target_type == TypeManager.ushort_type) return new UShortConstant ((ushort) v); if (target_type == TypeManager.int32_type) return new IntConstant ((int) v); if (target_type == TypeManager.uint32_type) return new UIntConstant ((uint) v); if (target_type == TypeManager.int64_type) return new LongConstant ((long) v); if (target_type == TypeManager.uint64_type) return new ULongConstant ((ulong) v); if (target_type == TypeManager.float_type) return new FloatConstant ((float) v); if (target_type == TypeManager.double_type) return new DoubleConstant ((double) v); if (target_type == TypeManager.char_type) return new CharConstant ((char) v); if (target_type == TypeManager.decimal_type) return new DecimalConstant ((decimal) v); } if (expr is ShortConstant){ short v = ((ShortConstant) expr).Value; if (target_type == TypeManager.byte_type) return new ByteConstant ((byte) v); if (target_type == TypeManager.sbyte_type) return new SByteConstant ((sbyte) v); if (target_type == TypeManager.ushort_type) return new UShortConstant ((ushort) v); if (target_type == TypeManager.int32_type) return new IntConstant ((int) v); if (target_type == TypeManager.uint32_type) return new UIntConstant ((uint) v); if (target_type == TypeManager.int64_type) return new LongConstant ((long) v); if (target_type == TypeManager.uint64_type) return new ULongConstant ((ulong) v); if (target_type == TypeManager.float_type) return new FloatConstant ((float) v); if (target_type == TypeManager.double_type) return new DoubleConstant ((double) v); if (target_type == TypeManager.char_type) return new CharConstant ((char) v); if (target_type == TypeManager.decimal_type) return new DecimalConstant ((decimal) v); } if (expr is UShortConstant){ ushort v = ((UShortConstant) expr).Value; if (target_type == TypeManager.byte_type) return new ByteConstant ((byte) v); if (target_type == TypeManager.sbyte_type) return new SByteConstant ((sbyte) v); if (target_type == TypeManager.short_type) return new ShortConstant ((short) v); if (target_type == TypeManager.int32_type) return new IntConstant ((int) v); if (target_type == TypeManager.uint32_type) return new UIntConstant ((uint) v); if (target_type == TypeManager.int64_type) return new LongConstant ((long) v); if (target_type == TypeManager.uint64_type) return new ULongConstant ((ulong) v); if (target_type == TypeManager.float_type) return new FloatConstant ((float) v); if (target_type == TypeManager.double_type) return new DoubleConstant ((double) v); if (target_type == TypeManager.char_type) return new CharConstant ((char) v); if (target_type == TypeManager.decimal_type) return new DecimalConstant ((decimal) v); } if (expr is IntConstant){ int v = ((IntConstant) expr).Value; if (target_type == TypeManager.byte_type) return new ByteConstant ((byte) v); if (target_type == TypeManager.sbyte_type) return new SByteConstant ((sbyte) v); if (target_type == TypeManager.short_type) return new ShortConstant ((short) v); if (target_type == TypeManager.ushort_type) return new UShortConstant ((ushort) v); if (target_type == TypeManager.uint32_type) return new UIntConstant ((uint) v); if (target_type == TypeManager.int64_type) return new LongConstant ((long) v); if (target_type == TypeManager.uint64_type) return new ULongConstant ((ulong) v); if (target_type == TypeManager.float_type) return new FloatConstant ((float) v); if (target_type == TypeManager.double_type) return new DoubleConstant ((double) v); if (target_type == TypeManager.char_type) return new CharConstant ((char) v); if (target_type == TypeManager.decimal_type) return new DecimalConstant ((decimal) v); } if (expr is UIntConstant){ uint v = ((UIntConstant) expr).Value; if (target_type == TypeManager.byte_type) return new ByteConstant ((byte) v); if (target_type == TypeManager.sbyte_type) return new SByteConstant ((sbyte) v); if (target_type == TypeManager.short_type) return new ShortConstant ((short) v); if (target_type == TypeManager.ushort_type) return new UShortConstant ((ushort) v); if (target_type == TypeManager.int32_type) return new IntConstant ((int) v); if (target_type == TypeManager.int64_type) return new LongConstant ((long) v); if (target_type == TypeManager.uint64_type) return new ULongConstant ((ulong) v); if (target_type == TypeManager.float_type) return new FloatConstant ((float) v); if (target_type == TypeManager.double_type) return new DoubleConstant ((double) v); if (target_type == TypeManager.char_type) return new CharConstant ((char) v); if (target_type == TypeManager.decimal_type) return new DecimalConstant ((decimal) v); } if (expr is LongConstant){ long v = ((LongConstant) expr).Value; if (target_type == TypeManager.byte_type) return new ByteConstant ((byte) v); if (target_type == TypeManager.sbyte_type) return new SByteConstant ((sbyte) v); if (target_type == TypeManager.short_type) return new ShortConstant ((short) v); if (target_type == TypeManager.ushort_type) return new UShortConstant ((ushort) v); if (target_type == TypeManager.int32_type) return new IntConstant ((int) v); if (target_type == TypeManager.uint32_type) return new UIntConstant ((uint) v); if (target_type == TypeManager.uint64_type) return new ULongConstant ((ulong) v); if (target_type == TypeManager.float_type) return new FloatConstant ((float) v); if (target_type == TypeManager.double_type) return new DoubleConstant ((double) v); if (target_type == TypeManager.char_type) return new CharConstant ((char) v); if (target_type == TypeManager.decimal_type) return new DecimalConstant ((decimal) v); } if (expr is ULongConstant){ ulong v = ((ULongConstant) expr).Value; if (target_type == TypeManager.byte_type) return new ByteConstant ((byte) v); if (target_type == TypeManager.sbyte_type) return new SByteConstant ((sbyte) v); if (target_type == TypeManager.short_type) return new ShortConstant ((short) v); if (target_type == TypeManager.ushort_type) return new UShortConstant ((ushort) v); if (target_type == TypeManager.int32_type) return new IntConstant ((int) v); if (target_type == TypeManager.uint32_type) return new UIntConstant ((uint) v); if (target_type == TypeManager.int64_type) return new LongConstant ((long) v); if (target_type == TypeManager.float_type) return new FloatConstant ((float) v); if (target_type == TypeManager.double_type) return new DoubleConstant ((double) v); if (target_type == TypeManager.char_type) return new CharConstant ((char) v); if (target_type == TypeManager.decimal_type) return new DecimalConstant ((decimal) v); } if (expr is FloatConstant){ float v = ((FloatConstant) expr).Value; if (target_type == TypeManager.byte_type) return new ByteConstant ((byte) v); if (target_type == TypeManager.sbyte_type) return new SByteConstant ((sbyte) v); if (target_type == TypeManager.short_type) return new ShortConstant ((short) v); if (target_type == TypeManager.ushort_type) return new UShortConstant ((ushort) v); if (target_type == TypeManager.int32_type) return new IntConstant ((int) v); if (target_type == TypeManager.uint32_type) return new UIntConstant ((uint) v); if (target_type == TypeManager.int64_type) return new LongConstant ((long) v); if (target_type == TypeManager.uint64_type) return new ULongConstant ((ulong) v); if (target_type == TypeManager.double_type) return new DoubleConstant ((double) v); if (target_type == TypeManager.char_type) return new CharConstant ((char) v); if (target_type == TypeManager.decimal_type) return new DecimalConstant ((decimal) v); } if (expr is DoubleConstant){ double v = ((DoubleConstant) expr).Value; if (target_type == TypeManager.byte_type) return new ByteConstant ((byte) v); if (target_type == TypeManager.sbyte_type) return new SByteConstant ((sbyte) v); if (target_type == TypeManager.short_type) return new ShortConstant ((short) v); if (target_type == TypeManager.ushort_type) return new UShortConstant ((ushort) v); if (target_type == TypeManager.int32_type) return new IntConstant ((int) v); if (target_type == TypeManager.uint32_type) return new UIntConstant ((uint) v); if (target_type == TypeManager.int64_type) return new LongConstant ((long) v); if (target_type == TypeManager.uint64_type) return new ULongConstant ((ulong) v); if (target_type == TypeManager.float_type) return new FloatConstant ((float) v); if (target_type == TypeManager.char_type) return new CharConstant ((char) v); if (target_type == TypeManager.decimal_type) return new DecimalConstant ((decimal) v); } return null; } public override Expression DoResolve (EmitContext ec) { expr = expr.Resolve (ec); if (expr == null) return null; type = ec.DeclSpace.ResolveType (target_type, false, Location); if (type == null) return null; eclass = ExprClass.Value; if (expr is Constant){ Expression e = TryReduce (ec, type); if (e != null) return e; } expr = ConvertExplicit (ec, expr, type, runtime_cast, loc); return expr; } public override void Emit (EmitContext ec) { // // This one will never happen // throw new Exception ("Should not happen"); } } public class StringConcat : Expression { Expression left, right; ArrayList Arguments; protected MethodBase method; public StringConcat(Location loc, Expression left, Expression right) { this.left = left; this.right = right; this.loc = loc; } public override Expression DoResolve (EmitContext ec) { left = left.Resolve (ec); right = right.Resolve (ec); if (left == null || right == null) return null; if (left.Type == null) throw new Exception ( "Resolve returned non null, but did not set the type! (" + left + ") at Line: " + loc.Row); if (right.Type == null) throw new Exception ( "Resolve returned non null, but did not set the type! (" + right + ") at Line: "+ loc.Row); eclass = ExprClass.Value; if (left is StringConstant && right is StringConstant){ return new StringConstant ( ((StringConstant) left).Value + ((StringConstant) right).Value); } Type l = left.Type; Type r = right.Type; if (l == TypeManager.string_type && r == TypeManager.string_type) { type = TypeManager.string_type; method = TypeManager.string_concat_string_string; Arguments = new ArrayList (); Arguments.Add (new Argument (left, Argument.AType.Expression)); Arguments.Add (new Argument (right, Argument.AType.Expression)); return this; } if (l != TypeManager.string_type) { method = TypeManager.string_concat_object_object; left = ConvertImplicit (ec, left, TypeManager.string_type, loc); if (left == null){ Error_OperatorCannotBeApplied (loc, "&", l, r); return null; } 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; } if (r != TypeManager.string_type) { method = TypeManager.string_concat_object_object; right = ConvertImplicit (ec, right, TypeManager.string_type, loc); if (right == null){ Error_OperatorCannotBeApplied (loc, "&", l, r); return null; } 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; } return this; } public override void Emit (EmitContext ec) { ILGenerator ig = ec.ig; 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; } } static public void Error_OperatorCannotBeApplied (Location loc, string name, Type l, Type r) { Report.Error (19, loc, "Operator " + name + " cannot be applied to operands of type '" + TypeManager.MonoBASIC_Name (l) + "' and '" + TypeManager.MonoBASIC_Name (r) + "'"); } } /// /// Binary operators /// public class Binary : Expression { public enum Operator : byte { Exponentiation, Multiply, Division, IntDivision, Modulus, Addition, Subtraction, LeftShift, RightShift, LessThan, GreaterThan, LessThanOrEqual, GreaterThanOrEqual, Equality, Inequality, Like, BitwiseAnd, ExclusiveOr, BitwiseOr, LogicalAnd, LogicalOr, Is, TOP } Operator oper; Expression left, right; // // After resolution, method might contain the operator overload // method. // protected MethodBase method; ArrayList Arguments; bool DelegateOperation; // This must be kept in sync with Operator!!! static string [] oper_names; static Binary () { oper_names = new string [(int) Operator.TOP]; oper_names [(int) Operator.Multiply] = "op_Multiply"; oper_names [(int) Operator.Division] = "op_Division"; oper_names [(int) Operator.IntDivision] = "op_Division"; oper_names [(int) Operator.Modulus] = "op_Modulus"; oper_names [(int) Operator.Addition] = "op_Addition"; oper_names [(int) Operator.Subtraction] = "op_Subtraction"; oper_names [(int) Operator.LeftShift] = "op_LeftShift"; oper_names [(int) Operator.RightShift] = "op_RightShift"; oper_names [(int) Operator.LessThan] = "op_LessThan"; oper_names [(int) Operator.GreaterThan] = "op_GreaterThan"; oper_names [(int) Operator.LessThanOrEqual] = "op_LessThanOrEqual"; oper_names [(int) Operator.GreaterThanOrEqual] = "op_GreaterThanOrEqual"; oper_names [(int) Operator.Equality] = "op_Equality"; oper_names [(int) Operator.Inequality] = "op_Inequality"; oper_names [(int) Operator.BitwiseAnd] = "op_BitwiseAnd"; oper_names [(int) Operator.BitwiseOr] = "op_BitwiseOr"; oper_names [(int) Operator.ExclusiveOr] = "op_ExclusiveOr"; oper_names [(int) Operator.LogicalOr] = "op_LogicalOr"; oper_names [(int) Operator.LogicalAnd] = "op_LogicalAnd"; oper_names [(int) Operator.Is] = "op_Is"; } public Binary (Operator oper, Expression left, Expression right, Location loc) { left = Parser.SetValueRequiredFlag (left); right = Parser.SetValueRequiredFlag (right); 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 /// static string OperName (Operator oper) { switch (oper){ case Operator.Exponentiation: return "^"; case Operator.Multiply: return "*"; case Operator.Division: return "/"; case Operator.IntDivision: return "\\"; case Operator.Modulus: return "Mod"; 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 "And"; case Operator.BitwiseOr: return "Or"; case Operator.ExclusiveOr: return "^"; case Operator.LogicalOr: return "Or"; case Operator.LogicalAnd: return "And"; case Operator.Is: return "Is"; } return oper.ToString (); } public override string ToString () { return "operator " + OperName (oper) + "(" + left.ToString () + ", " + right.ToString () + ")"; } Expression ForceConversion (EmitContext ec, Expression expr, Type target_type) { if (expr.Type == target_type) return expr; return ConvertImplicit (ec, expr, target_type, Location.Null); } public static void Error_OperatorAmbiguous (Location loc, Operator oper, Type l, Type r) { Report.Error ( 34, loc, "Operator '" + OperName (oper) + "' is ambiguous on operands of type '" + TypeManager.MonoBASIC_Name (l) + "' " + "and '" + TypeManager.MonoBASIC_Name (r) + "'"); } // // Handles boolean types also // bool DoNumericPromotions (EmitContext ec, Type l, Type r, Operator oper) { Type conv_left_as = null; Type conv_right_as = null; if (left is NullLiteral) conv_left_as = r; if (right is NullLiteral) conv_right_as = l; // Need not do anything for shift operators, as this will be handled by the // 'CheckShiftArguments' method if (oper == Operator.LeftShift || oper == Operator.RightShift) return true; if (l == TypeManager.bool_type && r == TypeManager.bool_type) { if (IsArithmaticOperator (oper) && oper != Operator.Division) { type = TypeManager.int32_type; conv_left_as = conv_right_as = TypeManager.short_type; } } if (IsBitwiseOperator (oper)) { if (l == TypeManager.decimal_type || l == TypeManager.double_type || l == TypeManager.float_type) { conv_left_as = type = TypeManager.int64_type; l = conv_left_as; } if (r == TypeManager.decimal_type || r == TypeManager.double_type || r == TypeManager.float_type) { conv_right_as = type = TypeManager.int64_type; r = conv_right_as; } } if (oper == Operator.IntDivision) { if (l == TypeManager.decimal_type || r == TypeManager.decimal_type || l == TypeManager.float_type || r == TypeManager.float_type || l == TypeManager.double_type || r == TypeManager.double_type) conv_left_as = conv_right_as = TypeManager.int64_type; l = r = TypeManager.int64_type; } if (IsLogicalOperator (oper)) { if (l == TypeManager.decimal_type) conv_left_as = TypeManager.bool_type; else if (r == TypeManager.decimal_type) conv_right_as = TypeManager.bool_type; } else if ((l == TypeManager.double_type || r == TypeManager.double_type) || (oper == Operator.Exponentiation) || (oper == Operator.Division && !(l == TypeManager.decimal_type || r == TypeManager.decimal_type))) { // // If either operand is of type double, the other operand is // conveted to type double. // type = conv_left_as = conv_right_as = TypeManager.double_type; } else if (l == TypeManager.float_type || r == TypeManager.float_type){ // // if either operand is of type float, the other operand is // converted to type float. // type = conv_left_as = conv_right_as = TypeManager.float_type; } else if (l == TypeManager.decimal_type || r == TypeManager.decimal_type){ type = conv_left_as = conv_right_as = TypeManager.decimal_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. // type = conv_left_as = conv_right_as = TypeManager.int64_type; } else if (l == TypeManager.int32_type || r == TypeManager.int32_type){ type = conv_left_as = conv_right_as = TypeManager.int32_type; } else if (l == TypeManager.short_type || r == TypeManager.short_type){ conv_left_as = conv_right_as = TypeManager.short_type; type = TypeManager.int32_type; } else { type = TypeManager.int32_type; } if (conv_left_as != null) left = ConvertImplicit (ec, left, conv_left_as, loc); if (conv_right_as != null) right = ConvertImplicit (ec, right, conv_right_as, loc); return (left != null) && (right != null); } static public void Error_OperatorCannotBeApplied (Location loc, string name, Type l, Type r) { Report.Error (19, loc, "Operator '" + name + "' cannot be applied to operands of type '" + TypeManager.MonoBASIC_Name (l) + "' and '" + TypeManager.MonoBASIC_Name (r) + "'"); } void Error_OperatorCannotBeApplied () { Error_OperatorCannotBeApplied (loc, OperName (oper), left.Type, right.Type); } static bool is_32_or_64 (Type t) { return (t == TypeManager.int32_type || t == TypeManager.uint32_type || t == TypeManager.int64_type || t == TypeManager.uint64_type); } static bool is_unsigned (Type t) { return (t == TypeManager.uint32_type || t == TypeManager.uint64_type || t == TypeManager.short_type || t == TypeManager.byte_type); } Expression CheckShiftArguments (EmitContext ec) { Expression e; e = ForceConversion (ec, right, TypeManager.int32_type); if (e == null){ Error_OperatorCannotBeApplied (); return null; } type = left.Type; if (left is NullLiteral) { type = right.Type; if (right.Type != TypeManager.bool_type) { left = ConvertImplicit (ec, left, right.Type, loc); if (left == null) { Error_OperatorCannotBeApplied (loc, OperName (oper), left.Type, right.Type); return null; } } } right = e; if (type == TypeManager.bool_type) { left = ConvertImplicit (ec, left, TypeManager.short_type, loc); if (left == null) { Error_OperatorCannotBeApplied (loc, OperName (oper), left.Type, right.Type); return null; } type = left.Type; } int mask = 0; if ( type == TypeManager.byte_type) mask = 7; else if (type == TypeManager.short_type || type == TypeManager.bool_type) mask = 15; else if (type == TypeManager.int32_type || type == TypeManager.uint32_type) mask = 31; else mask = 63; if (mask != 0) { right = new Binary (Binary.Operator.BitwiseAnd, right, new IntLiteral (mask), loc); right = right.DoResolve (ec); } if (type == TypeManager.byte_type || type == TypeManager.short_type || type == TypeManager.int32_type) { type = TypeManager.int32_type; return this; } if (type == TypeManager.int64_type) return this; if ((e = ConvertImplicit (ec, left, TypeManager.int64_type, loc)) != null) { left = e; type = TypeManager.int64_type; return this; } Error_OperatorCannotBeApplied (); return null; } bool IsRelationalOperator (Binary.Operator oper) { return (oper == Operator.Equality || oper == Operator.Inequality || oper == Operator.LessThan || oper == Operator.LessThanOrEqual || oper == Operator.GreaterThan || oper == Operator.GreaterThanOrEqual); } bool IsArithmaticOperator (Binary.Operator oper) { return (oper == Operator.Addition || oper == Operator.Subtraction || oper == Operator.Multiply || oper == Operator.Division || oper == Operator.IntDivision || oper == Operator.Exponentiation || oper == Operator.Modulus); } bool IsShiftOperator (Binary.Operator oper) { return (oper == Operator.LeftShift || oper == Operator.RightShift); } bool IsLogicalOperator (Binary.Operator oper) { return (oper == Operator.LogicalOr || oper == Operator.LogicalAnd); } bool IsBitwiseOperator (Binary.Operator oper) { return (oper == Operator.BitwiseOr || oper == Operator.BitwiseAnd || oper == Operator.ExclusiveOr); } Expression ResolveOperator (EmitContext ec) { Type l = left.Type; Type r = right.Type; Expression left_expr, right_expr; left_expr = right_expr = null; if (oper == Operator.Addition && right is Unary) { Unary unary_right = (Unary) right; if (unary_right.Oper == Unary.Operator.UnaryNegation) { oper = Operator.Subtraction; right = unary_right.Expr; r = right.Type; } } if (TypeManager.IsEnumType (l)) l = TypeManager.EnumToUnderlying (l); if (TypeManager.IsEnumType (r)) r = TypeManager.EnumToUnderlying (r); Type conv_left_as = null; Type conv_right_as = null; if (left is NullLiteral && (r.IsValueType || r == TypeManager.string_type)) { // Just treat nothing as the other type, implicit conversion // will return the default value conv_left_as = r; l = r; } if (right is NullLiteral && (l.IsValueType || l == TypeManager.string_type)) { // Just treat nothing as the other type, implicit conversion // will return the default value conv_right_as = l; r = l; } // deal with objects and reference types first if (l == TypeManager.object_type || r == TypeManager.object_type) { // // operator != (object a, object b) // operator == (object a, object b) // // For this to be used, both arguments have to be reference-types. // Read the rationale on the spec (14.9.6) // // Also, if at compile time we know that the classes do not inherit // one from the other, then we catch the error there. // If other type is a value type, convert it to object if (r == TypeManager.object_type && (l.IsValueType || l == TypeManager.string_type)) left = ConvertImplicit (ec, left, TypeManager.object_type, loc); if (l == TypeManager.object_type && (r.IsValueType || r == TypeManager.string_type)) right = ConvertImplicit (ec, right, TypeManager.object_type, loc); if (left == null || right == null) { Error_OperatorCannotBeApplied (loc, OperName (oper), l, r); return null; } l = left.Type; r = right.Type; if (l == TypeManager.object_type && r == TypeManager.object_type) { string fqn = null; switch (oper) { case Operator.Addition : fqn = "ObjectType.AddObj"; break; case Operator.Subtraction : fqn = "ObjectType.SubObj"; break; case Operator.Multiply : fqn = "ObjectType.MulObj"; break; case Operator.Division : fqn = "ObjectType.DivObj"; break; case Operator.IntDivision : fqn = "ObjectType.IDivObj"; break; case Operator.Modulus : fqn = "ObjectType.ModObj"; break; case Operator.Exponentiation : fqn = "ObjectType.PowObj"; break; case Operator.Like : fqn = "ObjectType.LikeObj"; break; case Operator.Equality : case Operator.Inequality : case Operator.LessThan : case Operator.LessThanOrEqual : case Operator.GreaterThan : case Operator.GreaterThanOrEqual : fqn = "ObjectType.ObjTst"; break; case Operator.BitwiseAnd: fqn = "ObjectType.BitAndObj"; break; case Operator.BitwiseOr: fqn = "ObjectType.BitOrObj"; break; case Operator.ExclusiveOr: fqn = "ObjectType.BitXorObj"; break; case Operator.LeftShift: fqn = "ObjectType.ShiftLeftObj"; break; case Operator.RightShift: fqn = "ObjectType.ShiftRightObj"; break; case Operator.Is: eclass = ExprClass.Value; type = TypeManager.bool_type; return this; } if (fqn == null) { Error_OperatorCannotBeApplied (loc, OperName (oper), l, r); return null; } if (oper == Operator.LeftShift || oper == Operator.RightShift) { right = ConvertImplicit (ec, right, TypeManager.object_type, loc); if (right == null) { Error_OperatorCannotBeApplied (loc, OperName (oper), l, r); return null; } } Expression etmp = Mono.MonoBASIC.Parser.DecomposeQI ( "Microsoft.VisualBasic.CompilerServices." + fqn, Location.Null); ArrayList args = new ArrayList (); args.Add (new Argument (left, Argument.AType.Expression)); args.Add (new Argument (right, Argument.AType.Expression)); if (IsRelationalOperator (oper)) args.Add (new Argument (new BoolConstant (false), Argument.AType.Expression)); if (oper == Operator.Like) args.Add (new Argument(new IntLiteral (0), Argument.AType.Expression)); Expression e = new Invocation (etmp, args, loc); if (IsRelationalOperator (oper)) { e = new Binary (oper, e.Resolve(ec), new IntConstant (0), loc); } return e.Resolve (ec); } else if (!l.IsValueType || !r.IsValueType) { // If one of the operands are reference types and other is object, support for 'Is' operator if (oper == Operator.Is) { eclass = ExprClass.Value; type = TypeManager.bool_type; return this; } } } else if (!l.IsValueType || !r.IsValueType) { if (!l.IsValueType && !r.IsValueType) { // If both the operands are reference types, support for 'Is' operator if (oper == Operator.Is) { eclass = ExprClass.Value; type = TypeManager.bool_type; return this; } } // Either of the operands are reference types if (l.IsSubclassOf (TypeManager.delegate_type) && r.IsSubclassOf (TypeManager.delegate_type)) { if (oper == Operator.Addition || oper == Operator.Subtraction) { Arguments = new ArrayList (); Arguments.Add (new Argument (left, Argument.AType.Expression)); Arguments.Add (new Argument (right, Argument.AType.Expression)); if (oper == Operator.Addition) method = TypeManager.delegate_combine_delegate_delegate; else method = TypeManager.delegate_remove_delegate_delegate; if (l != r) { Error_OperatorCannotBeApplied (); return null; } DelegateOperation = true; type = l; return this; } if (oper != Operator.Equality) { Error_OperatorCannotBeApplied (loc, OperName (oper), l, r); return null; } } bool left_is_string = (left.Type == TypeManager.string_type); bool right_is_string = (right.Type == TypeManager.string_type); if (left_is_string || right_is_string) { if (left is NullLiteral) { left_is_string = true; l = r; } if (right is NullLiteral) { right_is_string = true; r = l; } if (left_is_string && right_is_string) { if (oper == Operator.Addition) { // Both operands are string Expression e = new StringConcat (loc, left, right); return e.Resolve(ec); } if (IsRelationalOperator (oper)) { Expression etmp = Mono.MonoBASIC.Parser.DecomposeQI ("Microsoft.VisualBasic.CompilerServices.StringType.StrCmp", Location.Null); eclass = ExprClass.Value; type = TypeManager.bool_type; ArrayList args = new ArrayList (); args.Add (new Argument(left, Argument.AType.Expression)); args.Add (new Argument(right, Argument.AType.Expression)); args.Add (new Argument(new BoolConstant(false), Argument.AType.Expression)); Expression e = (Expression) new Invocation (etmp, args, loc); e = new Binary (oper, e.Resolve(ec), new IntConstant(0), loc); return e.Resolve(ec); } if (oper == Operator.Like) { Expression etmp = Mono.MonoBASIC.Parser.DecomposeQI ("Microsoft.VisualBasic.CompilerServices.StringType.StrLike", Location.Null); type = TypeManager.bool_type; ArrayList args = new ArrayList (); args.Add (new Argument(left, Argument.AType.Expression)); args.Add (new Argument(right, Argument.AType.Expression)); args.Add (new Argument(new IntLiteral (0), Argument.AType.Expression)); Expression e = (Expression) new Invocation (etmp, args, loc); return e.Resolve (ec); } } Expression other = right_is_string ? left: right; Type other_type = other.Type; // // Disallow arithmatic / shift / logical operators on dates and characters // if (other_type == TypeManager.date_type || other_type == TypeManager.char_type) { if (!(oper == Operator.Addition || IsRelationalOperator (oper) || oper == Operator.Like)) { Error_OperatorCannotBeApplied (loc, OperName (oper), l, r); return null; } } if (oper == Operator.Addition) { if (other_type == TypeManager.void_type) { Error_OperatorCannotBeApplied (loc, OperName (oper), l, r); return null; } if (other_type == TypeManager.date_type || other_type == TypeManager.char_type || other_type == typeof (System.Char[])) { conv_left_as = conv_right_as = TypeManager.string_type; type = TypeManager.string_type; } else { // numeric operand conv_right_as = conv_left_as = TypeManager.double_type; type = TypeManager.double_type; } } else if (IsRelationalOperator (oper)) { if (other_type == TypeManager.char_type || other_type == typeof (System.Char[])) { conv_left_as = conv_right_as = TypeManager.string_type; } else if (other_type == TypeManager.date_type) { conv_right_as = conv_left_as = other_type; } else if (other_type == TypeManager.bool_type) { conv_right_as = conv_left_as = other_type; } else if (! other_type.IsValueType) { // Do Nothing, just return type = TypeManager.bool_type; return this; } else { conv_right_as = conv_left_as = TypeManager.double_type; } type = TypeManager.bool_type; } else if (oper == Operator.Like) { conv_left_as = conv_right_as = TypeManager.string_type; } else if (oper == Operator.LeftShift || oper == Operator.RightShift) { conv_left_as = TypeManager.int64_type; conv_right_as = TypeManager.int32_type; type = TypeManager.int64_type; } else if ( IsLogicalOperator (oper)) { type = conv_right_as = conv_left_as = TypeManager.bool_type; } else if ( IsBitwiseOperator (oper)) { if (other_type == TypeManager.bool_type) { conv_right_as = conv_left_as = TypeManager.bool_type; type = TypeManager.bool_type; } else { conv_left_as = conv_right_as = TypeManager.int64_type; type = TypeManager.int64_type; } } else if (oper == Operator.Exponentiation) { conv_left_as = conv_right_as = TypeManager.double_type; } else if (oper == Operator.IntDivision) { conv_left_as = conv_right_as = TypeManager.int64_type; } else { // Arithmatic operators conv_right_as = conv_left_as = TypeManager.double_type; type = TypeManager.double_type; } } else { // Both are not of type string if (oper == Operator.Equality || oper == Operator.Inequality || oper == Operator.Is) { if (l.IsValueType || r.IsValueType) { Error_OperatorCannotBeApplied (loc, OperName (oper), l, r); return null; } type = TypeManager.bool_type; return this; } else { Error_OperatorCannotBeApplied (loc, OperName (oper), l, r); return null; } } } else if (l == TypeManager.date_type || r == TypeManager.date_type) { // Date with string operations handled above // Only other possiblity is date with date if (oper == Operator.Like) { conv_right_as = conv_left_as = TypeManager.string_type; type = TypeManager.bool_type; } else if (l == TypeManager.date_type && r == TypeManager.date_type) { if (oper == Operator.Addition) { conv_left_as = conv_right_as = TypeManager.string_type; } else if (IsRelationalOperator (oper)) { Expression etmp = Mono.MonoBASIC.Parser.DecomposeQI ("System.DateTime.Compare", Location.Null); eclass = ExprClass.Value; type = TypeManager.bool_type; ArrayList args = new ArrayList (); args.Add (new Argument(left, Argument.AType.Expression)); args.Add (new Argument(right, Argument.AType.Expression)); Expression e = (Expression) new Invocation (etmp, args, loc); e = new Binary (oper, e.Resolve(ec), new IntConstant(0), loc); return e.Resolve(ec); } else { Error_OperatorCannotBeApplied (loc, OperName (oper), l, r); return null; } } else { Error_OperatorCannotBeApplied (loc, OperName (oper), l, r); return null; } } else if (l == TypeManager.char_type || r == TypeManager.char_type) { // char op string handled above if (oper == Operator.Like) { conv_right_as = conv_left_as = TypeManager.string_type; type = TypeManager.bool_type; } else if (l == TypeManager.char_type && r == TypeManager.char_type) { if (oper == Operator.Addition) conv_left_as = conv_right_as = TypeManager.string_type; else if (IsRelationalOperator (oper)) { type = TypeManager.bool_type; } else { Error_OperatorCannotBeApplied (loc, OperName (oper), l, r); return null; } } else { Error_OperatorCannotBeApplied (loc, OperName (oper), l, r); return null; } } else if (l.IsPointer || r.IsPointer) { if (oper == Operator.Addition || oper == Operator.Subtraction) { if (l.IsPointer){ if (r.IsPointer && oper == Operator.Subtraction){ if (r == l) return new PointerArithmetic ( false, left, right, TypeManager.int64_type, loc); } else if (is_32_or_64 (r)) return new PointerArithmetic ( oper == Operator.Addition, left, right, l, loc); } else if (r.IsPointer && is_32_or_64 (l) && oper == Operator.Addition) return new PointerArithmetic ( true, right, left, r, loc); } // // Pointer comparison // if (l.IsPointer && r.IsPointer){ if (oper == Operator.Equality || oper == Operator.Inequality || oper == Operator.LessThan || oper == Operator.LessThanOrEqual || oper == Operator.GreaterThan || oper == Operator.GreaterThanOrEqual){ type = TypeManager.bool_type; return this; } } Error_OperatorCannotBeApplied (loc, OperName (oper), l, r); return null; } else if (oper == Operator.Like) { conv_left_as = conv_right_as = TypeManager.string_type; } else { // Numeric Types DoNumericPromotions (ec, l, r, oper); if (left == null || right == null) { Error_OperatorCannotBeApplied (loc, OperName(oper), l, r); return null; } l = left.Type; r = right.Type; // Required conversions done by 'DoNumericPromotions' method // So Reset 'conv_left_as', 'conv_right_as' conv_left_as = conv_right_as = null; if (l == TypeManager.decimal_type && r == TypeManager.decimal_type) { if (IsRelationalOperator (oper)) { Expression etmp = Mono.MonoBASIC.Parser.DecomposeQI ("System.Decimal.Compare", Location.Null); eclass = ExprClass.Value; type = TypeManager.bool_type; ArrayList args = new ArrayList (); args.Add (new Argument(left, Argument.AType.Expression)); args.Add (new Argument(right, Argument.AType.Expression)); Expression e = (Expression) new Invocation (etmp, args, loc); e = new Binary (oper, e.Resolve(ec), new IntConstant(0), loc); return e.Resolve(ec); } else if (IsArithmaticOperator (oper)) { string fqn = null; if (oper == Operator.Addition) fqn = "System.Decimal.Add"; else if (oper == Operator.Subtraction) fqn = "System.Decimal.Subtract"; else if (oper == Operator.Multiply) fqn = "System.Decimal.Multiply"; else if (oper == Operator.Division) fqn = "System.Decimal.Divide"; else if (oper == Operator.Modulus) fqn = "System.Decimal.Remainder"; if (fqn != null) { Expression etmp = Mono.MonoBASIC.Parser.DecomposeQI (fqn, Location.Null); eclass = ExprClass.Value; type = TypeManager.decimal_type; ArrayList args = new ArrayList (); args.Add (new Argument(left, Argument.AType.Expression)); args.Add (new Argument(right, Argument.AType.Expression)); Expression e = (Expression) new Invocation (etmp, args, loc); return e.Resolve (ec); } } } } bool conv_done = false; if (conv_left_as != null && conv_left_as != l) { conv_done = true; left = ConvertImplicit (ec, left, conv_left_as, loc); if (left == null) { Error_OperatorCannotBeApplied (loc, OperName (oper), l, r); return null; } l = left.Type; } if (conv_right_as != null && conv_right_as != r) { conv_done = true; right = ConvertImplicit (ec, right, conv_right_as, loc); if (right == null) { Error_OperatorCannotBeApplied (loc, OperName (oper), l, r); return null; } r = right.Type; } if (conv_done) return ResolveOperator (ec); if (oper == Operator.Exponentiation) { Expression etmp = Mono.MonoBASIC.Parser.DecomposeQI("System.Math.Pow", loc); ArrayList args = new ArrayList(); args.Add (new Argument (left, Argument.AType.Expression)); args.Add (new Argument (right, Argument.AType.Expression)); Expression e = (Expression) new Invocation (etmp, args, loc); return e.Resolve(ec); } bool overload_failed = false; string op = oper_names [(int) oper]; MethodGroupExpr union = null; left_expr = MemberLookup (ec, l, op, MemberTypes.Method, AllBindingFlags, loc); if (r != l) { right_expr = MemberLookup ( ec, r, op, MemberTypes.Method, AllBindingFlags, loc); union = Invocation.MakeUnionSet (left_expr, right_expr, loc); } else union = (MethodGroupExpr) left_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, Location.Null); if (method != null) { MethodInfo mi = (MethodInfo) method; type = mi.ReturnType; return this; } else { overload_failed = true; } } if (overload_failed) { Error_OperatorCannotBeApplied (); return null; } if (IsRelationalOperator (oper)) { type = TypeManager.bool_type; if (l == TypeManager.bool_type && r == TypeManager.bool_type) { // Reverse the operator - to make it consistent with vbc if (oper == Operator.LessThan) oper = Operator.GreaterThan; else if (oper == Operator.GreaterThan) oper = Operator.LessThan; else if (oper == Operator.LessThanOrEqual) oper = Operator.GreaterThanOrEqual; else if (oper == Operator.GreaterThanOrEqual) oper = Operator.LessThanOrEqual; } } if (IsLogicalOperator (oper)) type = TypeManager.bool_type; if (IsBitwiseOperator (oper)) { if (l == r) { if (l == TypeManager.byte_type || l == TypeManager.short_type || l == TypeManager.bool_type || l == TypeManager.int32_type || l == TypeManager.int64_type) type = l; else { Error_OperatorCannotBeApplied(); return null; } } else { Error_OperatorCannotBeApplied(); return null; } } if (oper == Operator.LeftShift || oper == Operator.RightShift) { return CheckShiftArguments (ec); } return this; } 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; // To support 'Or' argument of AttributeTargets in AttributeUsage if (left is EnumConstant && oper != Operator.BitwiseOr) { left = ((EnumConstant) left).WidenToCompilerConstant(); } if (right is EnumConstant && oper != Operator.BitwiseOr) { right = ((EnumConstant) right).WidenToCompilerConstant(); } if (left is Constant && right is Constant){ Expression e = ConstantFold.BinaryFold ( ec, oper, (Constant) left, (Constant) right, loc); if (e != null) return e; } Expression etmp = ResolveOperator (ec); Type l = left.Type; // if the operands are of type byte/short, convert the result back to short/byte if (l == TypeManager.bool_type || l == TypeManager.short_type || l == TypeManager.byte_type) { if (l == TypeManager.bool_type) l = TypeManager.short_type; if (IsArithmaticOperator (oper) && oper != Operator.Division) { Expression conv_exp = ConvertImplicit (ec, etmp, l, loc); if (conv_exp != null) return conv_exp; } if (IsShiftOperator (oper)) { // No overflow checks are needed if (l == TypeManager.byte_type) return new OpcodeCast (etmp, l, OpCodes.Conv_U1); else return new OpcodeCast (etmp, l, OpCodes.Conv_I2); } } return etmp; } /// /// EmitBranchable is called from Statement.EmitBoolExpression in the /// context of a conditional bool expression. This function will return /// false if it is was possible to use EmitBranchable, or true if it was. /// /// The expression's code is generated, and we will generate a branch to 'target' /// if the resulting expression value is equal to isTrue /// public bool EmitBranchable (EmitContext ec, Label target, bool onTrue) { if (method != null) return false; ILGenerator ig = ec.ig; // // This is more complicated than it looks, but its just to avoid // duplicated tests: basically, we allow ==, !=, >, <, >= and <= // but on top of that we want for == and != to use a special path // if we are comparing against null // if (oper == Operator.Equality || oper == Operator.Inequality || oper == Operator.Is){ bool my_on_true = oper == Operator.Inequality ? onTrue : !onTrue; if (left is NullLiteral){ right.Emit (ec); if (my_on_true) ig.Emit (OpCodes.Brtrue, target); else ig.Emit (OpCodes.Brfalse, target); return true; } else if (right is NullLiteral){ left.Emit (ec); if (my_on_true) ig.Emit (OpCodes.Brtrue, target); else ig.Emit (OpCodes.Brfalse, target); return true; } } else if (!(oper == Operator.LessThan || oper == Operator.GreaterThan || oper == Operator.LessThanOrEqual || oper == Operator.GreaterThanOrEqual || oper == Operator.Is)) return false; left.Emit (ec); right.Emit (ec); bool isUnsigned = is_unsigned (left.Type); switch (oper){ case Operator.Equality: if (onTrue) ig.Emit (OpCodes.Beq, target); else ig.Emit (OpCodes.Bne_Un, target); break; case Operator.Inequality: if (onTrue) ig.Emit (OpCodes.Bne_Un, target); else ig.Emit (OpCodes.Beq, target); break; case Operator.LessThan: if (onTrue) if (isUnsigned) ig.Emit (OpCodes.Blt_Un, target); else ig.Emit (OpCodes.Blt, target); else if (isUnsigned) ig.Emit (OpCodes.Bge_Un, target); else ig.Emit (OpCodes.Bge, target); break; case Operator.GreaterThan: if (onTrue) if (isUnsigned) ig.Emit (OpCodes.Bgt_Un, target); else ig.Emit (OpCodes.Bgt, target); else if (isUnsigned) ig.Emit (OpCodes.Ble_Un, target); else ig.Emit (OpCodes.Ble, target); break; case Operator.LessThanOrEqual: if (onTrue) if (isUnsigned) ig.Emit (OpCodes.Ble_Un, target); else ig.Emit (OpCodes.Ble, target); else if (isUnsigned) ig.Emit (OpCodes.Bgt_Un, target); else ig.Emit (OpCodes.Bgt, target); break; case Operator.GreaterThanOrEqual: if (onTrue) if (isUnsigned) ig.Emit (OpCodes.Bge_Un, target); else ig.Emit (OpCodes.Bge, target); else if (isUnsigned) ig.Emit (OpCodes.Blt_Un, target); else ig.Emit (OpCodes.Blt, target); break; case Operator.Is: if (onTrue) ig.Emit (OpCodes.Beq, target); //Check this else ig.Emit (OpCodes.Bne_Un_S, target); break; default: return false; } return true; } public override void Emit (EmitContext ec) { ILGenerator ig = ec.ig; Type l = left.Type; Type r = right.Type; //Type r = right.Type; OpCode opcode; if (method != null) { // Note that operators are static anyway if (Arguments != null) Invocation.EmitArguments (ec, method, Arguments); if (method is MethodInfo) ig.Emit (OpCodes.Call, (MethodInfo) method); else ig.Emit (OpCodes.Call, (ConstructorInfo) method); if (DelegateOperation) ig.Emit (OpCodes.Castclass, type); return; } // // Handle short-circuit operators differently // than the rest // if (IsLogicalOperator (oper)) { Label load_zero = ig.DefineLabel (); Label load_one = ig.DefineLabel (); Label end = ig.DefineLabel (); left.Emit (ec); if (l != TypeManager.bool_type) { if (l == TypeManager.int64_type) { ec.ig.Emit (OpCodes.Ldc_I8, 0L); ec.ig.Emit (OpCodes.Cgt_Un); } else if (l == TypeManager.float_type) { ec.ig.Emit (OpCodes.Ldc_R4, 0.0F); ec.ig.Emit (OpCodes.Ceq); ec.ig.Emit (OpCodes.Ldc_I4_0); ec.ig.Emit (OpCodes.Ceq); } else if (l == TypeManager.double_type) { ec.ig.Emit (OpCodes.Ldc_R8, 0.0); ec.ig.Emit (OpCodes.Ceq); ec.ig.Emit (OpCodes.Ldc_I4_0); ec.ig.Emit (OpCodes.Ceq); } else { ec.ig.Emit (OpCodes.Ldc_I4_0); ec.ig.Emit (OpCodes.Cgt_Un); } } if (oper == Operator.LogicalAnd) ig.Emit (OpCodes.Brfalse, load_zero); else ig.Emit (OpCodes.Brtrue, load_one); right.Emit (ec); if (r != TypeManager.bool_type) { if (r == TypeManager.int64_type) { ec.ig.Emit (OpCodes.Ldc_I8, 0L); ec.ig.Emit (OpCodes.Cgt_Un); } else if (r == TypeManager.float_type) { ec.ig.Emit (OpCodes.Ldc_R4, 0.0F); ec.ig.Emit (OpCodes.Ceq); ec.ig.Emit (OpCodes.Ldc_I4_0); ec.ig.Emit (OpCodes.Ceq); } else if (r == TypeManager.double_type) { ec.ig.Emit (OpCodes.Ldc_R8, 0.0); ec.ig.Emit (OpCodes.Ceq); ec.ig.Emit (OpCodes.Ldc_I4_0); ec.ig.Emit (OpCodes.Ceq); } else { ec.ig.Emit (OpCodes.Ldc_I4_0); ec.ig.Emit (OpCodes.Cgt_Un); } } ig.Emit (OpCodes.Brtrue, load_one); ig.MarkLabel (load_zero); ig.Emit (OpCodes.Ldc_I4_0); 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: case Operator.IntDivision: 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.Add; } 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: if (l == TypeManager.uint32_type || l == TypeManager.uint64_type) opcode = OpCodes.Shr_Un; else opcode = OpCodes.Shr; break; case Operator.LeftShift: opcode = OpCodes.Shl; break; case Operator.Equality: case Operator.Is: 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); } public bool IsBuiltinOperator { get { return method == null; } } } public class PointerArithmetic : Expression { Expression left, right; bool is_add; // // We assume that 'l' is always a pointer // public PointerArithmetic (bool is_addition, Expression l, Expression r, Type t, Location loc) { type = t; eclass = ExprClass.Variable; this.loc = loc; left = l; right = r; is_add = is_addition; } public override Expression DoResolve (EmitContext ec) { // // We are born fully resolved // return this; } public override void Emit (EmitContext ec) { Type op_type = left.Type; ILGenerator ig = ec.ig; int size = GetTypeSize (op_type.GetElementType ()); if (right.Type.IsPointer){ // // handle (pointer - pointer) // left.Emit (ec); right.Emit (ec); ig.Emit (OpCodes.Sub); if (size != 1){ if (size == 0) ig.Emit (OpCodes.Sizeof, op_type); else IntLiteral.EmitInt (ig, size); ig.Emit (OpCodes.Div); } ig.Emit (OpCodes.Conv_I8); } else { // // handle + and - on (pointer op int) // left.Emit (ec); ig.Emit (OpCodes.Conv_I); right.Emit (ec); if (size != 1){ if (size == 0) ig.Emit (OpCodes.Sizeof, op_type); else IntLiteral.EmitInt (ig, size); ig.Emit (OpCodes.Mul); } if (is_add) ig.Emit (OpCodes.Add); else ig.Emit (OpCodes.Sub); } } } /// /// Implements the ternary conditional operator (?:) /// public class Conditional : Expression { Expression expr, trueExpr, falseExpr; 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 == null) return null; if (expr.Type != TypeManager.bool_type) expr = Expression.ConvertImplicitRequired ( ec, expr, TypeManager.bool_type, loc); trueExpr = trueExpr.Resolve (ec); falseExpr = falseExpr.Resolve (ec); if (trueExpr == null || falseExpr == null) return null; eclass = ExprClass.Value; if (trueExpr.Type == falseExpr.Type) type = trueExpr.Type; else { Expression conv; Type true_type = trueExpr.Type; Type false_type = falseExpr.Type; if (trueExpr is NullLiteral){ type = false_type; return this; } else if (falseExpr is NullLiteral){ type = true_type; return this; } // // First, if an implicit conversion exists from trueExpr // to falseExpr, then the result type is of type falseExpr.Type // conv = ConvertImplicit (ec, trueExpr, false_type, loc); if (conv != null){ // // Check if both can convert implicitl to each other's type // if (ConvertImplicit (ec, falseExpr, true_type, loc) != null){ Error (172, "Can not compute type of conditional expression " + "as '" + TypeManager.MonoBASIC_Name (trueExpr.Type) + "' and '" + TypeManager.MonoBASIC_Name (falseExpr.Type) + "' convert implicitly to each other"); return null; } type = false_type; trueExpr = conv; } else if ((conv = ConvertImplicit(ec, falseExpr, true_type,loc))!= null){ type = true_type; falseExpr = conv; } else { Error (173, "The type of the conditional expression can " + "not be computed because there is no implicit conversion" + " from '" + TypeManager.MonoBASIC_Name (trueExpr.Type) + "'" + " and '" + TypeManager.MonoBASIC_Name (falseExpr.Type) + "'"); return null; } } if (expr is BoolConstant){ BoolConstant bc = (BoolConstant) expr; if (bc.Value) return trueExpr; else return falseExpr; } return this; } public override void Emit (EmitContext ec) { ILGenerator ig = ec.ig; Label false_target = ig.DefineLabel (); Label end_target = ig.DefineLabel (); Statement.EmitBoolExpression (ec, expr, false_target, false); trueExpr.Emit (ec); ig.Emit (OpCodes.Br, end_target); ig.MarkLabel (false_target); falseExpr.Emit (ec); ig.MarkLabel (end_target); } } /// /// Local variables /// public class LocalVariableReference : Expression, IAssignMethod, IMemoryLocation, IVariable { public readonly string Name; public readonly Block Block; VariableInfo variable_info; bool is_readonly; public LocalVariableReference (Block block, string name, Location l) { Block = block; Name = name; loc = l; eclass = ExprClass.Variable; } // Setting 'is_readonly' to false will allow you to create a writable // reference to a read-only variable. This is used by foreach and using. public LocalVariableReference (Block block, string name, Location l, VariableInfo variable_info, bool is_readonly) : this (block, name, l) { this.variable_info = variable_info; this.is_readonly = is_readonly; } public VariableInfo VariableInfo { get { if (variable_info == null) { variable_info = Block.GetVariableInfo (Name); is_readonly = variable_info.ReadOnly; } return variable_info; } } public bool IsAssigned (EmitContext ec, Location loc) { return VariableInfo.IsAssigned (ec, loc); } public bool IsFieldAssigned (EmitContext ec, string name, Location loc) { return VariableInfo.IsFieldAssigned (ec, name, loc); } public void SetAssigned (EmitContext ec) { VariableInfo.SetAssigned (ec); } public void SetFieldAssigned (EmitContext ec, string name) { VariableInfo.SetFieldAssigned (ec, name); } public bool IsReadOnly { get { if (variable_info == null) { variable_info = Block.GetVariableInfo (Name); is_readonly = variable_info.ReadOnly; } return is_readonly; } } public override Expression DoResolve (EmitContext ec) { VariableInfo vi = VariableInfo; if (Block.IsConstant (Name)) { Expression e = Block.GetConstantExpression (Name); vi.Used = true; return e; } if (ec.DoFlowAnalysis && !IsAssigned (ec, loc)) return null; type = vi.VariableType; return this; } override public Expression DoResolveLValue (EmitContext ec, Expression right_side) { VariableInfo vi = VariableInfo; if (ec.DoFlowAnalysis) ec.SetVariableAssigned (vi); Expression e = DoResolve (ec); if (e == null) return null; if (is_readonly){ Error (1604, "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; ig.Emit (OpCodes.Ldloc, vi.LocalBuilder); vi.Used = true; } public void EmitAssign (EmitContext ec, Expression source) { ILGenerator ig = ec.ig; VariableInfo vi = VariableInfo; vi.Assigned = true; source.Emit (ec); ig.Emit (OpCodes.Stloc, vi.LocalBuilder); } public void AddressOf (EmitContext ec, AddressOp mode) { VariableInfo vi = VariableInfo; ec.ig.Emit (OpCodes.Ldloca, vi.LocalBuilder); } } /// /// This represents a reference to a parameter in the intermediate /// representation. /// public class ParameterReference : Expression, IAssignMethod, IMemoryLocation, IVariable { Parameters pars; String name; int idx; public Parameter.Modifier mod; public bool is_ref, is_out; public ParameterReference (Parameters pars, int idx, string name, Location loc) { this.pars = pars; this.idx = idx; this.name = name; this.loc = loc; eclass = ExprClass.Variable; } public bool IsAssigned (EmitContext ec, Location loc) { if (!is_out || !ec.DoFlowAnalysis) return true; if (!ec.CurrentBranching.IsParameterAssigned (idx)) { Report.Error (165, loc, "Use of unassigned local variable '" + name + "'"); return false; } return true; } public bool IsFieldAssigned (EmitContext ec, string field_name, Location loc) { if (!is_out || !ec.DoFlowAnalysis) return true; if (ec.CurrentBranching.IsParameterAssigned (idx)) return true; if (!ec.CurrentBranching.IsParameterAssigned (idx, field_name)) { Report.Error (170, loc, "Use of possibly unassigned field '" + field_name + "'"); return false; } return true; } public void SetAssigned (EmitContext ec) { if (is_out && ec.DoFlowAnalysis) ec.CurrentBranching.SetParameterAssigned (idx); } public void SetFieldAssigned (EmitContext ec, string field_name) { if (is_out && ec.DoFlowAnalysis) ec.CurrentBranching.SetParameterAssigned (idx, field_name); } // // 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.DeclSpace, idx, out mod); is_ref = (mod & Parameter.Modifier.ISBYREF) != 0; // is_out = (mod & Parameter.Modifier.OUT) != 0; eclass = ExprClass.Variable; /* if (is_out && ec.DoFlowAnalysis && !IsAssigned (ec, loc)) return null; */ return this; } override public Expression DoResolveLValue (EmitContext ec, Expression right_side) { type = pars.GetParameterInfo (ec.DeclSpace, idx, out mod); is_ref = (mod & Parameter.Modifier.ISBYREF) != 0; // is_out = (mod & Parameter.Modifier.OUT) != 0; eclass = ExprClass.Variable; /* if (is_out && ec.DoFlowAnalysis) ec.SetParameterAssigned (idx); */ return this; } static void EmitLdArg (ILGenerator ig, int x) { if (x <= 255){ switch (x){ case 0: ig.Emit (OpCodes.Ldarg_0); break; case 1: ig.Emit (OpCodes.Ldarg_1); break; case 2: ig.Emit (OpCodes.Ldarg_2); break; case 3: ig.Emit (OpCodes.Ldarg_3); break; default: ig.Emit (OpCodes.Ldarg_S, (byte) x); break; } } else ig.Emit (OpCodes.Ldarg, x); } // // This method is used by parameters that are references, that are // being passed as references: we only want to pass the pointer (that // is already stored in the parameter, not the address of the pointer, // and not the value of the variable). // public void EmitLoad (EmitContext ec) { ILGenerator ig = ec.ig; int arg_idx = idx; if (!ec.IsStatic) arg_idx++; EmitLdArg (ig, arg_idx); } public override void Emit (EmitContext ec) { ILGenerator ig = ec.ig; int arg_idx = idx; if (!ec.IsStatic) arg_idx++; EmitLdArg (ig, arg_idx); if (!is_ref) return; // // If we are a reference, we loaded on the stack a pointer // Now lets load the real value // LoadFromPtr (ig, type); } public void EmitAssign (EmitContext ec, Expression source) { ILGenerator ig = ec.ig; int arg_idx = idx; if (!ec.IsStatic) arg_idx++; if (is_ref) EmitLdArg (ig, arg_idx); source.Emit (ec); if (is_ref) StoreFromPtr (ig, type); 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, AddressOp mode) { int arg_idx = idx; if (!ec.IsStatic) arg_idx++; if (is_ref){ if (arg_idx <= 255) ec.ig.Emit (OpCodes.Ldarg_S, (byte) arg_idx); else ec.ig.Emit (OpCodes.Ldarg, arg_idx); } else { if (arg_idx <= 255) ec.ig.Emit (OpCodes.Ldarga_S, (byte) arg_idx); else ec.ig.Emit (OpCodes.Ldarga, arg_idx); } } } /// /// Invocation of methods or delegates. /// public class Invocation : ExpressionStatement { public ArrayList Arguments; public Expression expr; MethodBase method = null; bool is_base; bool is_latebinding; bool is_left_hand; // Needed for late bound calls bool is_retval_required; // Needed for late bound calls static Hashtable method_parameter_cache; //static MemberFilter CompareName; static ArrayList tempvars; // For ByRef - different parameter and argument type static bool is_byref_conversion = false; //For ByRef when it is converted static string errorMsg = ""; static Invocation () { method_parameter_cache = new PtrHashtable (); } // // arguments is an ArrayList, but we do not want to typecast, // as it might be null. // // FIXME: only allow expr to be a method invocation or a // delegate invocation (7.5.5) // public Invocation (Expression expr, ArrayList arguments, Location l) { this.expr = expr; if (this.expr is MemberAccess) { ((MemberAccess) this.expr).IsInvocation = true; } this.is_retval_required = false; this.is_left_hand = false; Arguments = arguments; loc = l; //CompareName = new MemberFilter (compare_name_filter); } public Expression Expr { get { return expr; } } public bool IsLeftHand { get { return is_left_hand; } set { is_left_hand = value; } } public bool IsRetvalRequired { get { return is_retval_required; } set { is_retval_required = value; } } public bool IsLateBinding { get { return is_latebinding; } set { is_latebinding = value; } } /// /// 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; } } enum Applicability { Same, Better, Worse }; /// /// 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 Applicability BetterFunction (EmitContext ec, ArrayList args, MethodBase candidate, MethodBase best, bool expanded_form, Location loc) { ParameterData candidate_pd = GetParameterData (candidate); ParameterData best_pd; int argument_count; if (args == null) argument_count = 0; else argument_count = args.Count; int cand_count = candidate_pd.Count; if (cand_count == 0 && argument_count == 0) return Applicability.Same; if (candidate_pd.ParameterModifier (cand_count - 1) != Parameter.Modifier.PARAMS) if (cand_count != argument_count) return Applicability.Worse; best_pd = GetParameterData (best); Applicability res = Applicability.Same; for (int j = 0; j < argument_count; ++j) { //Argument a = (Argument) args [j]; Type ct = candidate_pd.ParameterType (j); Type bt = best_pd.ParameterType (j); if (candidate_pd.ParameterModifier (j) == Parameter.Modifier.PARAMS) if (expanded_form) ct = ct.GetElementType (); if (best_pd.ParameterModifier (j) == Parameter.Modifier.PARAMS) if (expanded_form) bt = bt.GetElementType (); if (ct != bt) { if (!WideningConversionExists (ct, bt)) return Applicability.Worse; res = Applicability.Better; } } if (res == Applicability.Same) if (candidate_pd.Count < best_pd.Count) res = Applicability.Better; else if (candidate_pd.Count > best_pd.Count) res = Applicability.Worse; return res; } public static string FullMethodDesc (MethodBase mb) { string ret_type = ""; if (mb is MethodInfo) ret_type = TypeManager.MonoBASIC_Name (((MethodInfo) mb).ReturnType) + " "; StringBuilder sb = new StringBuilder (ret_type + mb.Name); ParameterData pd = GetParameterData (mb); int count = pd.Count; sb.Append (" ("); for (int i = count; i > 0; ) { i--; sb.Append (pd.ParameterDesc (count - i - 1)); if (i != 0) sb.Append (", "); } sb.Append (")"); return sb.ToString (); } public static MethodGroupExpr MakeUnionSet (Expression mg1, Expression mg2, Location loc) { MemberInfo [] miset; MethodGroupExpr union; if (mg1 == null){ if (mg2 == null) return null; return (MethodGroupExpr) mg2; } else { if (mg2 == null) return (MethodGroupExpr) mg1; } 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 (); foreach (MethodBase l in left_set.Methods){ foreach (MethodBase r in right_set.Methods){ if (l != r) continue; common.Add (r); break; } } miset = new MemberInfo [length1 + length2 - common.Count]; left_set.Methods.CopyTo (miset, 0); int k = length1; foreach (MemberInfo mi in right_set.Methods){ if (!common.Contains (mi)) miset [k++] = mi; } union = new MethodGroupExpr (miset, loc); return union; } protected enum ConversionType { None, Widening, Narrowing }; static ConversionType CheckParameterAgainstArgument (EmitContext ec, ParameterData pd, int i, Argument a, Type ptype) { if (a.ArgType == Argument.AType.NoArg) { return ConversionType.Widening; } Parameter.Modifier a_mod = a.GetParameterModifier () & ~(Parameter.Modifier.REF); Parameter.Modifier p_mod = pd.ParameterModifier (i) & ~(Parameter.Modifier.REF | Parameter.Modifier.OPTIONAL); if (a_mod == p_mod || (a_mod == Parameter.Modifier.NONE && p_mod == Parameter.Modifier.PARAMS)) { // if (a_mod == Parameter.Modifier.NONE) { if (! WideningConversionExists (a.Expr, ptype) ) { if (! NarrowingConversionExists (ec, a.Expr, ptype) ) return ConversionType.None; else return ConversionType.Narrowing; } else return ConversionType.Widening; // } /* if ((a_mod & Parameter.Modifier.ISBYREF) != 0) { Type pt = pd.ParameterType (i); if (!pt.IsByRef) pt = TypeManager.LookupType (pt.FullName + "&"); if (pt != a.Type) return ConversionType.None; } return ConversionType.Widening; */ } else return ConversionType.None; } static bool HasArrayParameter (ParameterData pd) { int c = pd.Count; return c > 0 && (pd.ParameterModifier (c - 1) & Parameter.Modifier.PARAMS) != 0; } static int CountStandardParams (ParameterData pd) { int count = pd.Count; for (int i = 0; i < count; i++) { Parameter.Modifier pm = pd.ParameterModifier (i); if ((pm & (Parameter.Modifier.OPTIONAL | Parameter.Modifier.PARAMS)) != 0) return i; } return count; } static ConversionType IsApplicable (EmitContext ec, ArrayList arguments, MethodBase candidate, out bool expanded) { bool objectArgsPresent; return IsApplicable (ec, arguments, candidate, out expanded, out objectArgsPresent); } /// /// Determines if the candidate method is applicable (section 14.4.2.1) /// to the given set of arguments /// static ConversionType IsApplicable (EmitContext ec, ArrayList arguments, MethodBase candidate, out bool expanded, out bool objectArgsPresent) { int arg_count; Type param_type; expanded = objectArgsPresent = false; int num_narr_conv = 0; // Count the narrowing conversion not involving object // arguments if (arguments == null) arg_count = 0; else arg_count = arguments.Count; ParameterData pd = GetParameterData (candidate); int ps_count = CountStandardParams (pd); int pd_count = pd.Count; // Validate argument count if (ps_count == pd_count) { if (arg_count != pd_count) return ConversionType.None; } else { if (arg_count < ps_count) return ConversionType.None; if (!HasArrayParameter (pd) && arg_count > pd_count) return ConversionType.None; } ConversionType result = ConversionType.Widening; if (arg_count > 0) { result = ConversionType.None; int array_param_index = -1; for (int i = 0; i < arg_count; ++i) { Argument a = (Argument) arguments [i]; param_type = pd.ParameterType (i); Parameter.Modifier mod = pd.ParameterModifier (i); if (array_param_index < 0 && (mod & Parameter.Modifier.PARAMS) != 0) array_param_index = i; bool IsDelegate = TypeManager.IsDelegateType (param_type); if (IsDelegate) { if (a.ArgType == Argument.AType.AddressOf) { a = new Argument ((Expression) a.Expr, Argument.AType.Expression); ArrayList args = new ArrayList(); args.Add (a); string param_name = pd.ParameterDesc(i).Replace('+', '.'); Expression pname = MonoBASIC.Parser.DecomposeQI (param_name, Location.Null); New temp_new = new New ((Expression)pname, args, Location.Null); Expression del_temp = temp_new.DoResolve(ec); if (del_temp == null) return ConversionType.None; a = new Argument (del_temp, Argument.AType.Expression); if (!a.Resolve(ec, Location.Null)) return ConversionType.None; } } else { if (a.ArgType == Argument.AType.AddressOf) return ConversionType.None; } if (a.ArgType != Argument.AType.NoArg && (mod & Parameter.Modifier.REF) != 0) { a = new Argument (a.Expr, Argument.AType.Ref); if (!a.Resolve(ec,Location.Null)) return ConversionType.None; } ConversionType match = ConversionType.None; if (i == array_param_index) match = CheckParameterAgainstArgument (ec, pd, i, a, param_type); if (match == ConversionType.None && array_param_index >= 0 && i >= array_param_index) { expanded = true; param_type = param_type.GetElementType (); } if (match == ConversionType.None) match = CheckParameterAgainstArgument (ec, pd, i, a, param_type); if (match == ConversionType.None) return ConversionType.None; if (match == ConversionType.Narrowing) { result = match; if (a.Expr.Type == TypeManager.object_type) objectArgsPresent = true; else { objectArgsPresent = false; num_narr_conv ++; } } else if (result == ConversionType.None) result = match; } } if (num_narr_conv > 0) // There were narrowing conversions other than those for object arguments objectArgsPresent = false; return result; } internal static ArrayList ReorderArguments (MethodBase mb, ArrayList Arguments, CaseInsensitiveHashtable namedArgs, ref string ErrMsg, Location loc) { ArrayList orderedArgs = new ArrayList (); ParameterData pd = GetParameterData (mb); bool error = false; for (int index = 0; index < pd.Count; index ++) { string paramName = pd.ParameterName (index); if (namedArgs.Contains (paramName)) { if ((pd.ParameterModifier (index) & Parameter.Modifier.PARAMS) == Parameter.Modifier.PARAMS) { error = true; ErrMsg += "\n\t'" + FullMethodDesc (mb) + "': Named argument cannot match a ParamArray parameter"; continue; } int argIndex = (int) namedArgs [paramName]; orderedArgs.Add (Arguments [argIndex]); } else { Parameter.Modifier p_mod = pd.ParameterModifier (index) & Parameter.Modifier.OPTIONAL; if (p_mod == Parameter.Modifier.OPTIONAL) orderedArgs.Add (new Argument (pd.ParameterName (index), new EmptyExpression (), Argument.AType.NoArg)); else { error = true; ErrMsg += "\n\t'" + FullMethodDesc (mb) + "': Argument not specified for parameter '" + paramName + "'"; } } } if (Arguments.Count > orderedArgs.Count) { for (int argIndex = 0; argIndex < Arguments.Count; argIndex ++) { string argName = ((Argument) Arguments [argIndex]).ParamName; bool found = false; for (int paramIndex = 0; paramIndex < pd.Count; paramIndex ++) { string paramName = pd.ParameterName (paramIndex); if (String.Compare (argName, paramName, true) == 0) { found = true; break; } } if (!found) { error = true; ErrMsg += "\n\t'" + FullMethodDesc (mb) + "': '" + argName + "' is not a parameter"; } } } if (error) return null; return orderedArgs; } /* static bool compare_name_filter (MemberInfo m, object filterCriteria) { return (m.Name == ((string) filterCriteria)); } */ public static MethodBase OverloadResolve (EmitContext ec, MethodGroupExpr me, ref ArrayList Arguments, Location loc) { bool isLateBindingRequired; return OverloadResolve (ec, me, ref Arguments, loc, out isLateBindingRequired); } // We need an overload for OverloadResolve because Invocation.DoResolve // must pass Arguments by reference, since a later call to IsApplicable // can change the argument list if optional parameters are defined // in the method declaration public static MethodBase OverloadResolve (EmitContext ec, MethodGroupExpr me, ArrayList Arguments, Location loc) { ArrayList a = Arguments; return OverloadResolve (ec, me, ref a, loc); } /* static string ToString(MethodBase mbase) { if (mbase == null) return "NULL"; if (mbase is MethodBuilder) { MethodBuilder mb = (MethodBuilder) mbase; String res = mb.ReturnType + " ("; ParameterInfo [] parms = mb.GetParameters(); for (int i = 0; i < parms.Length; i++) { if (i != 0) res += " "; res += parms[i].ParameterType; } res += ")"; return res; } return mbase.ToString(); } */ /// /// 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. /// /// isLateBindingRequired : Flag to indicate that this method call is /// is a candidate for late binding. Set to true if /// there is atleast one object argument and we get /// * Two or more candidates that require widening conv /// * Two or more candidates require narrowing conversions /// (for object arguments **only**) /// 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, ref ArrayList Arguments, Location loc, out bool isLateBindingRequired) { MethodBase method = null; int argument_count; ArrayList candidates = new ArrayList (); ArrayList lateBindingCandidates = new ArrayList (); Hashtable expanded_candidates = new Hashtable(); int narrow_count = 0; bool narrowing_candidate = false; errorMsg = ""; isLateBindingRequired = false; CaseInsensitiveHashtable namedArgs = new CaseInsensitiveHashtable (); if (Arguments == null) argument_count = 0; else argument_count = Arguments.Count; if (!CheckNamedArguments (Arguments, loc)) return null; if (!GetNamedArgPos (Arguments, ref namedArgs, loc)) return null; ArrayList newarglist = Arguments; foreach (MethodBase candidate in me.Methods){ bool candidate_expanded, object_args_present; newarglist = Arguments; if (argument_count > 0 && namedArgs.Count != 0) { newarglist = ReorderArguments (candidate, Arguments, namedArgs, ref errorMsg, loc); if (newarglist == null) continue; } ConversionType m = IsApplicable (ec, newarglist, candidate, out candidate_expanded, out object_args_present); if (candidate_expanded) expanded_candidates [candidate] = candidate; if (m == ConversionType.None) continue; else if (m == ConversionType.Narrowing) { if (object_args_present) // if the narrowing conversion was due // to the argument being an object lateBindingCandidates.Add (candidate); if (method == null) { method = candidate; narrowing_candidate = true; } narrow_count++; } else if (m == ConversionType.Widening) { if (method == null || narrowing_candidate) { method = candidate; narrowing_candidate = false; } else { Applicability res = BetterFunction (ec, Arguments, candidate, method, true, loc); if (res == Applicability.Same) continue; // should check it overrides? if (res == Applicability.Better) method = candidate; } candidates.Add (candidate); } } if (candidates.Count == 0) { if (lateBindingCandidates.Count > 1) { isLateBindingRequired = true; return null; } if (narrow_count > 1) { if (lateBindingCandidates.Count == 1) method = (MethodBase) lateBindingCandidates [0]; else method = null; } else if (narrow_count == 1) candidates = null; } else if (candidates.Count == 1) { method = (MethodBase)candidates [0]; candidates = null; } else narrow_count = 0; if (method == null) { // // Okay so we have failed to find anything so we // return by providing info about the closest match // for (int i = 0; i < me.Methods.Length; ++i) { MethodBase c = (MethodBase) me.Methods [i]; ParameterData pd = GetParameterData (c); if (pd.Count != argument_count) continue; bool dummy; if (narrow_count != 0) { if (IsApplicable (ec, Arguments, c, out dummy) == ConversionType.None) continue; Report.Error (1502, loc, "Overloaded match for method '" + FullMethodDesc (c) + "' requires narrowing conversionss"); } VerifyArgumentsCompat (ec, Arguments, argument_count, c, false, null, loc); } return null; } // // Now check that there are no ambiguities i.e the selected method // should be better than all the others // if (candidates != null) { foreach (MethodBase candidate in candidates){ if (candidate == method) continue; if (BetterFunction (ec, Arguments, candidate, method, false, loc) == Applicability.Better) { Report.Error ( 121, loc, "Ambiguous call of '" + me.Name + "' when selecting function due to implicit casts"); return null; } } } // // And now check if the arguments are all compatible, perform conversions // if necessary etc. and return if everything is all right // if (method == null) return null; bool chose_params_expanded = expanded_candidates.Contains (method); newarglist = Arguments; if (argument_count > 0 && namedArgs.Count != 0) { string err = ""; newarglist = ReorderArguments (method, Arguments, namedArgs, ref err, loc); if (newarglist == null) return null; } Arguments = ConstructArgumentList(ec, newarglist, namedArgs, method); if (VerifyArgumentsCompat (ec, Arguments, argument_count, method, chose_params_expanded, null, loc)) { return method; } else return null; } internal static bool CheckNamedArguments (ArrayList Arguments, Location loc) { if (Arguments == null || Arguments.Count == 0) return true; bool namedArgFound = false; for (int index = 0; index < Arguments.Count; index ++) { Argument a = (Argument) Arguments [index]; if (a.ParamName == null || a.ParamName == "") { if (namedArgFound) { Report.Error (30241, loc, "Named argument expected"); return false; } } else namedArgFound = true; } return true; } internal static bool GetNamedArgPos (ArrayList Arguments, ref CaseInsensitiveHashtable namedArgs, Location loc) { namedArgs.Clear (); if (Arguments == null || Arguments.Count == 0) return true; for (int index = 0; index < Arguments.Count; index ++) { Argument a = (Argument) Arguments [index]; if (a.ParamName == null || a.ParamName == "") // none of the args are named return true; if (namedArgs.Contains (a.ParamName)) { Report.Error (30274, loc, "Parameter '" + a.ParamName +"'already has a matching argument"); return false; } namedArgs.Add (a.ParamName, index); } return true; } public static ArrayList ConstructArgumentList (EmitContext ec, ArrayList Arguments, CaseInsensitiveHashtable namedArgs, MethodBase method) { ArrayList newarglist = new ArrayList(); int arg_count = Arguments == null ? 0 : Arguments.Count; ParameterData pd = GetParameterData (method); bool argNamesGiven = (namedArgs.Count > 0); for (int i = 0; i < arg_count; i++) { Argument a = (Argument) Arguments [i]; Type param_type = pd.ParameterType (i); bool IsDelegate = TypeManager.IsDelegateType (param_type); if (a.ArgType == Argument.AType.NoArg) { if (argNamesGiven) a = new Argument (pd.ParameterName (i), pd.DefaultValue (i), Argument.AType.Expression); else a = new Argument (pd.DefaultValue (i), Argument.AType.Expression); a.Resolve (ec, Location.Null); } if (IsDelegate) { if (a.ArgType == Argument.AType.AddressOf) { a = new Argument ((Expression) a.Expr, Argument.AType.Expression); ArrayList args = new ArrayList(); args.Add (a); string param_name = pd.ParameterDesc(i).Replace('+', '.'); Expression pname = MonoBASIC.Parser.DecomposeQI (param_name, Location.Null); New temp_new = new New ((Expression)pname, args, Location.Null); Expression del_temp = temp_new.DoResolve(ec); a = new Argument (del_temp, Argument.AType.Expression); a.Resolve(ec, Location.Null); } } if ((pd.ParameterModifier (i) & Parameter.Modifier.REF) != 0) { a.ArgType = Argument.AType.Ref; a.Resolve(ec, Location.Null); } newarglist.Add (a); } if (HasArrayParameter (pd) && arg_count == pd.Count - 1) return newarglist; for (int i = arg_count; i < pd.Count; i++) { Expression e = pd.DefaultValue (i); Argument a = null; if (argNamesGiven) a = new Argument (e, Argument.AType.Expression); else a = new Argument (pd.ParameterName (i), e, Argument.AType.Expression); if ((pd.ParameterModifier (i) & Parameter.Modifier.REF) != 0) a.ArgType = Argument.AType.Ref; e.Resolve (ec); a.Resolve (ec, Location.Null); newarglist.Add (a); } return newarglist; } public static bool VerifyArgumentsCompat (EmitContext ec, ArrayList Arguments, int argument_count, MethodBase method, bool chose_params_expanded, Type delegate_type, Location loc) { return (VerifyArgumentsCompat (ec, Arguments, argument_count, method, chose_params_expanded, delegate_type, loc, null)); } public static bool VerifyArgumentsCompat (EmitContext ec, ArrayList Arguments, int argument_count, MethodBase method, bool chose_params_expanded, Type delegate_type, Location loc, string InvokingProperty) { ParameterData pd = GetParameterData (method); int pd_count = pd.Count; for (int j = 0; j < argument_count; j++) { Argument a = (Argument) Arguments [j]; Expression a_expr = a.Expr; Type parameter_type = pd.ParameterType(j); if (parameter_type == null) { Error_WrongNumArguments(loc, (InvokingProperty == null)?((delegate_type == null)?FullMethodDesc (method):delegate_type.ToString ()):InvokingProperty, argument_count); return false; } if (pd.ParameterModifier (j) == Parameter.Modifier.PARAMS && chose_params_expanded) parameter_type = TypeManager.TypeToCoreType (parameter_type.GetElementType ()); // By pass conversion for foll. case and handle it in EmitArguments() if (a.ArgType != Argument.AType.Ref && a.Type != parameter_type){ Expression conv; conv = ConvertImplicit (ec, a_expr, parameter_type, loc); if (conv == null) { if (!Location.IsNull (loc)) { if (delegate_type == null) if (InvokingProperty == null) Report.Error (1502, loc, "The best overloaded match for method '" + FullMethodDesc (method) + "' has some invalid arguments"); else Report.Error (1502, loc, "Property '" + InvokingProperty + "' has some invalid arguments"); else Report.Error (1594, loc, "Delegate '" + delegate_type.ToString () + "' has some invalid arguments."); Report.Error (1503, loc, "Argument " + (j+1) + ": Cannot convert from '" + Argument.FullDesc (a) + "' to '" + pd.ParameterDesc (j) + "'"); } return false; } // // Update the argument with the implicit conversion // if (a_expr != conv) a.Expr = conv; } Parameter.Modifier a_mod = a.GetParameterModifier () & ~(Parameter.Modifier.REF); Parameter.Modifier p_mod = pd.ParameterModifier (j) & ~(Parameter.Modifier.REF | Parameter.Modifier.OPTIONAL); if (a_mod != p_mod && pd.ParameterModifier (pd_count - 1) != Parameter.Modifier.PARAMS) { if (!Location.IsNull (loc)) { Report.Error (1502, loc, "The best overloaded match for method '" + FullMethodDesc (method)+ "' has some invalid arguments"); Report.Error (1503, loc, "Argument " + (j+1) + ": Cannot convert from '" + Argument.FullDesc (a) + "' to '" + pd.ParameterDesc (j) + "'"); } return false; } } return true; } public override Expression DoResolveLValue (EmitContext ec, Expression right_side) { this.is_left_hand = true; Expression expr_to_return = DoResolve (ec); if (expr_to_return is IndexerAccess) { IndexerAccess ia = expr_to_return as IndexerAccess; expr_to_return = ia.DoResolveLValue (ec, right_side); } return expr_to_return; } public override Expression DoResolve (EmitContext ec) { // // First, resolve the expression that is used to // trigger the invocation // Expression expr_to_return = null; Expression temp = null; if (expr is BaseAccess) is_base = true; ResolveFlags flags; if ((ec.ReturnType != null) && (expr.ToString() == ec.BlockName)) { ec.InvokingOwnOverload = true; flags = ResolveFlags.MethodGroup; temp = expr.Resolve (ec, flags); ec.InvokingOwnOverload = false; } else { ec.InvokingOwnOverload = false; flags = ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup; temp = expr.Resolve (ec, flags); } if (temp == null) { if (is_left_hand) return null; if (expr is MemberAccess) { MemberAccess m = expr as MemberAccess; if (m.Expr.Type == TypeManager.object_type) { StatementSequence etmp = new StatementSequence (ec.CurrentBlock, loc, expr, Arguments, is_retval_required, is_left_hand); if (!etmp.ResolveArguments (ec)) return null; etmp.GenerateLateBindingStatements(); this.is_latebinding = true; return etmp.Resolve (ec); } } return null; } expr = temp; if (expr is Invocation) { // FIXME Calls which return an Array are not resolved (here or in the grammar) expr = expr.Resolve(ec); } 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); } } // // Next, evaluate all the expressions in the argument list // if (Arguments != null) { foreach (Argument a in Arguments) { if ((a.ArgType == Argument.AType.NoArg) && (!(expr is MethodGroupExpr))) Report.Error (999, "This item cannot have empty arguments"); if (!a.Resolve (ec, loc)) return null; } } if (expr is MethodGroupExpr) { MethodGroupExpr mg = (MethodGroupExpr) expr; bool isLateBindingRequired = false; method = OverloadResolve (ec, mg, ref Arguments, loc, out isLateBindingRequired); if (method == null) { if (isLateBindingRequired) { Expression type_expr = new TypeOf (Parser.DecomposeQI (mg.DeclaringType.Name, loc), loc); StatementSequence etmp = new StatementSequence (ec.CurrentBlock, loc, null, mg.Name, type_expr, Arguments, is_retval_required, is_left_hand); if (! etmp.ResolveArguments (ec)) return null; etmp.GenerateLateBindingStatements (); return etmp.Resolve (ec); } Error (30455, "Could not find any applicable function to invoke for this argument list" + errorMsg); return null; } if ((method as MethodInfo) != null) { MethodInfo mi = method as MethodInfo; type = TypeManager.TypeToCoreType (mi.ReturnType); if (!mi.IsStatic && !mg.IsExplicitImpl && (mg.InstanceExpression == null)) SimpleName.Error_ObjectRefRequired (ec, loc, mi.Name); } if ((method as ConstructorInfo) != null) { ConstructorInfo ci = method as ConstructorInfo; type = TypeManager.void_type; if (!ci.IsStatic && !mg.IsExplicitImpl && (mg.InstanceExpression == null)) SimpleName.Error_ObjectRefRequired (ec, loc, ci.Name); } if (type.IsPointer) { if (!ec.InUnsafe) { UnsafeError (loc); return null; } } eclass = ExprClass.Value; expr_to_return = this; return expr_to_return; } if (expr is PropertyExpr) { PropertyExpr pe = ((PropertyExpr) expr); if (pe.PropertyArgs != null) goto skip_already_resolved_property; pe.PropertyArgs = (ArrayList) Arguments; MethodBase mi = pe.PropertyInfo.GetGetMethod(true); int argCount = 0; if (Arguments != null) argCount = Arguments.Count; bool expanded = false; if (IsApplicable(ec, pe.PropertyArgs, mi, out expanded) != ConversionType.None) { if(VerifyArgumentsCompat (ec, pe.PropertyArgs, argCount, mi, expanded, null, loc, pe.Name)) { expr_to_return = pe.DoResolve (ec); expr_to_return.eclass = ExprClass.PropertyAccess; Arguments = new ArrayList (); return expr_to_return; } else { throw new Exception("Error resolving Property Access expression\n" + pe.ToString()); } } else { pe.PropertyArgs = new ArrayList (); if (VerifyArgumentsCompat (ec, pe.PropertyArgs, 0, mi, false, null, loc, pe.Name)) { expr = pe.DoResolve (ec); expr.eclass = ExprClass.PropertyAccess; } else { throw new Exception("Error resolving Property Access expression\n" + pe.ToString()); } } } skip_already_resolved_property: if (expr.Type.IsArray) { // If we are here, expr must be an ArrayAccess ArrayList idxs = new ArrayList(); foreach (Argument a in Arguments) { idxs.Add (a.Expr); } ElementAccess ea = new ElementAccess (expr, idxs, expr.Location); ArrayAccess aa = new ArrayAccess (ea, expr.Location); expr_to_return = aa.DoResolve(ec); expr_to_return.eclass = ExprClass.Variable; } else { // // check whether this is a indexer // ArrayList idxs = new ArrayList(); foreach (Argument a in Arguments) { idxs.Add (a.Expr); } ElementAccess ea = new ElementAccess (expr, idxs, expr.Location); IndexerAccess ia = new IndexerAccess (ea, expr.Location); if (!is_left_hand) expr_to_return = ia.DoResolve(ec); else expr_to_return = ia.DoResolve(ec); // // Since all the above are failed we need to do // late binding // if (expr_to_return == null) { // We can't resolve now, but we // have to try to access the array with a call // to LateIndexGet/Set in the runtime if (! is_left_hand) { StatementSequence etmp = new StatementSequence (ec.CurrentBlock, loc, ia, Arguments, true, false); if (!etmp.ResolveArguments (ec)) return null; etmp.GenerateLateBindingStatements(); return etmp.Resolve (ec); } return null; } } return expr_to_return; } static void Error_WrongNumArguments (Location loc, String name, int arg_count) { Report.Error (1501, loc, "No overload for method `" + name + "' takes `" + arg_count + "' arguments"); } // // 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 (TypeManager.LookupType (array_type)); IntConstant.EmitInt (ig, count); ig.Emit (OpCodes.Newarr, TypeManager.TypeToCoreType (t)); ig.Emit (OpCodes.Stloc, array); int top = arguments.Count; for (int j = idx; j < top; j++){ a = (Argument) arguments [j]; ig.Emit (OpCodes.Ldloc, array); IntConstant.EmitInt (ig, j - idx); a.Emit (ec); ArrayAccess.EmitStoreOpcode (ig, t); } ig.Emit (OpCodes.Ldloc, array); } /// /// Emits a list of resolved Arguments that are in the arguments /// ArrayList. /// public static void EmitArguments (EmitContext ec, MethodBase mb, ArrayList arguments) { ParameterData pd = GetParameterData (mb); // // If we are calling a params method with no arguments, special case it // if (arguments == null){ if (pd.Count > 0 && pd.ParameterModifier (0) == Parameter.Modifier.PARAMS){ ILGenerator ig = ec.ig; IntConstant.EmitInt (ig, 0); ig.Emit (OpCodes.Newarr, pd.ParameterType (0).GetElementType ()); } return; } int top = arguments.Count; for (int i = 0; i < top; i++){ Argument a = (Argument) arguments [i]; Type parameter_type = pd.ParameterType(i); Type argtype = a.Type; Type arg_expr_type = a.Expr.Type; if (pd.ParameterModifier (i) == Parameter.Modifier.PARAMS){ // // Special case if we are passing the same data as the // params argument, do not put it in an array. // if (pd.ParameterType (i) == a.Type) a.Emit (ec); else EmitParams (ec, i, arguments); return; } if ((a.ArgType == Argument.AType.Ref ) && (parameter_type != arg_expr_type || ! (a.Expr is IMemoryLocation))) { LocalTemporary localtmp = new LocalTemporary (ec, parameter_type ); if((arg_expr_type != parameter_type) && (a.ArgType == Argument.AType.Ref)) { Expression e = ConvertImplicit (ec, a.Expr, parameter_type, Location.Null); is_byref_conversion = true; e.Emit (ec); } else a.Expr.Emit (ec); if (tempvars == null) tempvars = new ArrayList (); if (a.Expr is IMemoryLocation && is_byref_conversion ) { Expression conv; if(argtype.IsByRef) argtype = argtype.GetElementType(); conv = ConvertImplicit (ec, localtmp, argtype, Location.Null); tempvars.Add (new Assign (a.Expr, conv, Location.Null)); } localtmp.Store (ec); a = new Argument (localtmp, a.ArgType); } a.Emit (ec); } if (pd.Count > top && pd.ParameterModifier (top) == Parameter.Modifier.PARAMS){ ILGenerator ig = ec.ig; IntConstant.EmitInt (ig, 0); ig.Emit (OpCodes.Newarr, pd.ParameterType (top).GetElementType ()); } } /// /// is_base tells whether we want to force the use of the 'call' /// opcode instead of using callvirt. Call is required to call /// a specific method, while callvirt will always use the most /// recent method in the vtable. /// /// is_static tells whether this is an invocation on a static method /// /// instance_expr is an expression that represents the instance /// it must be non-null if is_static is false. /// /// method is the method to invoke. /// /// Arguments is the list of arguments to pass to the method or constructor. /// public static void EmitCall (EmitContext ec, bool is_base, bool is_static, Expression instance_expr, MethodBase method, ArrayList Arguments, Location loc) { EmitCall (ec, is_base, is_static, instance_expr, method, Arguments, null, loc); } public static void EmitCall (EmitContext ec, bool is_base, bool is_static, Expression instance_expr, MethodBase method, ArrayList Arguments, ArrayList prop_args, Location loc) { ILGenerator ig = ec.ig; bool struct_call = false; bool is_myclass = false; if (instance_expr is This && ((This) instance_expr).AccessType == This.TypeOfAccess.MyClass) is_myclass = true; Type decl_type = method.DeclaringType; if (!RootContext.StdLib) { // Replace any calls to the system's System.Array type with calls to // the newly created one. if (method == TypeManager.system_int_array_get_length) method = TypeManager.int_array_get_length; else if (method == TypeManager.system_int_array_get_rank) method = TypeManager.int_array_get_rank; else if (method == TypeManager.system_object_array_clone) method = TypeManager.object_array_clone; else if (method == TypeManager.system_int_array_get_length_int) method = TypeManager.int_array_get_length_int; else if (method == TypeManager.system_int_array_get_lower_bound_int) method = TypeManager.int_array_get_lower_bound_int; else if (method == TypeManager.system_int_array_get_upper_bound_int) method = TypeManager.int_array_get_upper_bound_int; else if (method == TypeManager.system_void_array_copyto_array_int) method = TypeManager.void_array_copyto_array_int; } // // This checks the 'ConditionalAttribute' on the method, and the // ObsoleteAttribute // TypeManager.MethodFlags flags = TypeManager.GetMethodFlags (method, loc); if ((flags & TypeManager.MethodFlags.IsObsoleteError) != 0) return; if ((flags & TypeManager.MethodFlags.ShouldIgnore) != 0) return; if (!is_static) { if (decl_type.IsValueType) struct_call = true; // // If this is ourselves, push "this" // if (instance_expr == null) { ig.Emit (OpCodes.Ldarg_0); } else { // // Push the instance expression // if (instance_expr.Type.IsValueType) { // // Special case: calls to a function declared in a // reference-type with a value-type argument need // to have their value boxed. struct_call = true; if (decl_type.IsValueType) { // // If the expression implements IMemoryLocation, then // we can optimize and use AddressOf on the // return. // // If not we have to use some temporary storage for // it. if (instance_expr is IMemoryLocation) { ((IMemoryLocation)instance_expr). AddressOf (ec, AddressOp.LoadStore); } else { Type t = instance_expr.Type; instance_expr.Emit (ec); LocalBuilder temp = ig.DeclareLocal (t); ig.Emit (OpCodes.Stloc, temp); ig.Emit (OpCodes.Ldloca, temp); } } else { instance_expr.Emit (ec); ig.Emit (OpCodes.Box, instance_expr.Type); } } else instance_expr.Emit (ec); } } if (prop_args != null && prop_args.Count > 0) { if (Arguments == null) Arguments = new ArrayList(); for (int i = prop_args.Count-1; i >=0 ; i--) { Arguments.Insert (0,prop_args[i]); } } EmitArguments (ec, method, Arguments); if (is_static || struct_call || is_base || is_myclass) { 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); } } /* static void EmitPropertyArgs (EmitContext ec, ArrayList prop_args) { int top = prop_args.Count; for (int i = 0; i < top; i++) { Argument a = (Argument) prop_args [i]; a.Emit (ec); } } */ public override void Emit (EmitContext ec) { MethodGroupExpr mg = (MethodGroupExpr) this.expr; EmitCall ( ec, is_base, method.IsStatic, mg.InstanceExpression, method, Arguments, loc); } public override void EmitStatement (EmitContext ec) { Emit (ec); // // Pop the return value if there is one // if (method is MethodInfo){ Type ret = ((MethodInfo)method).ReturnType; if ((TypeManager.TypeToCoreType (ret) != TypeManager.void_type) && !this.is_latebinding) { ec.ig.Emit (OpCodes.Pop); if (tempvars != null) { foreach (ExpressionStatement s in tempvars) s.EmitStatement (ec); tempvars.Clear (); } } } } } // // This class is used to "disable" the code generation for the // temporary variable when initializing value types. // class EmptyAddressOf : EmptyExpression, IMemoryLocation { public void AddressOf (EmitContext ec, AddressOp Mode) { // nothing } } /// /// Implements the new expression /// public class New : ExpressionStatement { public readonly ArrayList Arguments; public readonly Expression RequestedType; 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; bool value_target_set = false; public bool isDelegate = false; public New (Expression requested_type, ArrayList arguments, Location l) { RequestedType = requested_type; Arguments = arguments; loc = l; } public Expression ValueTypeVariable { get { return value_target; } set { value_target = value; value_target_set = true; } } // // This function is used to disable the following code sequence for // value type initialization: // // AddressOf (temporary) // Construct/Init // LoadTemporary // // Instead the provide will have provided us with the address on the // stack to store the results. // static Expression MyEmptyExpression; public void DisableTemporaryValueType () { if (MyEmptyExpression == null) MyEmptyExpression = new EmptyAddressOf (); // // To enable this, look into: // test-34 and test-89 and self bootstrapping. // // For instance, we can avoid a copy by using 'newobj' // instead of Call + Push-temp on value types. // value_target = MyEmptyExpression; } public override Expression DoResolve (EmitContext ec) { if (this.isDelegate) { // if its a delegate resolve the type of RequestedType first Expression dtype = RequestedType.Resolve(ec); string ts = (dtype.Type.ToString()).Replace ('+','.'); dtype = Mono.MonoBASIC.Parser.DecomposeQI (ts, Location.Null); type = ec.DeclSpace.ResolveType (dtype, false, loc); } else type = ec.DeclSpace.ResolveType (RequestedType, false, loc); if (type == null) return null; bool IsDelegate = TypeManager.IsDelegateType (type); if (IsDelegate) return (new NewDelegate (type, Arguments, loc)).Resolve (ec); if (type.IsInterface || type.IsAbstract){ Error ( 30376, "It is not possible to create instances of Interfaces " + "or classes marked as MustInherit"); return null; } bool is_struct = false; is_struct = type.IsValueType; eclass = ExprClass.Value; // // SRE returns a match for .ctor () on structs (the object constructor), // so we have to manually ignore it. // if (is_struct && Arguments == null) return this; Expression ml; ml = MemberLookupFinal (ec, type, ".ctor", MemberTypes.Constructor, AllBindingFlags | BindingFlags.Public, loc); if (ml == null) return null; if (! (ml is MethodGroupExpr)){ if (!is_struct){ ml.Error118 ("method group"); return null; } } if (ml != null) { if (Arguments != null){ foreach (Argument a in Arguments){ if (!a.Resolve (ec, loc)) return null; } } method = Invocation.OverloadResolve (ec, (MethodGroupExpr) ml, Arguments, loc); } if (method == null) { if (!is_struct || Arguments.Count > 0) { Error (1501, "New invocation: Can not find a constructor for " + "this argument list"); return null; } } return this; } // // This DoEmit can be invoked in two contexts: // * As a mechanism that will leave a value on the stack (new object) // * As one that wont (init struct) // // You can control whether a value is required on the stack by passing // need_value_on_stack. The code *might* leave a value on the stack // so it must be popped manually // // If we are dealing with a ValueType, we have a few // situations to deal with: // // * The target is a ValueType, and we have been provided // the instance (this is easy, we are being assigned). // // * The target of New is being passed as an argument, // to a boxing operation or a function that takes a // ValueType. // // In this case, we need to create a temporary variable // that is the argument of New. // // Returns whether a value is left on the stack // bool DoEmit (EmitContext ec, bool need_value_on_stack) { bool is_value_type = type.IsValueType; ILGenerator ig = ec.ig; if (is_value_type){ IMemoryLocation ml; // Allow DoEmit() to be called multiple times. // We need to create a new LocalTemporary each time since // you can't share LocalBuilders among ILGeneators. if (!value_target_set) value_target = new LocalTemporary (ec, type); ml = (IMemoryLocation) value_target; ml.AddressOf (ec, AddressOp.Store); } if (method != null) Invocation.EmitArguments (ec, method, Arguments); if (is_value_type){ if (method == null) ig.Emit (OpCodes.Initobj, type); else ig.Emit (OpCodes.Call, (ConstructorInfo) method); if (need_value_on_stack){ value_target.Emit (ec); return true; } return false; } else { ig.Emit (OpCodes.Newobj, (ConstructorInfo) method); return true; } } public override void Emit (EmitContext ec) { DoEmit (ec, true); } public override void EmitStatement (EmitContext ec) { if (DoEmit (ec, false)) ec.ig.Emit (OpCodes.Pop); } } /// /// 14.5.10.2: 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 { Expression requested_base_type; ArrayList initializers; // // The list of Argument types. // This is used to construct the 'newarray' or constructor signature // ArrayList arguments; // // Method used to create the array object. // MethodBase new_method = null; Type array_element_type; Type underlying_type; bool is_one_dimensional = false; bool is_builtin_type = false; bool expect_initializers = false; int num_arguments = 0; int dimensions = 0; string rank; ArrayList array_data; Hashtable bounds; // // The number of array initializers that we can handle // via the InitializeArray method - through EmitStaticInitializers // int num_automatic_initializers; public ArrayCreation (Expression requested_base_type, ArrayList exprs, string rank, ArrayList initializers, Location l) { this.requested_base_type = requested_base_type; this.initializers = initializers; this.rank = rank; loc = l; arguments = new ArrayList (); foreach (Expression e in exprs) { arguments.Add (new Argument (e, Argument.AType.Expression)); num_arguments++; } } public ArrayCreation (Expression requested_base_type, string rank, ArrayList initializers, Location l) { this.requested_base_type = requested_base_type; this.initializers = initializers; this.rank = rank; loc = l; //this.rank = rank.Substring (0, rank.LastIndexOf ("[")); // //string tmp = rank.Substring (rank.LastIndexOf ("[")); // //dimensions = tmp.Length - 1; expect_initializers = true; } public Expression FormArrayType (Expression base_type, int idx_count, string rank) { StringBuilder sb = new StringBuilder (rank); sb.Append ("["); for (int i = 1; i < idx_count; i++) sb.Append (","); sb.Append ("]"); return new ComposedCast (base_type, sb.ToString (), loc); } void Error_IncorrectArrayInitializer () { Error (30567, "Incorrectly structured array initializer"); } public bool CheckIndices (EmitContext ec, ArrayList probe, int idx, bool specified_dims) { if (specified_dims) { Argument a = (Argument) arguments [idx]; if (!a.Resolve (ec, loc)) return false; if (!(a.Expr is Constant)) { Error (150, "A constant value is expected"); return false; } int value = (int) ((Constant) a.Expr).GetValue (); if (value != probe.Count) { Error_IncorrectArrayInitializer (); return false; } bounds [idx] = value; } int child_bounds = -1; foreach (object o in probe) { if (o is ArrayList) { int current_bounds = ((ArrayList) o).Count; if (child_bounds == -1) child_bounds = current_bounds; else if (child_bounds != current_bounds){ Error_IncorrectArrayInitializer (); return false; } bool ret = CheckIndices (ec, (ArrayList) o, idx + 1, specified_dims); if (!ret) return false; } else { if (child_bounds != -1){ Error_IncorrectArrayInitializer (); return false; } Expression tmp = (Expression) o; tmp = tmp.Resolve (ec); if (tmp == null) continue; // Console.WriteLine ("I got: " + tmp); // Handle initialization from vars, fields etc. Expression conv = ConvertImplicitRequired ( ec, tmp, underlying_type, loc); if (conv == null) return false; if (conv is StringConstant) array_data.Add (conv); else if (conv is Constant) { array_data.Add (conv); num_automatic_initializers++; } else array_data.Add (conv); } } return true; } public void UpdateIndices (EmitContext ec) { int i = 0; for (ArrayList probe = initializers; probe != null;) { if (probe.Count > 0 && probe [0] is ArrayList) { Expression e = new IntConstant (probe.Count); arguments.Add (new Argument (e, Argument.AType.Expression)); bounds [i++] = probe.Count; probe = (ArrayList) probe [0]; } else { Expression e = new IntConstant (probe.Count); arguments.Add (new Argument (e, Argument.AType.Expression)); bounds [i++] = probe.Count; probe = null; } } } public bool ValidateInitializers (EmitContext ec, Type array_type) { if (initializers == null) { if (expect_initializers) return false; else return true; } if (underlying_type == null) return 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 // array_data = 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) { Error_IncorrectArrayInitializer (); return false; } return ret; } } /* void Error_NegativeArrayIndex () { Error (284, "Can not create array with a negative size"); } // // Converts 'source' to an int, uint, long or ulong. // Expression ExpressionToArrayArgument (EmitContext ec, Expression source) { Expression target; bool old_checked = ec.CheckState; ec.CheckState = true; target = ConvertImplicit (ec, source, TypeManager.int32_type, loc); if (target == null){ target = ConvertImplicit (ec, source, TypeManager.uint32_type, loc); if (target == null){ target = ConvertImplicit (ec, source, TypeManager.int64_type, loc); if (target == null){ target = ConvertImplicit (ec, source, TypeManager.uint64_type, loc); if (target == null) Expression.Error_CannotConvertImplicit (loc, source.Type, TypeManager.int32_type); } } } ec.CheckState = old_checked; // // Only positive constants are allowed at compile time // if (target is Constant){ if (target is IntConstant){ if (((IntConstant) target).Value < 0){ Error_NegativeArrayIndex (); return null; } } if (target is LongConstant){ if (((LongConstant) target).Value < 0){ Error_NegativeArrayIndex (); return null; } } } return target; } */ // // Creates the type of the array // bool LookupType (EmitContext ec) { StringBuilder array_qualifier = new StringBuilder (rank); // // 'In the first form allocates an array instace of the type that results // from deleting each of the individual expression from the expression list' // if (num_arguments > 0) { array_qualifier.Append ("["); for (int i = num_arguments-1; i > 0; i--) array_qualifier.Append (","); array_qualifier.Append ("]"); } // // Lookup the type // Expression array_type_expr; array_type_expr = new ComposedCast (requested_base_type, array_qualifier.ToString (), loc); type = ec.DeclSpace.ResolveType (array_type_expr, false, loc); if (type == null) return false; underlying_type = type; if (underlying_type.IsArray) underlying_type = TypeManager.TypeToCoreType (underlying_type.GetElementType ()); dimensions = type.GetArrayRank (); return true; } public override Expression DoResolve (EmitContext ec) { int arg_count; if (!LookupType (ec)) return null; // // First step is to validate the initializers and fill // in any missing bits // if (!ValidateInitializers (ec, type)) return null; if (arguments == null) arg_count = 0; else { arg_count = arguments.Count; foreach (Argument a in arguments){ if (!a.Resolve (ec, loc)) return null; Expression real_arg = ExpressionToArrayArgument (ec, a.Expr, loc); if (real_arg == null) return null; a.Expr = real_arg; } } array_element_type = TypeManager.TypeToCoreType (type.GetElementType ()); if (arg_count == 1) { is_one_dimensional = true; eclass = ExprClass.Value; return this; } is_builtin_type = TypeManager.IsBuiltinType (type); if (is_builtin_type) { Expression ml; ml = MemberLookup (ec, type, ".ctor", MemberTypes.Constructor, AllBindingFlags, loc); if (!(ml is MethodGroupExpr)) { ml.Error118 ("method group"); return null; } if (ml == null) { Error (-6, "New invocation: Can not find a constructor for " + "this argument list"); return null; } new_method = Invocation.OverloadResolve (ec, (MethodGroupExpr) ml, arguments, loc); if (new_method == null) { Error (-6, "New invocation: Can not find a constructor for " + "this argument list"); return null; } eclass = ExprClass.Value; return this; } else { ModuleBuilder mb = CodeGen.ModuleBuilder; ArrayList args = new ArrayList (); if (arguments != null) { for (int i = 0; i < arg_count; i++) args.Add (TypeManager.int32_type); } Type [] arg_types = null; if (args.Count > 0) arg_types = new Type [args.Count]; args.CopyTo (arg_types, 0); new_method = mb.GetArrayMethod (type, ".ctor", CallingConventions.HasThis, null, arg_types); if (new_method == null) { Error (-6, "New invocation: Can not find a constructor for " + "this argument list"); return null; } eclass = ExprClass.Value; return this; } } public static byte [] MakeByteBlob (ArrayList array_data, Type underlying_type, Location loc) { int factor; byte [] data; byte [] element; int count = array_data.Count; if (underlying_type.IsEnum) underlying_type = TypeManager.EnumToUnderlying (underlying_type); factor = GetTypeSize (underlying_type); if (factor == 0) throw new Exception ("unrecognized type in MakeByteBlob: " + underlying_type); data = new byte [(count * factor + 4) & ~3]; int idx = 0; for (int i = 0; i < count; ++i) { object v = array_data [i]; if (v is EnumConstant) v = ((EnumConstant) v).Child; if (v is Constant && !(v is StringConstant)) v = ((Constant) v).GetValue (); else { idx += factor; continue; } if (underlying_type == TypeManager.int64_type){ if (!(v is Expression)){ long val = (long) v; for (int j = 0; j < factor; ++j) { data [idx + j] = (byte) (val & 0xFF); val = (val >> 8); } } } else if (underlying_type == TypeManager.uint64_type){ if (!(v is Expression)){ ulong val = (ulong) v; for (int j = 0; j < factor; ++j) { data [idx + j] = (byte) (val & 0xFF); val = (val >> 8); } } } else if (underlying_type == TypeManager.float_type) { if (!(v is Expression)){ element = BitConverter.GetBytes ((float) v); for (int j = 0; j < factor; ++j) data [idx + j] = element [j]; } } else if (underlying_type == TypeManager.double_type) { if (!(v is Expression)){ element = BitConverter.GetBytes ((double) v); for (int j = 0; j < factor; ++j) data [idx + j] = element [j]; } } else if (underlying_type == TypeManager.char_type){ if (!(v is Expression)){ int val = (int) ((char) v); data [idx] = (byte) (val & 0xff); data [idx+1] = (byte) (val >> 8); } } else if (underlying_type == TypeManager.short_type){ if (!(v is Expression)){ int val = (int) ((short) v); data [idx] = (byte) (val & 0xff); data [idx+1] = (byte) (val >> 8); } } else if (underlying_type == TypeManager.ushort_type){ if (!(v is Expression)){ int val = (int) ((ushort) v); data [idx] = (byte) (val & 0xff); data [idx+1] = (byte) (val >> 8); } } else if (underlying_type == TypeManager.int32_type) { if (!(v is Expression)){ int val = (int) v; data [idx] = (byte) (val & 0xff); data [idx+1] = (byte) ((val >> 8) & 0xff); data [idx+2] = (byte) ((val >> 16) & 0xff); data [idx+3] = (byte) (val >> 24); } } else if (underlying_type == TypeManager.uint32_type) { if (!(v is Expression)){ uint val = (uint) v; data [idx] = (byte) (val & 0xff); data [idx+1] = (byte) ((val >> 8) & 0xff); data [idx+2] = (byte) ((val >> 16) & 0xff); data [idx+3] = (byte) (val >> 24); } } else if (underlying_type == TypeManager.sbyte_type) { if (!(v is Expression)){ sbyte val = (sbyte) v; data [idx] = (byte) val; } } else if (underlying_type == TypeManager.byte_type) { if (!(v is Expression)){ byte val = (byte) v; data [idx] = (byte) val; } } else if (underlying_type == TypeManager.bool_type) { if (!(v is Expression)){ bool val = (bool) v; data [idx] = (byte) (val ? 1 : 0); } } else if (underlying_type == TypeManager.decimal_type){ if (!(v is Expression)){ int [] bits = Decimal.GetBits ((decimal) v); int p = idx; for (int j = 0; j < 4; j++){ data [p++] = (byte) (bits [j] & 0xff); data [p++] = (byte) ((bits [j] >> 8) & 0xff); data [p++] = (byte) ((bits [j] >> 16) & 0xff); data [p++] = (byte) (bits [j] >> 24); } } } else throw new Exception ("Unrecognized type in MakeByteBlob: " + underlying_type); idx += factor; } return data; } // // Emits the initializers for the array // void EmitStaticInitializers (EmitContext ec, bool is_expression) { // // First, the static data // FieldBuilder fb; ILGenerator ig = ec.ig; byte [] data = MakeByteBlob (array_data, underlying_type, loc); 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 = array_data.Count; LocalBuilder temp = ig.DeclareLocal (type); ig.Emit (OpCodes.Stloc, temp); MethodInfo set = null; if (dims != 1){ Type [] args; ModuleBuilder mb = null; mb = CodeGen.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 (array_data [i] is Expression) e = (Expression) array_data [i]; if (e != null) { // // Basically we do this for string literals and // other non-literal expressions // if (e is StringConstant || !(e is Constant) || num_automatic_initializers <= 2) { Type etype = e.Type; ig.Emit (OpCodes.Ldloc, temp); for (int idx = 0; idx < dims; idx++) IntConstant.EmitInt (ig, current_pos [idx]); // // If we are dealing with a struct, get the // address of it, so we can store it. // if ((dims == 1) && etype.IsSubclassOf (TypeManager.value_type) && (!TypeManager.IsBuiltinType (etype) || etype == TypeManager.decimal_type)) { if (e is New){ New n = (New) e; // // Let new know that we are providing // the address where to store the results // n.DisableTemporaryValueType (); } ig.Emit (OpCodes.Ldelema, etype); } e.Emit (ec); if (dims == 1) ArrayAccess.EmitStoreOpcode (ig, array_element_type); else ig.Emit (OpCodes.Call, set); } } // // Advance counter // for (int j = dims - 1; j >= 0; 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 EmitArrayArguments (EmitContext ec) { ILGenerator ig = ec.ig; foreach (Argument a in arguments) { Type atype = a.Type; a.Emit (ec); if (atype == TypeManager.uint64_type) ig.Emit (OpCodes.Conv_Ovf_U4); else if (atype == TypeManager.int64_type) ig.Emit (OpCodes.Conv_Ovf_I4); } } void DoEmit (EmitContext ec, bool is_statement) { ILGenerator ig = ec.ig; EmitArrayArguments (ec); if (is_one_dimensional) ig.Emit (OpCodes.Newarr, array_element_type); else { if (is_builtin_type) ig.Emit (OpCodes.Newobj, (ConstructorInfo) new_method); else ig.Emit (OpCodes.Newobj, (MethodInfo) new_method); } if (initializers != null){ // // FIXME: Set this variable correctly. // bool dynamic_initializers = true; if (underlying_type != TypeManager.string_type && underlying_type != TypeManager.object_type) { if (num_automatic_initializers > 2) EmitStaticInitializers (ec, dynamic_initializers || !is_statement); } if (dynamic_initializers) EmitDynamicInitializers (ec, !is_statement); } } public override void Emit (EmitContext ec) { DoEmit (ec, false); } public override void EmitStatement (EmitContext ec) { DoEmit (ec, true); } } /// /// Represents the 'this' construct /// public class This : Expression, IAssignMethod, IMemoryLocation, IVariable { public enum TypeOfAccess : byte { Me, MyClass } Block block; VariableInfo vi; TypeOfAccess access_type; public This (TypeOfAccess access_type, Block block, Location loc) { this.loc = loc; this.block = block; this.access_type = access_type; } public This (Block block, Location loc) { this.loc = loc; this.block = block; this.access_type = TypeOfAccess.Me; } public This (Location loc) { this.loc = loc; this.access_type = TypeOfAccess.Me; } public TypeOfAccess AccessType { get { return access_type; } } public bool IsAssigned (EmitContext ec, Location loc) { if (vi == null) return true; return vi.IsAssigned (ec, loc); } public bool IsFieldAssigned (EmitContext ec, string field_name, Location loc) { if (vi == null) return true; return vi.IsFieldAssigned (ec, field_name, loc); } public void SetAssigned (EmitContext ec) { if (vi != null) vi.SetAssigned (ec); } public void SetFieldAssigned (EmitContext ec, string field_name) { if (vi != null) vi.SetFieldAssigned (ec, field_name); } public override Expression DoResolve (EmitContext ec) { eclass = ExprClass.Variable; type = ec.ContainerType; if (ec.IsStatic){ Error (26, "Keyword this not valid in static code"); return null; } if (block != null) vi = block.ThisVariable; return this; } override public Expression DoResolveLValue (EmitContext ec, Expression right_side) { DoResolve (ec); VariableInfo vi = ec.CurrentBlock.ThisVariable; if (vi != null) vi.SetAssigned (ec); if (ec.TypeContainer is Class){ Error (1604, "Cannot assign to 'this'"); return null; } return this; } public override void Emit (EmitContext ec) { ILGenerator ig = ec.ig; ig.Emit (OpCodes.Ldarg_0); if (ec.TypeContainer is Struct) ig.Emit (OpCodes.Ldobj, type); } public void EmitAssign (EmitContext ec, Expression source) { ILGenerator ig = ec.ig; if (ec.TypeContainer is Struct){ ig.Emit (OpCodes.Ldarg_0); source.Emit (ec); ig.Emit (OpCodes.Stobj, type); } else { source.Emit (ec); ig.Emit (OpCodes.Starg, 0); } } public void AddressOf (EmitContext ec, AddressOp mode) { 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 Expression QueriedType; Type typearg; public TypeOf (Expression queried_type, Location l) { QueriedType = queried_type; loc = l; } public override Expression DoResolve (EmitContext ec) { typearg = ec.DeclSpace.ResolveType (QueriedType, false, loc); if (typearg == null) return null; type = TypeManager.type_type; eclass = ExprClass.Type; return this; } public override void Emit (EmitContext ec) { ec.ig.Emit (OpCodes.Ldtoken, typearg); ec.ig.Emit (OpCodes.Call, TypeManager.system_type_get_type_from_handle); } public Type TypeArg { get { return typearg; } } } /// /// Implements the sizeof expression /// public class SizeOf : Expression { public readonly Expression QueriedType; Type type_queried; public SizeOf (Expression queried_type, Location l) { this.QueriedType = queried_type; loc = l; } public override Expression DoResolve (EmitContext ec) { if (!ec.InUnsafe) { Error (233, "Sizeof may only be used in an unsafe context " + "(consider using System.Runtime.InteropServices.Marshal.SizeOf"); return null; } type_queried = ec.DeclSpace.ResolveType (QueriedType, false, loc); if (type_queried == null) return null; if (!TypeManager.IsUnmanagedType (type_queried)){ Report.Error (208, "Cannot take the size of an unmanaged type (" + TypeManager.MonoBASIC_Name (type_queried) + ")"); return null; } type = TypeManager.int32_type; eclass = ExprClass.Value; return this; } public override void Emit (EmitContext ec) { int size = GetTypeSize (type_queried); if (size == 0) ec.ig.Emit (OpCodes.Sizeof, type_queried); else IntConstant.EmitInt (ec.ig, size); } } /// /// Implements the member access expression /// public class MemberAccess : Expression, ITypeExpression { public readonly string Identifier; Expression expr; Expression member_lookup; bool is_invocation; bool is_left_hand; public MemberAccess (Expression expr, string id, Location l) { this.expr = expr; Identifier = id; loc = l; } public MemberAccess (Expression expr, string id, Location l, bool isInvocation) { this.expr = expr; Identifier = id; loc = l; is_invocation = isInvocation; } public bool IsInvocation { get { return is_invocation; } set { is_invocation = value; } } public bool IsLeftHand { get { return is_left_hand; } set { is_left_hand = value; } } public Expression Expr { get { return expr; } } static void error176 (Location loc, string name) { Report.Error (176, loc, "Static member '" + name + "' cannot be accessed " + "with an instance reference, qualify with a " + "type name instead"); } static bool IdenticalNameAndTypeName (EmitContext ec, Expression left_original, Location loc) { if (left_original == null) return false; if (!(left_original is SimpleName)) return false; SimpleName sn = (SimpleName) left_original; Type t = RootContext.LookupType (ec.DeclSpace, sn.Name, true, loc); if (t != null) return true; return false; } public static Expression ResolveMemberAccess (EmitContext ec, Expression member_lookup, Expression left, Location loc, Expression left_original) { bool left_is_type, left_is_explicit; // If 'left' is null, then we're called from SimpleNameResolve and this is // a member in the currently defining class. if (left == null) { left_is_type = ec.IsStatic || ec.IsFieldInitializer; left_is_explicit = false; // Implicitly default to 'this' unless we're static. if (!ec.IsStatic && !ec.IsFieldInitializer && !ec.InEnumContext) left = ec.This; } else { left_is_type = left is TypeExpr; left_is_explicit = true; } if (member_lookup is FieldExpr){ FieldExpr fe = (FieldExpr) member_lookup; FieldInfo fi = fe.FieldInfo; Type decl_type = fi.DeclaringType; if (fi is FieldBuilder) { Const c = TypeManager.LookupConstant ((FieldBuilder) fi); if (c != null) { object o; if (!c.LookupConstantValue (out o, ec)) return null; object real_value = ((Constant) c.Expr).GetValue (); Expression exp = Constantify (real_value, fi.FieldType); return exp; } } // IsInitOnly is because of MS compatibility, I don't know why but they emit decimal constant as InitOnly if (fi.IsInitOnly && !(fi is FieldBuilder) && fi.FieldType == TypeManager.decimal_type) { object[] attrs = fi.GetCustomAttributes (TypeManager.decimal_constant_attribute_type, false); if (attrs.Length == 1) return new DecimalConstant (((System.Runtime.CompilerServices.DecimalConstantAttribute) attrs [0]).Value); } if (fi.IsLiteral) { Type t = fi.FieldType; object o; if (fi is FieldBuilder) o = TypeManager.GetValue ((FieldBuilder) fi); else o = fi.GetValue (fi); if (decl_type.IsSubclassOf (TypeManager.enum_type)) { if (left_is_explicit && !left_is_type && !IdenticalNameAndTypeName (ec, left_original, loc)) { error176 (loc, fe.FieldInfo.Name); return null; } Expression enum_member = MemberLookup ( ec, decl_type, "value__", MemberTypes.Field, AllBindingFlags, loc); Enum en = TypeManager.LookupEnum (decl_type); Constant c; if (en != null) c = Constantify (o, en.UnderlyingType); else c = Constantify (o, enum_member.Type); return new EnumConstant (c, decl_type); } Expression exp = Constantify (o, t); if (left_is_explicit && !left_is_type) { error176 (loc, fe.FieldInfo.Name); return null; } return exp; } if (fi.FieldType.IsPointer && !ec.InUnsafe){ UnsafeError (loc); return null; } } if (member_lookup is IMemberExpr) { IMemberExpr me = (IMemberExpr) member_lookup; if (left_is_type){ MethodGroupExpr mg = me as MethodGroupExpr; if ((mg != null) && left_is_explicit && left.Type.IsInterface) mg.IsExplicitImpl = left_is_explicit; if (!me.IsStatic){ if (IdenticalNameAndTypeName (ec, left_original, loc)) return member_lookup; SimpleName.Error_ObjectRefRequired (ec, loc, me.Name); return null; } } else { if (!me.IsInstance){ if (IdenticalNameAndTypeName (ec, left_original, loc)) return member_lookup; /*if (left_is_explicit) { error176 (loc, me.Name); return null; }*/ } // // Since we can not check for instance objects in SimpleName, // becaue of the rule that allows types and variables to share // the name (as long as they can be de-ambiguated later, see // IdenticalNameAndTypeName), we have to check whether left // is an instance variable in a static context // // However, if the left-hand value is explicitly given, then // it is already our instance expression, so we aren't in // static context. // if (ec.IsStatic && !left_is_explicit && left is IMemberExpr){ IMemberExpr mexp = (IMemberExpr) left; if (!mexp.IsStatic){ SimpleName.Error_ObjectRefRequired (ec, loc, mexp.Name); return null; } } me.InstanceExpression = left; } return member_lookup; } if (member_lookup is TypeExpr){ member_lookup.Resolve (ec, ResolveFlags.Type); return member_lookup; } Console.WriteLine ("Left is: " + left); Report.Error (-100, loc, "Support for [" + member_lookup + "] is not present yet"); Environment.Exit (0); return null; } public Expression DoResolve (EmitContext ec, Expression right_side, ResolveFlags flags) { if (type != null) throw new Exception (); // // Resolve the expression with flow analysis turned off, we'll do the definite // assignment checks later. This is because we don't know yet what the expression // will resolve to - it may resolve to a FieldExpr and in this case we must do the // definite assignment check on the actual field and not on the whole struct. // Expression original = expr; expr = expr.Resolve (ec, flags | ResolveFlags.DisableFlowAnalysis); if (expr == null) return null; if (expr is SimpleName){ SimpleName child_expr = (SimpleName) expr; Expression new_expr = new SimpleName (child_expr.Name + "." + Identifier, loc); if ((flags & ResolveFlags.MaskExprClass) == ResolveFlags.Type) return new_expr.Resolve (ec, flags); else return new_expr.Resolve (ec, flags | ResolveFlags.MethodGroup | ResolveFlags.VariableOrValue); } int errors = Report.Errors; Type expr_type = expr.Type; if (expr is TypeExpr){ //FIXME: add access level check //if (!ec.DeclSpace.CheckAccessLevel (expr_type)) { // Error (30390, "'" + TypeManager.MonoBASIC_Name (expr_type) + "' " + // "is inaccessible because of its protection level"); // return null; //} if (expr_type == TypeManager.enum_type || expr_type.IsSubclassOf (TypeManager.enum_type)){ Enum en = TypeManager.LookupEnum (expr_type); if (en != null) { object value = en.LookupEnumValue (Identifier); if (value != null){ Constant c = Constantify (value, en.UnderlyingType); return new EnumConstant (c, expr_type); } else { Report.Error (30456, loc, Identifier + " is not found in member list of enum " + en.Name); } } } } if (expr_type.IsPointer){ Error (30311, "The '.' operator can not be applied to pointer operands (" + TypeManager.MonoBASIC_Name (expr_type) + ")"); return null; } member_lookup = MemberLookup (ec, expr_type, Identifier, loc); if (member_lookup == null) { // Error has already been reported. if (errors < Report.Errors) return null; // // Try looking the member up from the same type, if we find // it, we know that the error was due to limited visibility // object lookup = TypeManager.MemberLookup ( expr_type, expr_type, AllMemberTypes, AllBindingFlags | BindingFlags.NonPublic, Identifier); if (lookup == null) { if (expr_type != TypeManager.object_type) Error (30456, "'" + expr_type + "' does not contain a definition for '" + Identifier + "'"); // If this came as a part of Invocation, // Since argumets are not known, return null, // let Invocation's Resolve take care if (is_invocation) return null; else if (! is_left_hand) { StatementSequence etmp = new StatementSequence (ec.CurrentBlock, loc, this, null, true, is_left_hand); etmp.GenerateLateBindingStatements(); return etmp.Resolve (ec); } // if the expression is a left hand side of an assignment, // return null, as we dont know the RHS // Let assign take care of Late Binding return null; } else { if ((expr_type != ec.ContainerType) && ec.ContainerType.IsSubclassOf (expr_type)) { // Although a derived class can access protected members of // its base class it cannot do so through an instance of the // base class (CS1540). If the expr_type is a parent of the // ec.ContainerType and the lookup succeeds with the latter one, // then we are in this situation. lookup = TypeManager.MemberLookup( ec.ContainerType, ec.ContainerType, AllMemberTypes, AllBindingFlags, Identifier); if (lookup != null) Error (1540, "Cannot access protected member '" + expr_type + "." + Identifier + "' " + "via a qualifier of type '" + TypeManager.MonoBASIC_Name (expr_type) + "'; the " + "qualifier must be of type '" + TypeManager.MonoBASIC_Name (ec.ContainerType) + "' " + "(or derived from it)"); else Error (30390, "'" + expr_type + "." + Identifier + "' " + "is inaccessible because of its protection level"); } else Error (30390, "'" + expr_type + "." + Identifier + "' " + "is inaccessible because of its protection level"); } return null; } if ((expr is TypeExpr) && (expr_type.IsSubclassOf (TypeManager.enum_type))) { Enum en = TypeManager.LookupEnum (expr_type); if (en != null) { object value = en.LookupEnumValue (Identifier); expr_type = TypeManager.int32_type; if (value != null) { Constant c = Constantify (value, en.UnderlyingType); return new EnumConstant (c, en.UnderlyingType); } else { Report.Error (30456, loc, Identifier + " is not found in member list of enum " + en.Name); } } } if (member_lookup is TypeExpr){ member_lookup.Resolve (ec, ResolveFlags.Type); return member_lookup; } else if ((flags & ResolveFlags.MaskExprClass) == ResolveFlags.Type) return null; member_lookup = ResolveMemberAccess (ec, member_lookup, expr, loc, original); if (member_lookup == null) return null; // The following DoResolve/DoResolveLValue will do the definite assignment // check. if (right_side != null) member_lookup = member_lookup.DoResolveLValue (ec, right_side); else member_lookup = member_lookup.DoResolve (ec); return member_lookup; } public override Expression DoResolve (EmitContext ec) { return DoResolve (ec, null, ResolveFlags.VariableOrValue | ResolveFlags.SimpleName | ResolveFlags.Type); } public override Expression DoResolveLValue (EmitContext ec, Expression right_side) { return DoResolve (ec, right_side, ResolveFlags.VariableOrValue | ResolveFlags.SimpleName | ResolveFlags.Type); } public Expression DoResolveType (EmitContext ec) { return DoResolve (ec, null, ResolveFlags.Type); } public override void Emit (EmitContext ec) { throw new Exception ("Should not happen"); } public override string ToString () { return expr + "." + Identifier; } } /// /// Implements checked expressions /// public class CheckedExpr : Expression { public Expression Expr; public CheckedExpr (Expression e, Location l) { Expr = e; loc = l; } public override Expression DoResolve (EmitContext ec) { bool last_const_check = ec.ConstantCheckState; ec.ConstantCheckState = true; Expr = Expr.Resolve (ec); ec.ConstantCheckState = last_const_check; if (Expr == null) return null; if (Expr is Constant) return Expr; eclass = Expr.eclass; type = Expr.Type; return this; } public override void Emit (EmitContext ec) { bool last_check = ec.CheckState; bool last_const_check = ec.ConstantCheckState; ec.CheckState = true; ec.ConstantCheckState = true; Expr.Emit (ec); ec.CheckState = last_check; ec.ConstantCheckState = last_const_check; } } /// /// Implements the unchecked expression /// public class UnCheckedExpr : Expression { public Expression Expr; public UnCheckedExpr (Expression e, Location l) { Expr = e; loc = l; } public override Expression DoResolve (EmitContext ec) { bool last_const_check = ec.ConstantCheckState; ec.ConstantCheckState = false; Expr = Expr.Resolve (ec); ec.ConstantCheckState = last_const_check; if (Expr == null) return null; if (Expr is Constant) return Expr; eclass = Expr.eclass; type = Expr.Type; return this; } public override void Emit (EmitContext ec) { bool last_check = ec.CheckState; bool last_const_check = ec.ConstantCheckState; ec.CheckState = false; ec.ConstantCheckState = false; Expr.Emit (ec); ec.CheckState = last_check; ec.ConstantCheckState = last_const_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 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; foreach (Argument a in Arguments){ if (!a.Resolve (ec, loc)) return false; } return true; } Expression MakePointerAccess () { Type t = Expr.Type; if (t == TypeManager.void_ptr_type){ Error ( 242, "The array index operation is not valid for void pointers"); return null; } if (Arguments.Count != 1){ Error ( 196, "A pointer must be indexed by a single value"); return null; } Expression p = new PointerArithmetic (true, Expr, ((Argument)Arguments [0]).Expr, t, loc); return new Indirection (p, loc); } 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. // Type t = Expr.Type; if (t.IsArray) return (new ArrayAccess (this, loc)).Resolve (ec); else if (t.IsPointer) return MakePointerAccess (); else return (new IndexerAccess (this, loc)).Resolve (ec); } public override Expression DoResolveLValue (EmitContext ec, Expression right_side) { if (!CommonResolve (ec)) return null; Type t = Expr.Type; if (t.IsArray) return (new ArrayAccess (this, loc)).ResolveLValue (ec, right_side); else if (t.IsPointer) return MakePointerAccess (); else return (new IndexerAccess (this, loc)).ResolveLValue (ec, right_side); } public override void Emit (EmitContext ec) { throw new Exception ("Should never be reached"); } } /// /// Implements array access /// public class ArrayAccess : Expression, IAssignMethod, IMemoryLocation { // // Points to our "data" repository // ElementAccess ea; LocalTemporary [] cached_locations; public ArrayAccess (ElementAccess ea_data, Location l) { ea = ea_data; eclass = ExprClass.Variable; loc = l; } public override Expression DoResolve (EmitContext ec) { //ExprClass eclass = ea.Expr.eclass; #if false // As long as the type is valid if (!(eclass == ExprClass.Variable || eclass == ExprClass.PropertyAccess || eclass == ExprClass.Value)) { ea.Expr.Error118 ("variable or value"); return null; } #endif Type t = ea.Expr.Type; /* if (t == typeof (System.Object)) { // We can't resolve now, but we // have to try to access the array with a call // to LateIndexGet in the runtime Expression lig_call_expr = Mono.MonoBASIC.Parser.DecomposeQI("Microsoft.VisualBasic.CompilerServices.LateBinding.LateIndexGet", Location.Null); Expression obj_type = Mono.MonoBASIC.Parser.DecomposeQI("System.Object", Location.Null); ArrayList adims = new ArrayList(); ArrayList ainit = new ArrayList(); foreach (Argument a in ea.Arguments) ainit.Add ((Expression) a.Expr); adims.Add ((Expression) new IntLiteral (ea.Arguments.Count)); Expression oace = new ArrayCreation (obj_type, adims, "", ainit, Location.Null); ArrayList args = new ArrayList(); args.Add (new Argument(ea.Expr, Argument.AType.Expression)); args.Add (new Argument(oace, Argument.AType.Expression)); args.Add (new Argument(NullLiteral.Null, Argument.AType.Expression)); Expression lig_call = new Invocation (lig_call_expr, args, Location.Null); lig_call = lig_call.Resolve(ec); return lig_call; } */ if (t.GetArrayRank () != ea.Arguments.Count){ ea.Error (22, "Incorrect number of indexes for array " + " expected: " + t.GetArrayRank () + " got: " + ea.Arguments.Count); return null; } type = TypeManager.TypeToCoreType (t.GetElementType ()); if (type.IsPointer && !ec.InUnsafe){ UnsafeError (ea.Location); return null; } foreach (Argument a in ea.Arguments){ Type argtype = a.Type; if (argtype == TypeManager.int32_type || argtype == TypeManager.uint32_type || argtype == TypeManager.int64_type || argtype == TypeManager.uint64_type) continue; // // Mhm. This is strage, because the Argument.Type is not the same as // Argument.Expr.Type: the value changes depending on the ref/out setting. // // Wonder if I will run into trouble for this. // a.Expr = ExpressionToArrayArgument (ec, a.Expr, ea.Location); if (a.Expr == null) return null; } eclass = ExprClass.Variable; return this; } /// /// Emits the right opcode to load an object of Type 't' /// from an array of T /// static public void EmitLoadOpcode (ILGenerator ig, Type type) { if (type == TypeManager.byte_type || type == TypeManager.bool_type) ig.Emit (OpCodes.Ldelem_U1); else if (type == TypeManager.sbyte_type) ig.Emit (OpCodes.Ldelem_I1); else if (type == TypeManager.short_type) ig.Emit (OpCodes.Ldelem_I2); else if (type == TypeManager.ushort_type || type == TypeManager.char_type) ig.Emit (OpCodes.Ldelem_U2); else if (type == TypeManager.int32_type) ig.Emit (OpCodes.Ldelem_I4); else if (type == TypeManager.uint32_type) ig.Emit (OpCodes.Ldelem_U4); else if (type == TypeManager.uint64_type) ig.Emit (OpCodes.Ldelem_I8); else if (type == TypeManager.int64_type) ig.Emit (OpCodes.Ldelem_I8); else if (type == TypeManager.float_type) ig.Emit (OpCodes.Ldelem_R4); else if (type == TypeManager.double_type) ig.Emit (OpCodes.Ldelem_R8); else if (type == TypeManager.intptr_type) ig.Emit (OpCodes.Ldelem_I); else if (type.IsValueType){ ig.Emit (OpCodes.Ldelema, type); ig.Emit (OpCodes.Ldobj, type); } else ig.Emit (OpCodes.Ldelem_Ref); } /// /// Emits the right opcode to store an object of Type 't' /// from an array of T. /// static public void EmitStoreOpcode (ILGenerator ig, Type t) { t = TypeManager.TypeToCoreType (t); if (TypeManager.IsEnumType (t) && t != TypeManager.enum_type) t = TypeManager.EnumToUnderlying (t); if (t == TypeManager.byte_type || t == TypeManager.sbyte_type || t == TypeManager.bool_type) ig.Emit (OpCodes.Stelem_I1); else if (t == TypeManager.short_type || t == TypeManager.ushort_type || t == TypeManager.char_type) ig.Emit (OpCodes.Stelem_I2); else if (t == TypeManager.int32_type || t == TypeManager.uint32_type) ig.Emit (OpCodes.Stelem_I4); else if (t == TypeManager.int64_type || t == TypeManager.uint64_type) ig.Emit (OpCodes.Stelem_I8); else if (t == TypeManager.float_type) ig.Emit (OpCodes.Stelem_R4); else if (t == TypeManager.double_type) ig.Emit (OpCodes.Stelem_R8); else if (t == TypeManager.intptr_type) ig.Emit (OpCodes.Stelem_I); else if (t.IsValueType){ ig.Emit (OpCodes.Stobj, t); } else ig.Emit (OpCodes.Stelem_Ref); } MethodInfo FetchGetMethod () { ModuleBuilder mb = CodeGen.ModuleBuilder; int arg_count = ea.Arguments.Count; Type [] args = new Type [arg_count]; MethodInfo get; for (int i = 0; i < arg_count; i++){ //args [i++] = a.Type; args [i] = TypeManager.int32_type; } get = mb.GetArrayMethod ( ea.Expr.Type, "Get", CallingConventions.HasThis | CallingConventions.Standard, type, args); return get; } MethodInfo FetchAddressMethod () { ModuleBuilder mb = CodeGen.ModuleBuilder; int arg_count = ea.Arguments.Count; Type [] args = new Type [arg_count]; MethodInfo address; string ptr_type_name; Type ret_type; ptr_type_name = type.FullName + "&"; ret_type = Type.GetType (ptr_type_name); // // It is a type defined by the source code we are compiling // if (ret_type == null){ ret_type = mb.GetType (ptr_type_name); } for (int i = 0; i < arg_count; i++){ //args [i++] = a.Type; args [i] = TypeManager.int32_type; } address = mb.GetArrayMethod ( ea.Expr.Type, "Address", CallingConventions.HasThis | CallingConventions.Standard, ret_type, args); return address; } // // Load the array arguments into the stack. // // If we have been requested to cache the values (cached_locations array // initialized), then load the arguments the first time and store them // in locals. otherwise load from local variables. // void LoadArrayAndArguments (EmitContext ec) { ILGenerator ig = ec.ig; if (cached_locations == null){ ea.Expr.Emit (ec); foreach (Argument a in ea.Arguments){ Type argtype = a.Expr.Type; a.Expr.Emit (ec); if (argtype == TypeManager.int64_type) ig.Emit (OpCodes.Conv_Ovf_I); else if (argtype == TypeManager.uint64_type) ig.Emit (OpCodes.Conv_Ovf_I_Un); } return; } if (cached_locations [0] == null){ cached_locations [0] = new LocalTemporary (ec, ea.Expr.Type); ea.Expr.Emit (ec); ig.Emit (OpCodes.Dup); cached_locations [0].Store (ec); int j = 1; foreach (Argument a in ea.Arguments){ Type argtype = a.Expr.Type; cached_locations [j] = new LocalTemporary (ec, TypeManager.intptr_type /* a.Expr.Type */); a.Expr.Emit (ec); if (argtype == TypeManager.int64_type) ig.Emit (OpCodes.Conv_Ovf_I); else if (argtype == TypeManager.uint64_type) ig.Emit (OpCodes.Conv_Ovf_I_Un); ig.Emit (OpCodes.Dup); cached_locations [j].Store (ec); j++; } return; } foreach (LocalTemporary lt in cached_locations) lt.Emit (ec); } public new void CacheTemporaries (EmitContext ec) { cached_locations = new LocalTemporary [ea.Arguments.Count + 1]; } public override void Emit (EmitContext ec) { int rank = ea.Expr.Type.GetArrayRank (); ILGenerator ig = ec.ig; LoadArrayAndArguments (ec); if (rank == 1) EmitLoadOpcode (ig, type); else { MethodInfo method; method = FetchGetMethod (); ig.Emit (OpCodes.Call, method); } } public void EmitAssign (EmitContext ec, Expression source) { int rank = ea.Expr.Type.GetArrayRank (); ILGenerator ig = ec.ig; Type t = source.Type; LoadArrayAndArguments (ec); // // The stobj opcode used by value types will need // an address on the stack, not really an array/array // pair // if (rank == 1){ if (t == TypeManager.enum_type || t == TypeManager.decimal_type || (t.IsSubclassOf (TypeManager.value_type) && !TypeManager.IsEnumType (t) && !TypeManager.IsBuiltinType (t))) ig.Emit (OpCodes.Ldelema, t); } source.Emit (ec); if (rank == 1) EmitStoreOpcode (ig, t); else { ModuleBuilder mb = CodeGen.ModuleBuilder; int arg_count = ea.Arguments.Count; Type [] args = new Type [arg_count + 1]; MethodInfo set; for (int i = 0; i < arg_count; i++){ //args [i++] = a.Type; args [i] = TypeManager.int32_type; } args [arg_count] = type; set = mb.GetArrayMethod ( ea.Expr.Type, "Set", CallingConventions.HasThis | CallingConventions.Standard, TypeManager.void_type, args); ig.Emit (OpCodes.Call, set); } } public void AddressOf (EmitContext ec, AddressOp mode) { int rank = ea.Expr.Type.GetArrayRank (); ILGenerator ig = ec.ig; LoadArrayAndArguments (ec); if (rank == 1){ ig.Emit (OpCodes.Ldelema, type); } else { MethodInfo address = FetchAddressMethod (); ig.Emit (OpCodes.Call, address); } } } class Indexers { public ArrayList getters, setters; static Hashtable map; static Indexers () { map = new Hashtable (); } Indexers (MemberInfo [] mi) { foreach (PropertyInfo property in mi){ MethodInfo get, set; get = property.GetGetMethod (true); if (get != null){ if (getters == null) getters = new ArrayList (); getters.Add (get); } set = property.GetSetMethod (true); if (set != null){ if (setters == null) setters = new ArrayList (); setters.Add (set); } } } static private Indexers GetIndexersForTypeOrInterface (Type caller_type, Type lookup_type) { Indexers ix = (Indexers) map [lookup_type]; if (ix != null) return ix; string p_name = TypeManager.IndexerPropertyName (lookup_type); MemberInfo [] mi = TypeManager.MemberLookup ( caller_type, lookup_type, MemberTypes.Property, BindingFlags.Public | BindingFlags.Instance, p_name); if (mi == null || mi.Length == 0) return null; ix = new Indexers (mi); map [lookup_type] = ix; return ix; } static public Indexers GetIndexersForType (Type caller_type, Type lookup_type, Location loc) { Indexers ix = (Indexers) map [lookup_type]; if (ix != null) return ix; ix = GetIndexersForTypeOrInterface (caller_type, lookup_type); if (ix != null) return ix; Type [] ifaces = TypeManager.GetInterfaces (lookup_type); if (ifaces != null) { foreach (Type itype in ifaces) { ix = GetIndexersForTypeOrInterface (caller_type, itype); if (ix != null) return ix; } } if (lookup_type != TypeManager.object_type) Report.Error (21, loc, "Type '" + TypeManager.MonoBASIC_Name (lookup_type) + "' does not have any indexers defined"); return null; } } /// /// Expressions that represent an indexer call. /// public class IndexerAccess : Expression, IAssignMethod { // // Points to our "data" repository // MethodInfo get, set; Indexers ilist; ArrayList set_arguments; //bool is_base_indexer; protected Type indexer_type; protected Type current_type; protected Expression instance_expr; protected ArrayList arguments; public IndexerAccess (ElementAccess ea, Location loc) : this (ea.Expr, false, loc) { this.arguments = ea.Arguments; } protected IndexerAccess (Expression instance_expr, bool is_base_indexer, Location loc) { this.instance_expr = instance_expr; //this.is_base_indexer = is_base_indexer; this.eclass = ExprClass.Value; this.loc = loc; } public Expression Instance { get { return instance_expr; } } public ArrayList Arguments { get { return arguments; } } protected virtual bool CommonResolve (EmitContext ec) { indexer_type = instance_expr.Type; current_type = ec.ContainerType; return true; } public override Expression DoResolve (EmitContext ec) { if (!CommonResolve (ec)) return null; // // 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 ( current_type, indexer_type, 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, loc), arguments, loc); if (get == null){ if (instance_expr.Type != TypeManager.object_type) Error (30524, "indexer can not be used in this context, because " + "it lacks a 'get' accessor"); return null; } type = get.ReturnType; if (type.IsPointer && !ec.InUnsafe){ UnsafeError (loc); return null; } eclass = ExprClass.IndexerAccess; return this; } public override Expression DoResolveLValue (EmitContext ec, Expression right_side) { if (!CommonResolve (ec)) return null; Type right_type = right_side.Type; if (ilist == null) ilist = Indexers.GetIndexersForType ( current_type, indexer_type, loc); if (ilist != null && ilist.setters != null && ilist.setters.Count > 0){ set_arguments = (ArrayList) arguments.Clone (); set_arguments.Add (new Argument (right_side, Argument.AType.Expression)); set = (MethodInfo) Invocation.OverloadResolve ( ec, new MethodGroupExpr (ilist.setters, loc), set_arguments, loc); } if (set == null){ Error (30526, "indexer X.this [" + TypeManager.MonoBASIC_Name (right_type) + "] lacks a 'set' accessor"); return null; } type = TypeManager.void_type; eclass = ExprClass.IndexerAccess; return this; } public override void Emit (EmitContext ec) { Invocation.EmitCall (ec, false, false, instance_expr, get, arguments, loc); } // // source is ignored, because we already have a copy of it from the // LValue resolution and we have already constructed a pre-cached // version of the arguments (ea.set_arguments); // public void EmitAssign (EmitContext ec, Expression source) { Invocation.EmitCall (ec, false, false, instance_expr, set, set_arguments, loc); } } /// /// The base operator for method names /// public class BaseAccess : Expression { public string member; public BaseAccess (string member, Location l) { this.member = member; loc = l; } public override Expression DoResolve (EmitContext ec) { Expression member_lookup; Type current_type = ec.ContainerType; Type base_type = current_type.BaseType; Expression e; if (ec.IsStatic){ Error (1511, "Keyword MyBase is not allowed in static method"); return null; } if (member == "New") member = ".ctor"; member_lookup = MemberLookup (ec, current_type, base_type, member, AllMemberTypes, AllBindingFlags, loc); if (member_lookup == null) { Error (30456, TypeManager.MonoBASIC_Name (base_type) + " does not " + "contain a definition for '" + member + "'"); return null; } Expression left; if (ec.IsStatic) left = new TypeExpr (base_type, loc); else left = ec.This; e = MemberAccess.ResolveMemberAccess (ec, member_lookup, left, loc, null); if (e is PropertyExpr) { PropertyExpr pe = (PropertyExpr) e; pe.IsBase = true; } return e; } public override void Emit (EmitContext ec) { throw new Exception ("Should never be called"); } } /// /// The base indexer operator /// public class BaseIndexerAccess : IndexerAccess { public BaseIndexerAccess (ArrayList args, Location loc) : base (null, true, loc) { arguments = new ArrayList (); foreach (Expression tmp in args) arguments.Add (new Argument (tmp, Argument.AType.Expression)); } protected override bool CommonResolve (EmitContext ec) { instance_expr = ec.This; current_type = ec.ContainerType.BaseType; indexer_type = current_type; foreach (Argument a in arguments){ if (!a.Resolve (ec, loc)) return false; } return true; } } /// /// 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 /// /// This is also now used as a placeholder where a no-action expression /// is needed (the 'New' class). /// public class EmptyExpression : Expression { public EmptyExpression () { type = TypeManager.object_type; eclass = ExprClass.Value; loc = Location.Null; } public EmptyExpression (Type t) { type = t; eclass = ExprClass.Value; loc = Location.Null; } 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, Location l) { this.method = method; this.source = source; type = method.ReturnType; eclass = ExprClass.Value; loc = l; } 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, ITypeExpression { Expression left; string dim; public ComposedCast (Expression left, string dim, Location l) { this.left = left; this.dim = dim; loc = l; } public Expression DoResolveType (EmitContext ec) { Type ltype = ec.DeclSpace.ResolveType (left, false, loc); if (ltype == null) return null; // // ltype.Fullname is already fully qualified, so we can skip // a lot of probes, and go directly to TypeManager.LookupType // string cname = ltype.FullName + dim; type = TypeManager.LookupTypeDirect (cname); if (type == null){ // // For arrays of enumerations we are having a problem // with the direct lookup. Need to investigate. // // For now, fall back to the full lookup in that case. // type = RootContext.LookupType ( ec.DeclSpace, cname, false, loc); if (type == null) return null; } if (!ec.ResolvingTypeTree){ // // If the above flag is set, this is being invoked from the ResolveType function. // Upper layers take care of the type validity in this context. // if (!ec.InUnsafe && type.IsPointer){ UnsafeError (loc); return null; } } eclass = ExprClass.Type; return this; } public override Expression DoResolve (EmitContext ec) { return DoResolveType (ec); } public override void Emit (EmitContext ec) { throw new Exception ("This should never be called"); } public override string ToString () { return left + dim; } } // // This class is used to represent the address of an array, used // only by the Fixed statement, this is like the C "&a [0]" construct. // public class ArrayPtr : Expression { Expression array; public ArrayPtr (Expression array, Location l) { Type array_type = array.Type.GetElementType (); this.array = array; string array_ptr_type_name = array_type.FullName + "*"; type = Type.GetType (array_ptr_type_name); if (type == null){ ModuleBuilder mb = CodeGen.ModuleBuilder; type = mb.GetType (array_ptr_type_name); } eclass = ExprClass.Value; loc = l; } public override void Emit (EmitContext ec) { ILGenerator ig = ec.ig; array.Emit (ec); IntLiteral.EmitInt (ig, 0); ig.Emit (OpCodes.Ldelema, array.Type.GetElementType ()); } public override Expression DoResolve (EmitContext ec) { // // We are born fully resolved // return this; } } // // Used by the fixed statement // public class StringPtr : Expression { LocalBuilder b; public StringPtr (LocalBuilder b, Location l) { this.b = b; eclass = ExprClass.Value; type = TypeManager.char_ptr_type; loc = l; } public override Expression DoResolve (EmitContext ec) { // This should never be invoked, we are born in fully // initialized state. return this; } public override void Emit (EmitContext ec) { ILGenerator ig = ec.ig; ig.Emit (OpCodes.Ldloc, b); ig.Emit (OpCodes.Conv_I); ig.Emit (OpCodes.Call, TypeManager.int_get_offset_to_string_data); ig.Emit (OpCodes.Add); } } // // Implements the 'stackalloc' keyword // public class StackAlloc : Expression { Type otype; Expression t; Expression count; public StackAlloc (Expression type, Expression count, Location l) { t = type; this.count = count; loc = l; } public override Expression DoResolve (EmitContext ec) { count = count.Resolve (ec); if (count == null) return null; if (count.Type != TypeManager.int32_type){ count = ConvertImplicitRequired (ec, count, TypeManager.int32_type, loc); if (count == null) return null; } if (ec.InCatch || ec.InFinally){ Error (255, "stackalloc can not be used in a catch or finally block"); return null; } otype = ec.DeclSpace.ResolveType (t, false, loc); if (otype == null) return null; if (!TypeManager.VerifyUnManaged (otype, loc)) return null; string ptr_name = otype.FullName + "*"; type = Type.GetType (ptr_name); if (type == null){ ModuleBuilder mb = CodeGen.ModuleBuilder; type = mb.GetType (ptr_name); } eclass = ExprClass.Value; return this; } public override void Emit (EmitContext ec) { int size = GetTypeSize (otype); ILGenerator ig = ec.ig; if (size == 0) ig.Emit (OpCodes.Sizeof, otype); else IntConstant.EmitInt (ig, size); count.Emit (ec); ig.Emit (OpCodes.Mul); ig.Emit (OpCodes.Localloc); } } public class Preserve : ExpressionStatement { ArrayList args = null; MethodInfo mi = null; Expression target = null; ExpressionStatement source = null; public Preserve (Expression RedimTarget, ExpressionStatement acExpr, Location l) { Type type = typeof(Microsoft.VisualBasic.CompilerServices.Utils); mi = type.GetMethod("CopyArray"); target = RedimTarget; source = acExpr; eclass = ExprClass.Value; loc = l; } public override Expression DoResolve (EmitContext ec) { // // We are born fully resolved // type = mi.ReturnType; source.Resolve (ec); return this; } public override void Emit (EmitContext ec) { args = new ArrayList (2); args.Add (new Argument (target, Argument.AType.Expression)); args.Add (new Argument (source, Argument.AType.Expression)); Invocation.EmitArguments (ec, mi, args); ec.ig.Emit (OpCodes.Call, mi); return; } public override void EmitStatement (EmitContext ec) { Emit (ec); } } }