// Marek Safar (marek.safar@gmail.com)
//
// Dual licensed under the terms of the MIT X11 or GNU GPL
-// Copyright 2003-2008 Novell, Inc.
+// Copyright 2003-2011 Novell, Inc.
+// Copyright 2011 Xamarin Inc
//
using System;
-using System.Collections;
-using System.Reflection;
+using System.Collections.Generic;
+
+#if STATIC
+using IKVM.Reflection.Emit;
+#else
using System.Reflection.Emit;
+#endif
namespace Mono.CSharp
{
{
public enum AType : byte
{
+ None = 0,
Ref = 1, // ref modifier used
Out = 2, // out modifier used
Default = 3, // argument created from default parameter value
- DynamicStatic = 4 // static argument for dynamic binding
+ DynamicTypeName = 4, // System.Type argument for dynamic binding
+ ExtensionType = 5, // Instance expression inserted as the first argument
}
public readonly AType ArgType;
this.Expr = expr;
}
- public Type Type {
- get { return Expr.Type; }
+ #region Properties
+
+ public bool IsByRef {
+ get { return ArgType == AType.Ref || ArgType == AType.Out; }
+ }
+
+ public bool IsDefaultArgument {
+ get { return ArgType == AType.Default; }
}
public Parameter.Modifier Modifier {
}
}
+ public TypeSpec Type {
+ get { return Expr.Type; }
+ }
+
+ #endregion
+
+ public Argument Clone (Expression expr)
+ {
+ Argument a = (Argument) MemberwiseClone ();
+ a.Expr = expr;
+ return a;
+ }
+
+ public Argument Clone (CloneContext clonectx)
+ {
+ return Clone (Expr.Clone (clonectx));
+ }
+
public virtual Expression CreateExpressionTree (ResolveContext ec)
{
if (ArgType == AType.Default)
- Report.Error (854, Expr.Location, "An expression tree cannot contain an invocation which uses optional parameter");
+ ec.Report.Error (854, Expr.Location, "An expression tree cannot contain an invocation which uses optional parameter");
return Expr.CreateExpressionTree (ec);
}
- public string GetSignatureForError ()
+
+ public virtual void Emit (EmitContext ec)
{
- if (Expr.eclass == ExprClass.MethodGroup)
- return Expr.ExprClassName;
+ if (!IsByRef) {
+ Expr.Emit (ec);
+ return;
+ }
- return TypeManager.CSharpName (Expr.Type);
+ AddressOp mode = AddressOp.Store;
+ if (ArgType == AType.Ref)
+ mode |= AddressOp.Load;
+
+ IMemoryLocation ml = (IMemoryLocation) Expr;
+ ml.AddressOf (ec, mode);
}
- public bool IsByRef {
- get { return ArgType == AType.Ref || ArgType == AType.Out; }
+ public Argument EmitToField (EmitContext ec, bool cloneResult)
+ {
+ var res = Expr.EmitToField (ec);
+ if (cloneResult && res != Expr)
+ return new Argument (res, ArgType);
+
+ Expr = res;
+ return this;
}
- public bool IsDefaultArgument {
- get { return ArgType == AType.Default; }
+ public string GetSignatureForError ()
+ {
+ if (Expr.eclass == ExprClass.MethodGroup)
+ return Expr.ExprClassName;
+
+ return TypeManager.CSharpName (Expr.Type);
}
public bool ResolveMethodGroup (ResolveContext ec)
public void Resolve (ResolveContext ec)
{
- if (Expr == EmptyExpression.Null)
- return;
-
- using (ec.With (ResolveContext.Options.DoFlowAnalysis, true)) {
+// using (ec.With (ResolveContext.Options.DoFlowAnalysis, true)) {
// Verify that the argument is readable
if (ArgType != AType.Out)
Expr = Expr.Resolve (ec);
Expr = Expr.ResolveLValue (ec, EmptyExpression.OutAccess);
if (Expr == null)
- Expr = EmptyExpression.Null;
- }
- }
-
- public virtual void Emit (EmitContext ec)
- {
- if (!IsByRef) {
- Expr.Emit (ec);
- return;
- }
-
- AddressOp mode = AddressOp.Store;
- if (ArgType == AType.Ref)
- mode |= AddressOp.Load;
-
- IMemoryLocation ml = (IMemoryLocation) Expr;
- ParameterReference pr = ml as ParameterReference;
-
- //
- // ParameterReferences might already be references, so we want
- // to pass just the value
- //
- if (pr != null && pr.IsRef)
- pr.EmitLoad (ec);
- else
- ml.AddressOf (ec, mode);
- }
-
- public Argument Clone (CloneContext clonectx)
- {
- Argument a = (Argument) MemberwiseClone ();
- a.Expr = Expr.Clone (clonectx);
- return a;
+ Expr = ErrorExpression.Instance;
+// }
}
}
- public class NamedArgument : Argument
+ public class MovableArgument : Argument
{
- public readonly LocatedToken Name;
LocalTemporary variable;
- public NamedArgument (LocatedToken name, Expression expr)
- : base (expr)
+ public MovableArgument (Argument arg)
+ : this (arg.Expr, arg.ArgType)
{
- Name = name;
}
- public override Expression CreateExpressionTree (ResolveContext ec)
+ protected MovableArgument (Expression expr, AType modifier)
+ : base (expr, modifier)
{
- Report.Error (853, Name.Location, "An expression tree cannot contain named argument");
- return base.CreateExpressionTree (ec);
}
public override void Emit (EmitContext ec)
variable.Release (ec);
}
- public void EmitAssign (EmitContext ec)
+ public void EmitToVariable (EmitContext ec)
{
- Expr.Emit (ec);
- variable = new LocalTemporary (Expr.Type);
+ var type = Expr.Type;
+ if (IsByRef) {
+ var ml = (IMemoryLocation) Expr;
+ ml.AddressOf (ec, AddressOp.LoadStore);
+ type = ReferenceContainer.MakeType (ec.Module, type);
+ } else {
+ Expr.Emit (ec);
+ }
+
+ variable = new LocalTemporary (type);
variable.Store (ec);
Expr = variable;
}
}
+ public class NamedArgument : MovableArgument
+ {
+ public readonly string Name;
+ readonly Location loc;
+
+ public NamedArgument (string name, Location loc, Expression expr)
+ : this (name, loc, expr, AType.None)
+ {
+ }
+
+ public NamedArgument (string name, Location loc, Expression expr, AType modifier)
+ : base (expr, modifier)
+ {
+ this.Name = name;
+ this.loc = loc;
+ }
+
+ public override Expression CreateExpressionTree (ResolveContext ec)
+ {
+ ec.Report.Error (853, loc, "An expression tree cannot contain named argument");
+ return base.CreateExpressionTree (ec);
+ }
+
+ public Location Location {
+ get { return loc; }
+ }
+ }
+
public class Arguments
{
- ArrayList args; // TODO: This should really be linked list
- ArrayList reordered; // TODO: LinkedList
+ sealed class ArgumentsOrdered : Arguments
+ {
+ readonly List<MovableArgument> ordered;
+
+ public ArgumentsOrdered (Arguments args)
+ : base (args.Count)
+ {
+ AddRange (args);
+ ordered = new List<MovableArgument> ();
+ }
+
+ public void AddOrdered (MovableArgument arg)
+ {
+ ordered.Add (arg);
+ }
+
+ public override Arguments Emit (EmitContext ec, bool dup_args, bool prepareAwait)
+ {
+ foreach (var a in ordered) {
+ if (prepareAwait)
+ a.EmitToField (ec, false);
+ else
+ a.EmitToVariable (ec);
+ }
+
+ return base.Emit (ec, dup_args, prepareAwait);
+ }
+ }
+
+ // Try not to add any more instances to this class, it's allocated a lot
+ List<Argument> args;
public Arguments (int capacity)
{
- args = new ArrayList (capacity);
+ args = new List<Argument> (capacity);
+ }
+
+ private Arguments (List<Argument> args)
+ {
+ this.args = args;
}
- public int Add (Argument arg)
+ public void Add (Argument arg)
{
- return args.Add (arg);
+ args.Add (arg);
}
public void AddRange (Arguments args)
this.args.AddRange (args.args);
}
- public ArrayList CreateDynamicBinderArguments ()
+ public bool ContainsEmitWithAwait ()
+ {
+ foreach (var arg in args) {
+ if (arg.Expr.ContainsEmitWithAwait ())
+ return true;
+ }
+
+ return false;
+ }
+
+ public ArrayInitializer CreateDynamicBinderArguments (ResolveContext rc)
{
- ArrayList all = new ArrayList (args.Count);
Location loc = Location.Null;
+ var all = new ArrayInitializer (args.Count, loc);
MemberAccess binder = DynamicExpressionStatement.GetBinderNamespace (loc);
// CSharpArgumentInfoFlags.None = 0
const string info_flags_enum = "CSharpArgumentInfoFlags";
- Expression info_flags = new IntLiteral (0, loc);
+ Expression info_flags = new IntLiteral (rc.BuiltinTypes, 0, loc);
if (a.Expr is Constant) {
- // Any constant is emitted as a literal
info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
- new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "LiteralConstant", loc));
+ new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "Constant", loc));
} else if (a.ArgType == Argument.AType.Ref) {
info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "IsRef", loc));
+ info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
+ new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "UseCompileTimeType", loc));
} else if (a.ArgType == Argument.AType.Out) {
info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "IsOut", loc));
- } else if (a.ArgType == Argument.AType.DynamicStatic) {
+ info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
+ new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "UseCompileTimeType", loc));
+ } else if (a.ArgType == Argument.AType.DynamicTypeName) {
info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "IsStaticType", loc));
}
- if (!TypeManager.IsDynamicType (a.Expr.Type)) {
+ var arg_type = a.Expr.Type;
+
+ if (arg_type.BuiltinType != BuiltinTypeSpec.Type.Dynamic && arg_type != InternalType.NullLiteral) {
+ MethodGroupExpr mg = a.Expr as MethodGroupExpr;
+ if (mg != null) {
+ rc.Report.Error (1976, a.Expr.Location,
+ "The method group `{0}' cannot be used as an argument of dynamic operation. Consider using parentheses to invoke the method",
+ mg.Name);
+ } else if (arg_type == InternalType.AnonymousMethod) {
+ rc.Report.Error (1977, a.Expr.Location,
+ "An anonymous method or lambda expression cannot be used as an argument of dynamic operation. Consider using a cast");
+ } else if (arg_type.Kind == MemberKind.Void || arg_type == InternalType.Arglist || arg_type.IsPointer) {
+ rc.Report.Error (1978, a.Expr.Location,
+ "An expression of type `{0}' cannot be used as an argument of dynamic operation",
+ TypeManager.CSharpName (arg_type));
+ }
+
info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "UseCompileTimeType", loc));
}
info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "NamedArgument", loc));
- named_value = na.Name.Value;
+ named_value = na.Name;
} else {
named_value = null;
}
dargs.Add (new Argument (info_flags));
- dargs.Add (new Argument (new StringLiteral (named_value, loc)));
- all.Add (new New (new MemberAccess (binder, "CSharpArgumentInfo", loc), dargs, loc));
+ dargs.Add (new Argument (new StringLiteral (rc.BuiltinTypes, named_value, loc)));
+ all.Add (new Invocation (new MemberAccess (new MemberAccess (binder, "CSharpArgumentInfo", loc), "Create", loc), dargs));
}
return all;
return all;
}
- public void CheckArrayAsAttribute ()
+ public void CheckArrayAsAttribute (CompilerContext ctx)
{
foreach (Argument arg in args) {
// Type is undefined (was error 246)
continue;
if (arg.Type.IsArray)
- Report.Warning (3016, 1, arg.Expr.Location, "Arrays as attribute arguments are not CLS-compliant");
+ ctx.Report.Warning (3016, 1, arg.Expr.Location, "Arrays as attribute arguments are not CLS-compliant");
}
}
//
public void Emit (EmitContext ec)
{
- Emit (ec, false, null);
+ Emit (ec, false, false);
}
//
- // if `dup_args' is true, a copy of the arguments will be left
- // on the stack. If `dup_args' is true, you can specify `this_arg'
- // which will be duplicated before any other args. Only EmitCall
- // should be using this interface.
+ // if `dup_args' is true or any of arguments contains await.
+ // A copy of all arguments will be returned to the caller
//
- public void Emit (EmitContext ec, bool dup_args, LocalTemporary this_arg)
+ public virtual Arguments Emit (EmitContext ec, bool dup_args, bool prepareAwait)
{
- LocalTemporary[] temps = null;
-
- if (dup_args && Count != 0)
- temps = new LocalTemporary [Count];
+ List<Argument> dups;
- if (reordered != null && Count > 1) {
- foreach (NamedArgument na in reordered)
- na.EmitAssign (ec);
- }
+ if ((dup_args && Count != 0) || prepareAwait)
+ dups = new List<Argument> (Count);
+ else
+ dups = null;
- int i = 0;
+ LocalTemporary lt;
foreach (Argument a in args) {
+ if (prepareAwait) {
+ dups.Add (a.EmitToField (ec, true));
+ continue;
+ }
+
a.Emit (ec);
- if (dup_args) {
- ec.ig.Emit (OpCodes.Dup);
- (temps [i++] = new LocalTemporary (a.Type)).Store (ec);
+
+ if (!dup_args) {
+ continue;
}
- }
- if (dup_args) {
- if (this_arg != null)
- this_arg.Emit (ec);
+ if (a.Expr.IsSideEffectFree) {
+ //
+ // No need to create a temporary variable for side effect free expressions. I assume
+ // all side-effect free expressions are cheap, this has to be tweaked when we become
+ // more aggressive on detection
+ //
+ dups.Add (a);
+ } else {
+ ec.Emit (OpCodes.Dup);
+
+ // TODO: Release local temporary on next Emit
+ // Need to add a flag to argument to indicate this
+ lt = new LocalTemporary (a.Type);
+ lt.Store (ec);
- for (i = 0; i < temps.Length; i++) {
- temps[i].Emit (ec);
- temps[i].Release (ec);
+ dups.Add (new Argument (lt, a.ArgType));
}
}
- }
- public bool GetAttributableValue (ResolveContext ec, out object[] values)
- {
- values = new object [args.Count];
- for (int j = 0; j < values.Length; ++j) {
- Argument a = this [j];
- if (!a.Expr.GetAttributableValue (ec, a.Type, out values[j]))
- return false;
- }
+ if (dups != null)
+ return new Arguments (dups);
- return true;
+ return null;
}
- public IEnumerator GetEnumerator ()
+ public List<Argument>.Enumerator GetEnumerator ()
{
return args.GetEnumerator ();
}
+ //
+ // At least one argument is of dynamic type
+ //
+ public bool HasDynamic {
+ get {
+ foreach (Argument a in args) {
+ if (a.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic && !a.IsByRef)
+ return true;
+ }
+
+ return false;
+ }
+ }
+
+ //
+ // At least one argument is named argument
+ //
+ public bool HasNamed {
+ get {
+ foreach (Argument a in args) {
+ if (a is NamedArgument)
+ return true;
+ }
+
+ return false;
+ }
+ }
+
+
public void Insert (int index, Argument arg)
{
args.Insert (index, arg);
}
- public void MarkReorderedArgument (NamedArgument a)
+ public static System.Linq.Expressions.Expression[] MakeExpression (Arguments args, BuilderContext ctx)
+ {
+ if (args == null || args.Count == 0)
+ return null;
+
+ var exprs = new System.Linq.Expressions.Expression [args.Count];
+ for (int i = 0; i < exprs.Length; ++i) {
+ Argument a = args.args [i];
+ exprs[i] = a.Expr.MakeExpression (ctx);
+ }
+
+ return exprs;
+ }
+
+ //
+ // For named arguments when the order of execution is different
+ // to order of invocation
+ //
+ public Arguments MarkOrderedArgument (NamedArgument a)
{
//
- // Constant expression can have no effect on left-to-right execution
+ // An expression has no effect on left-to-right execution
//
- if (a.Expr is Constant)
- return;
-
- if (reordered == null)
- reordered = new ArrayList ();
+ if (a.Expr.IsSideEffectFree)
+ return this;
+
+ ArgumentsOrdered ra = this as ArgumentsOrdered;
+ if (ra == null) {
+ ra = new ArgumentsOrdered (this);
+
+ for (int i = 0; i < args.Count; ++i) {
+ var la = args [i];
+ if (la == a)
+ break;
+
+ //
+ // When the argument is filled later by default expression
+ //
+ if (la == null)
+ continue;
+
+ var ma = la as MovableArgument;
+ if (ma == null) {
+ ma = new MovableArgument (la);
+ ra.args[i] = ma;
+ }
+
+ ra.AddOrdered (ma);
+ }
+ }
- reordered.Add (a);
+ ra.AddOrdered (a);
+ return ra;
}
//
dynamic = false;
foreach (Argument a in args) {
a.Resolve (ec);
- dynamic |= TypeManager.IsDynamicType (a.Type);
+ if (a.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic && !a.IsByRef)
+ dynamic = true;
}
}
- public void MutateHoistedGenericType (AnonymousMethodStorey storey)
- {
- foreach (Argument a in args)
- a.Expr.MutateHoistedGenericType (storey);
- }
-
public void RemoveAt (int index)
{
args.RemoveAt (index);
}
public Argument this [int index] {
- get { return (Argument) args [index]; }
+ get { return args [index]; }
set { args [index] = value; }
}
}