/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/argument.cs

http://github.com/icsharpcode/ILSpy · C# · 674 lines · 492 code · 125 blank · 57 comment · 127 complexity · fb3b608686c43c9c2d9b35c1d0eedc7f MD5 · raw file

  1. //
  2. // argument.cs: Argument expressions
  3. //
  4. // Author:
  5. // Miguel de Icaza (miguel@ximain.com)
  6. // Marek Safar (marek.safar@gmail.com)
  7. //
  8. // Dual licensed under the terms of the MIT X11 or GNU GPL
  9. // Copyright 2003-2011 Novell, Inc.
  10. // Copyright 2011 Xamarin Inc
  11. //
  12. using System;
  13. using System.Collections.Generic;
  14. #if STATIC
  15. using IKVM.Reflection.Emit;
  16. #else
  17. using System.Reflection.Emit;
  18. #endif
  19. namespace Mono.CSharp
  20. {
  21. //
  22. // Argument expression used for invocation
  23. //
  24. public class Argument
  25. {
  26. public enum AType : byte
  27. {
  28. None = 0,
  29. Ref = 1, // ref modifier used
  30. Out = 2, // out modifier used
  31. Default = 3, // argument created from default parameter value
  32. DynamicTypeName = 4, // System.Type argument for dynamic binding
  33. ExtensionType = 5, // Instance expression inserted as the first argument
  34. // Conditional instance expression inserted as the first argument
  35. ExtensionTypeConditionalAccess = 5 | ConditionalAccessFlag,
  36. ConditionalAccessFlag = 1 << 7
  37. }
  38. public readonly AType ArgType;
  39. public Expression Expr;
  40. public Argument (Expression expr, AType type)
  41. {
  42. this.Expr = expr;
  43. this.ArgType = type;
  44. }
  45. public Argument (Expression expr)
  46. {
  47. this.Expr = expr;
  48. }
  49. #region Properties
  50. public bool IsByRef {
  51. get { return ArgType == AType.Ref || ArgType == AType.Out; }
  52. }
  53. public bool IsDefaultArgument {
  54. get { return ArgType == AType.Default; }
  55. }
  56. public bool IsExtensionType {
  57. get {
  58. return (ArgType & AType.ExtensionType) == AType.ExtensionType;
  59. }
  60. }
  61. public Parameter.Modifier Modifier {
  62. get {
  63. switch (ArgType) {
  64. case AType.Out:
  65. return Parameter.Modifier.OUT;
  66. case AType.Ref:
  67. return Parameter.Modifier.REF;
  68. default:
  69. return Parameter.Modifier.NONE;
  70. }
  71. }
  72. }
  73. public TypeSpec Type {
  74. get { return Expr.Type; }
  75. }
  76. #endregion
  77. public Argument Clone (Expression expr)
  78. {
  79. Argument a = (Argument) MemberwiseClone ();
  80. a.Expr = expr;
  81. return a;
  82. }
  83. public Argument Clone (CloneContext clonectx)
  84. {
  85. return Clone (Expr.Clone (clonectx));
  86. }
  87. public virtual Expression CreateExpressionTree (ResolveContext ec)
  88. {
  89. if (ArgType == AType.Default)
  90. ec.Report.Error (854, Expr.Location, "An expression tree cannot contain an invocation which uses optional parameter");
  91. return Expr.CreateExpressionTree (ec);
  92. }
  93. public virtual void Emit (EmitContext ec)
  94. {
  95. if (!IsByRef) {
  96. if (ArgType == AType.ExtensionTypeConditionalAccess) {
  97. var ie = new InstanceEmitter (Expr, false);
  98. ie.Emit (ec, true);
  99. } else {
  100. Expr.Emit (ec);
  101. }
  102. return;
  103. }
  104. AddressOp mode = AddressOp.Store;
  105. if (ArgType == AType.Ref)
  106. mode |= AddressOp.Load;
  107. IMemoryLocation ml = (IMemoryLocation) Expr;
  108. ml.AddressOf (ec, mode);
  109. }
  110. public Argument EmitToField (EmitContext ec, bool cloneResult)
  111. {
  112. var res = Expr.EmitToField (ec);
  113. if (cloneResult && res != Expr)
  114. return new Argument (res, ArgType);
  115. Expr = res;
  116. return this;
  117. }
  118. public void FlowAnalysis (FlowAnalysisContext fc)
  119. {
  120. if (ArgType == AType.Out) {
  121. var vr = Expr as VariableReference;
  122. if (vr != null) {
  123. if (vr.VariableInfo != null)
  124. fc.SetVariableAssigned (vr.VariableInfo);
  125. return;
  126. }
  127. var fe = Expr as FieldExpr;
  128. if (fe != null) {
  129. fe.SetFieldAssigned (fc);
  130. return;
  131. }
  132. return;
  133. }
  134. Expr.FlowAnalysis (fc);
  135. }
  136. public string GetSignatureForError ()
  137. {
  138. if (Expr.eclass == ExprClass.MethodGroup)
  139. return Expr.ExprClassName;
  140. return Expr.Type.GetSignatureForError ();
  141. }
  142. public bool ResolveMethodGroup (ResolveContext ec)
  143. {
  144. SimpleName sn = Expr as SimpleName;
  145. if (sn != null)
  146. Expr = sn.GetMethodGroup ();
  147. // FIXME: csc doesn't report any error if you try to use `ref' or
  148. // `out' in a delegate creation expression.
  149. Expr = Expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup);
  150. if (Expr == null)
  151. return false;
  152. return true;
  153. }
  154. public void Resolve (ResolveContext ec)
  155. {
  156. // Verify that the argument is readable
  157. if (ArgType != AType.Out)
  158. Expr = Expr.Resolve (ec);
  159. // Verify that the argument is writeable
  160. if (Expr != null && IsByRef)
  161. Expr = Expr.ResolveLValue (ec, EmptyExpression.OutAccess);
  162. if (Expr == null)
  163. Expr = ErrorExpression.Instance;
  164. }
  165. }
  166. public class MovableArgument : Argument
  167. {
  168. LocalTemporary variable;
  169. public MovableArgument (Argument arg)
  170. : this (arg.Expr, arg.ArgType)
  171. {
  172. }
  173. protected MovableArgument (Expression expr, AType modifier)
  174. : base (expr, modifier)
  175. {
  176. }
  177. public override void Emit (EmitContext ec)
  178. {
  179. // TODO: Should guard against multiple emits
  180. base.Emit (ec);
  181. // Release temporary variable when used
  182. if (variable != null)
  183. variable.Release (ec);
  184. }
  185. public void EmitToVariable (EmitContext ec)
  186. {
  187. var type = Expr.Type;
  188. if (IsByRef) {
  189. var ml = (IMemoryLocation) Expr;
  190. ml.AddressOf (ec, AddressOp.LoadStore);
  191. type = ReferenceContainer.MakeType (ec.Module, type);
  192. } else {
  193. Expr.Emit (ec);
  194. }
  195. variable = new LocalTemporary (type);
  196. variable.Store (ec);
  197. Expr = variable;
  198. }
  199. }
  200. public class NamedArgument : MovableArgument
  201. {
  202. public readonly string Name;
  203. readonly Location loc;
  204. public NamedArgument (string name, Location loc, Expression expr)
  205. : this (name, loc, expr, AType.None)
  206. {
  207. }
  208. public NamedArgument (string name, Location loc, Expression expr, AType modifier)
  209. : base (expr, modifier)
  210. {
  211. this.Name = name;
  212. this.loc = loc;
  213. }
  214. public override Expression CreateExpressionTree (ResolveContext ec)
  215. {
  216. ec.Report.Error (853, loc, "An expression tree cannot contain named argument");
  217. return base.CreateExpressionTree (ec);
  218. }
  219. public Location Location {
  220. get { return loc; }
  221. }
  222. }
  223. public class Arguments
  224. {
  225. sealed class ArgumentsOrdered : Arguments
  226. {
  227. readonly List<MovableArgument> ordered;
  228. public ArgumentsOrdered (Arguments args)
  229. : base (args.Count)
  230. {
  231. AddRange (args);
  232. ordered = new List<MovableArgument> ();
  233. }
  234. public void AddOrdered (MovableArgument arg)
  235. {
  236. ordered.Add (arg);
  237. }
  238. public override void FlowAnalysis (FlowAnalysisContext fc, List<MovableArgument> movable = null)
  239. {
  240. foreach (var arg in ordered) {
  241. if (arg.ArgType != Argument.AType.Out)
  242. arg.FlowAnalysis (fc);
  243. }
  244. base.FlowAnalysis (fc, ordered);
  245. }
  246. public override Arguments Emit (EmitContext ec, bool dup_args, bool prepareAwait)
  247. {
  248. foreach (var a in ordered) {
  249. if (prepareAwait)
  250. a.EmitToField (ec, false);
  251. else
  252. a.EmitToVariable (ec);
  253. }
  254. return base.Emit (ec, dup_args, prepareAwait);
  255. }
  256. }
  257. // Try not to add any more instances to this class, it's allocated a lot
  258. List<Argument> args;
  259. public Arguments (int capacity)
  260. {
  261. args = new List<Argument> (capacity);
  262. }
  263. private Arguments (List<Argument> args)
  264. {
  265. this.args = args;
  266. }
  267. public void Add (Argument arg)
  268. {
  269. args.Add (arg);
  270. }
  271. public void AddRange (Arguments args)
  272. {
  273. this.args.AddRange (args.args);
  274. }
  275. public bool ContainsEmitWithAwait ()
  276. {
  277. foreach (var arg in args) {
  278. if (arg.Expr.ContainsEmitWithAwait ())
  279. return true;
  280. }
  281. return false;
  282. }
  283. public ArrayInitializer CreateDynamicBinderArguments (ResolveContext rc)
  284. {
  285. Location loc = Location.Null;
  286. var all = new ArrayInitializer (args.Count, loc);
  287. MemberAccess binder = DynamicExpressionStatement.GetBinderNamespace (loc);
  288. foreach (Argument a in args) {
  289. Arguments dargs = new Arguments (2);
  290. // CSharpArgumentInfoFlags.None = 0
  291. const string info_flags_enum = "CSharpArgumentInfoFlags";
  292. Expression info_flags = new IntLiteral (rc.BuiltinTypes, 0, loc);
  293. if (a.Expr is Constant) {
  294. info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
  295. new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "Constant", loc));
  296. } else if (a.ArgType == Argument.AType.Ref) {
  297. info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
  298. new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "IsRef", loc));
  299. info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
  300. new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "UseCompileTimeType", loc));
  301. } else if (a.ArgType == Argument.AType.Out) {
  302. info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
  303. new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "IsOut", loc));
  304. info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
  305. new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "UseCompileTimeType", loc));
  306. } else if (a.ArgType == Argument.AType.DynamicTypeName) {
  307. info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
  308. new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "IsStaticType", loc));
  309. }
  310. var arg_type = a.Expr.Type;
  311. if (arg_type.BuiltinType != BuiltinTypeSpec.Type.Dynamic && arg_type != InternalType.NullLiteral) {
  312. MethodGroupExpr mg = a.Expr as MethodGroupExpr;
  313. if (mg != null) {
  314. rc.Report.Error (1976, a.Expr.Location,
  315. "The method group `{0}' cannot be used as an argument of dynamic operation. Consider using parentheses to invoke the method",
  316. mg.Name);
  317. } else if (arg_type == InternalType.AnonymousMethod) {
  318. rc.Report.Error (1977, a.Expr.Location,
  319. "An anonymous method or lambda expression cannot be used as an argument of dynamic operation. Consider using a cast");
  320. } else if (arg_type.Kind == MemberKind.Void || arg_type == InternalType.Arglist || arg_type.IsPointer) {
  321. rc.Report.Error (1978, a.Expr.Location,
  322. "An expression of type `{0}' cannot be used as an argument of dynamic operation",
  323. arg_type.GetSignatureForError ());
  324. }
  325. info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
  326. new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "UseCompileTimeType", loc));
  327. }
  328. string named_value;
  329. NamedArgument na = a as NamedArgument;
  330. if (na != null) {
  331. info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
  332. new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "NamedArgument", loc));
  333. named_value = na.Name;
  334. } else {
  335. named_value = null;
  336. }
  337. dargs.Add (new Argument (info_flags));
  338. dargs.Add (new Argument (new StringLiteral (rc.BuiltinTypes, named_value, loc)));
  339. all.Add (new Invocation (new MemberAccess (new MemberAccess (binder, "CSharpArgumentInfo", loc), "Create", loc), dargs));
  340. }
  341. return all;
  342. }
  343. public static Arguments CreateForExpressionTree (ResolveContext ec, Arguments args, params Expression[] e)
  344. {
  345. Arguments all = new Arguments ((args == null ? 0 : args.Count) + e.Length);
  346. for (int i = 0; i < e.Length; ++i) {
  347. if (e [i] != null)
  348. all.Add (new Argument (e[i]));
  349. }
  350. if (args != null) {
  351. foreach (Argument a in args.args) {
  352. Expression tree_arg = a.CreateExpressionTree (ec);
  353. if (tree_arg != null)
  354. all.Add (new Argument (tree_arg));
  355. }
  356. }
  357. return all;
  358. }
  359. public void CheckArrayAsAttribute (CompilerContext ctx)
  360. {
  361. foreach (Argument arg in args) {
  362. // Type is undefined (was error 246)
  363. if (arg.Type == null)
  364. continue;
  365. if (arg.Type.IsArray)
  366. ctx.Report.Warning (3016, 1, arg.Expr.Location, "Arrays as attribute arguments are not CLS-compliant");
  367. }
  368. }
  369. public Arguments Clone (CloneContext ctx)
  370. {
  371. Arguments cloned = new Arguments (args.Count);
  372. foreach (Argument a in args)
  373. cloned.Add (a.Clone (ctx));
  374. return cloned;
  375. }
  376. public int Count {
  377. get { return args.Count; }
  378. }
  379. //
  380. // Emits a list of resolved Arguments
  381. //
  382. public void Emit (EmitContext ec)
  383. {
  384. Emit (ec, false, false);
  385. }
  386. //
  387. // if `dup_args' is true or any of arguments contains await.
  388. // A copy of all arguments will be returned to the caller
  389. //
  390. public virtual Arguments Emit (EmitContext ec, bool dup_args, bool prepareAwait)
  391. {
  392. List<Argument> dups;
  393. if ((dup_args && Count != 0) || prepareAwait)
  394. dups = new List<Argument> (Count);
  395. else
  396. dups = null;
  397. LocalTemporary lt;
  398. foreach (Argument a in args) {
  399. if (prepareAwait) {
  400. dups.Add (a.EmitToField (ec, true));
  401. continue;
  402. }
  403. a.Emit (ec);
  404. if (!dup_args) {
  405. continue;
  406. }
  407. if (a.Expr.IsSideEffectFree) {
  408. //
  409. // No need to create a temporary variable for side effect free expressions. I assume
  410. // all side-effect free expressions are cheap, this has to be tweaked when we become
  411. // more aggressive on detection
  412. //
  413. dups.Add (a);
  414. } else {
  415. ec.Emit (OpCodes.Dup);
  416. // TODO: Release local temporary on next Emit
  417. // Need to add a flag to argument to indicate this
  418. lt = new LocalTemporary (a.Type);
  419. lt.Store (ec);
  420. dups.Add (new Argument (lt, a.ArgType));
  421. }
  422. }
  423. if (dups != null)
  424. return new Arguments (dups);
  425. return null;
  426. }
  427. public virtual void FlowAnalysis (FlowAnalysisContext fc, List<MovableArgument> movable = null)
  428. {
  429. bool has_out = false;
  430. foreach (var arg in args) {
  431. if (arg.ArgType == Argument.AType.Out) {
  432. has_out = true;
  433. continue;
  434. }
  435. if (movable == null) {
  436. arg.FlowAnalysis (fc);
  437. continue;
  438. }
  439. var ma = arg as MovableArgument;
  440. if (ma != null && !movable.Contains (ma))
  441. arg.FlowAnalysis (fc);
  442. }
  443. if (!has_out)
  444. return;
  445. foreach (var arg in args) {
  446. if (arg.ArgType != Argument.AType.Out)
  447. continue;
  448. arg.FlowAnalysis (fc);
  449. }
  450. }
  451. public List<Argument>.Enumerator GetEnumerator ()
  452. {
  453. return args.GetEnumerator ();
  454. }
  455. //
  456. // At least one argument is of dynamic type
  457. //
  458. public bool HasDynamic {
  459. get {
  460. foreach (Argument a in args) {
  461. if (a.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic && !a.IsByRef)
  462. return true;
  463. }
  464. return false;
  465. }
  466. }
  467. //
  468. // At least one argument is named argument
  469. //
  470. public bool HasNamed {
  471. get {
  472. foreach (Argument a in args) {
  473. if (a is NamedArgument)
  474. return true;
  475. }
  476. return false;
  477. }
  478. }
  479. public void Insert (int index, Argument arg)
  480. {
  481. args.Insert (index, arg);
  482. }
  483. public static System.Linq.Expressions.Expression[] MakeExpression (Arguments args, BuilderContext ctx)
  484. {
  485. if (args == null || args.Count == 0)
  486. return null;
  487. var exprs = new System.Linq.Expressions.Expression [args.Count];
  488. for (int i = 0; i < exprs.Length; ++i) {
  489. Argument a = args.args [i];
  490. exprs[i] = a.Expr.MakeExpression (ctx);
  491. }
  492. return exprs;
  493. }
  494. //
  495. // For named arguments when the order of execution is different
  496. // to order of invocation
  497. //
  498. public Arguments MarkOrderedArgument (NamedArgument a)
  499. {
  500. //
  501. // An expression has no effect on left-to-right execution
  502. //
  503. if (a.Expr.IsSideEffectFree)
  504. return this;
  505. ArgumentsOrdered ra = this as ArgumentsOrdered;
  506. if (ra == null) {
  507. ra = new ArgumentsOrdered (this);
  508. for (int i = 0; i < args.Count; ++i) {
  509. var la = args [i];
  510. if (la == a)
  511. break;
  512. //
  513. // When the argument is filled later by default expression
  514. //
  515. if (la == null)
  516. continue;
  517. var ma = la as MovableArgument;
  518. if (ma == null) {
  519. ma = new MovableArgument (la);
  520. ra.args[i] = ma;
  521. }
  522. ra.AddOrdered (ma);
  523. }
  524. }
  525. ra.AddOrdered (a);
  526. return ra;
  527. }
  528. //
  529. // Returns dynamic when at least one argument is of dynamic type
  530. //
  531. public void Resolve (ResolveContext ec, out bool dynamic)
  532. {
  533. dynamic = false;
  534. foreach (Argument a in args) {
  535. a.Resolve (ec);
  536. if (a.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic && !a.IsByRef)
  537. dynamic = true;
  538. }
  539. }
  540. public void RemoveAt (int index)
  541. {
  542. args.RemoveAt (index);
  543. }
  544. public Argument this [int index] {
  545. get { return args [index]; }
  546. set { args [index] = value; }
  547. }
  548. }
  549. }