PageRenderTime 72ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

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

http://github.com/icsharpcode/ILSpy
C# | 2292 lines | 1547 code | 408 blank | 337 comment | 384 complexity | 78a3ad6fc1ff25d143b9274abfc8b331 MD5 | raw file
Possible License(s): LGPL-2.1, MIT, CC-BY-SA-3.0
  1. //
  2. // anonymous.cs: Support for anonymous methods and types
  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. using Mono.CompilerServices.SymbolWriter;
  15. using System.Diagnostics;
  16. #if STATIC
  17. using IKVM.Reflection;
  18. using IKVM.Reflection.Emit;
  19. using System.Diagnostics;
  20. #else
  21. using System.Reflection;
  22. using System.Reflection.Emit;
  23. #endif
  24. namespace Mono.CSharp {
  25. public abstract class CompilerGeneratedContainer : ClassOrStruct
  26. {
  27. protected CompilerGeneratedContainer (TypeContainer parent, MemberName name, Modifiers mod)
  28. : this (parent, name, mod, MemberKind.Class)
  29. {
  30. }
  31. protected CompilerGeneratedContainer (TypeContainer parent, MemberName name, Modifiers mod, MemberKind kind)
  32. : base (parent, name, null, kind)
  33. {
  34. Debug.Assert ((mod & Modifiers.AccessibilityMask) != 0);
  35. ModFlags = mod | Modifiers.COMPILER_GENERATED | Modifiers.SEALED;
  36. spec = new TypeSpec (Kind, null, this, null, ModFlags);
  37. }
  38. protected void CheckMembersDefined ()
  39. {
  40. if (HasMembersDefined)
  41. throw new InternalErrorException ("Helper class already defined!");
  42. }
  43. protected override bool DoDefineMembers ()
  44. {
  45. if (Kind == MemberKind.Class && !IsStatic && !PartialContainer.HasInstanceConstructor) {
  46. DefineDefaultConstructor (false);
  47. }
  48. return base.DoDefineMembers ();
  49. }
  50. protected static MemberName MakeMemberName (MemberBase host, string name, int unique_id, TypeParameters tparams, Location loc)
  51. {
  52. string host_name = host == null ? null : host is InterfaceMemberBase ? ((InterfaceMemberBase)host).GetFullName (host.MemberName) : host.MemberName.Name;
  53. string tname = MakeName (host_name, "c", name, unique_id);
  54. TypeParameters args = null;
  55. if (tparams != null) {
  56. args = new TypeParameters (tparams.Count);
  57. // Type parameters will be filled later when we have TypeContainer
  58. // instance, for now we need only correct arity to create valid name
  59. for (int i = 0; i < tparams.Count; ++i)
  60. args.Add ((TypeParameter) null);
  61. }
  62. return new MemberName (tname, args, loc);
  63. }
  64. public static string MakeName (string host, string typePrefix, string name, int id)
  65. {
  66. return "<" + host + ">" + typePrefix + "__" + name + id.ToString ("X");
  67. }
  68. protected override TypeSpec[] ResolveBaseTypes (out FullNamedExpression base_class)
  69. {
  70. base_type = Compiler.BuiltinTypes.Object;
  71. base_class = null;
  72. return null;
  73. }
  74. }
  75. public class HoistedStoreyClass : CompilerGeneratedContainer
  76. {
  77. public sealed class HoistedField : Field
  78. {
  79. public HoistedField (HoistedStoreyClass parent, FullNamedExpression type, Modifiers mod, string name,
  80. Attributes attrs, Location loc)
  81. : base (parent, type, mod, new MemberName (name, loc), attrs)
  82. {
  83. }
  84. protected override bool ResolveMemberType ()
  85. {
  86. if (!base.ResolveMemberType ())
  87. return false;
  88. HoistedStoreyClass parent = ((HoistedStoreyClass) Parent).GetGenericStorey ();
  89. if (parent != null && parent.Mutator != null)
  90. member_type = parent.Mutator.Mutate (MemberType);
  91. return true;
  92. }
  93. }
  94. protected TypeParameterMutator mutator;
  95. public HoistedStoreyClass (TypeDefinition parent, MemberName name, TypeParameters tparams, Modifiers mods, MemberKind kind)
  96. : base (parent, name, mods | Modifiers.PRIVATE, kind)
  97. {
  98. if (tparams != null) {
  99. var type_params = name.TypeParameters;
  100. var src = new TypeParameterSpec[tparams.Count];
  101. var dst = new TypeParameterSpec[tparams.Count];
  102. for (int i = 0; i < tparams.Count; ++i) {
  103. type_params[i] = tparams[i].CreateHoistedCopy (spec);
  104. src[i] = tparams[i].Type;
  105. dst[i] = type_params[i].Type;
  106. }
  107. // A copy is not enough, inflate any type parameter constraints
  108. // using a new type parameters
  109. var inflator = new TypeParameterInflator (this, null, src, dst);
  110. for (int i = 0; i < tparams.Count; ++i) {
  111. src[i].InflateConstraints (inflator, dst[i]);
  112. }
  113. mutator = new TypeParameterMutator (tparams, type_params);
  114. }
  115. }
  116. #region Properties
  117. public TypeParameterMutator Mutator {
  118. get {
  119. return mutator;
  120. }
  121. set {
  122. mutator = value;
  123. }
  124. }
  125. #endregion
  126. public HoistedStoreyClass GetGenericStorey ()
  127. {
  128. TypeContainer storey = this;
  129. while (storey != null && storey.CurrentTypeParameters == null)
  130. storey = storey.Parent;
  131. return storey as HoistedStoreyClass;
  132. }
  133. }
  134. //
  135. // Anonymous method storey is created when an anonymous method uses
  136. // variable or parameter from outer scope. They are then hoisted to
  137. // anonymous method storey (captured)
  138. //
  139. public class AnonymousMethodStorey : HoistedStoreyClass
  140. {
  141. struct StoreyFieldPair
  142. {
  143. public readonly AnonymousMethodStorey Storey;
  144. public readonly Field Field;
  145. public StoreyFieldPair (AnonymousMethodStorey storey, Field field)
  146. {
  147. this.Storey = storey;
  148. this.Field = field;
  149. }
  150. }
  151. //
  152. // Needed to delay hoisted _this_ initialization. When an anonymous
  153. // method is used inside ctor and _this_ is hoisted, base ctor has to
  154. // be called first, otherwise _this_ will be initialized with
  155. // uninitialized value.
  156. //
  157. sealed class ThisInitializer : Statement
  158. {
  159. readonly HoistedThis hoisted_this;
  160. readonly AnonymousMethodStorey parent;
  161. public ThisInitializer (HoistedThis hoisted_this, AnonymousMethodStorey parent)
  162. {
  163. this.hoisted_this = hoisted_this;
  164. this.parent = parent;
  165. }
  166. protected override void DoEmit (EmitContext ec)
  167. {
  168. Expression source;
  169. if (parent == null)
  170. source = new CompilerGeneratedThis (ec.CurrentType, loc);
  171. else {
  172. source = new FieldExpr (parent.HoistedThis.Field, Location.Null) {
  173. InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, Location.Null)
  174. };
  175. }
  176. hoisted_this.EmitAssign (ec, source, false, false);
  177. }
  178. protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
  179. {
  180. return false;
  181. }
  182. protected override void CloneTo (CloneContext clonectx, Statement target)
  183. {
  184. // Nothing to clone
  185. }
  186. }
  187. // Unique storey ID
  188. public readonly int ID;
  189. public readonly ExplicitBlock OriginalSourceBlock;
  190. // A list of StoreyFieldPair with local field keeping parent storey instance
  191. List<StoreyFieldPair> used_parent_storeys;
  192. List<ExplicitBlock> children_references;
  193. // A list of hoisted parameters
  194. protected List<HoistedParameter> hoisted_params;
  195. List<HoistedParameter> hoisted_local_params;
  196. protected List<HoistedVariable> hoisted_locals;
  197. // Hoisted this
  198. protected HoistedThis hoisted_this;
  199. // Local variable which holds this storey instance
  200. public Expression Instance;
  201. bool initialize_hoisted_this;
  202. AnonymousMethodStorey hoisted_this_parent;
  203. public AnonymousMethodStorey (ExplicitBlock block, TypeDefinition parent, MemberBase host, TypeParameters tparams, string name, MemberKind kind)
  204. : base (parent, MakeMemberName (host, name, parent.PartialContainer.CounterAnonymousContainers, tparams, block.StartLocation),
  205. tparams, 0, kind)
  206. {
  207. OriginalSourceBlock = block;
  208. ID = parent.PartialContainer.CounterAnonymousContainers++;
  209. }
  210. public void AddCapturedThisField (EmitContext ec, AnonymousMethodStorey parent)
  211. {
  212. TypeExpr type_expr = new TypeExpression (ec.CurrentType, Location);
  213. Field f = AddCompilerGeneratedField ("$this", type_expr);
  214. hoisted_this = new HoistedThis (this, f);
  215. initialize_hoisted_this = true;
  216. hoisted_this_parent = parent;
  217. }
  218. public Field AddCapturedVariable (string name, TypeSpec type)
  219. {
  220. CheckMembersDefined ();
  221. FullNamedExpression field_type = new TypeExpression (type, Location);
  222. if (!spec.IsGenericOrParentIsGeneric)
  223. return AddCompilerGeneratedField (name, field_type);
  224. const Modifiers mod = Modifiers.INTERNAL | Modifiers.COMPILER_GENERATED;
  225. Field f = new HoistedField (this, field_type, mod, name, null, Location);
  226. AddField (f);
  227. return f;
  228. }
  229. protected Field AddCompilerGeneratedField (string name, FullNamedExpression type)
  230. {
  231. return AddCompilerGeneratedField (name, type, false);
  232. }
  233. protected Field AddCompilerGeneratedField (string name, FullNamedExpression type, bool privateAccess)
  234. {
  235. Modifiers mod = Modifiers.COMPILER_GENERATED | (privateAccess ? Modifiers.PRIVATE : Modifiers.INTERNAL);
  236. Field f = new Field (this, type, mod, new MemberName (name, Location), null);
  237. AddField (f);
  238. return f;
  239. }
  240. //
  241. // Creates a link between hoisted variable block and the anonymous method storey
  242. //
  243. // An anonymous method can reference variables from any outer block, but they are
  244. // hoisted in their own ExplicitBlock. When more than one block is referenced we
  245. // need to create another link between those variable storeys
  246. //
  247. public void AddReferenceFromChildrenBlock (ExplicitBlock block)
  248. {
  249. if (children_references == null)
  250. children_references = new List<ExplicitBlock> ();
  251. if (!children_references.Contains (block))
  252. children_references.Add (block);
  253. }
  254. public void AddParentStoreyReference (EmitContext ec, AnonymousMethodStorey storey)
  255. {
  256. CheckMembersDefined ();
  257. if (used_parent_storeys == null)
  258. used_parent_storeys = new List<StoreyFieldPair> ();
  259. else if (used_parent_storeys.Exists (i => i.Storey == storey))
  260. return;
  261. TypeExpr type_expr = storey.CreateStoreyTypeExpression (ec);
  262. Field f = AddCompilerGeneratedField ("<>f__ref$" + storey.ID, type_expr);
  263. used_parent_storeys.Add (new StoreyFieldPair (storey, f));
  264. }
  265. public void CaptureLocalVariable (ResolveContext ec, LocalVariable localVariable)
  266. {
  267. if (this is StateMachine) {
  268. if (ec.CurrentBlock.ParametersBlock != localVariable.Block.ParametersBlock)
  269. ec.CurrentBlock.Explicit.HasCapturedVariable = true;
  270. } else {
  271. ec.CurrentBlock.Explicit.HasCapturedVariable = true;
  272. }
  273. var hoisted = localVariable.HoistedVariant;
  274. if (hoisted != null && hoisted.Storey != this && hoisted.Storey is StateMachine) {
  275. //
  276. // Variable is already hoisted but we need it in storey which can be shared
  277. //
  278. hoisted.Storey.hoisted_locals.Remove (hoisted);
  279. hoisted.Storey.Members.Remove (hoisted.Field);
  280. hoisted = null;
  281. }
  282. if (hoisted == null) {
  283. hoisted = new HoistedLocalVariable (this, localVariable, GetVariableMangledName (localVariable));
  284. localVariable.HoistedVariant = hoisted;
  285. if (hoisted_locals == null)
  286. hoisted_locals = new List<HoistedVariable> ();
  287. hoisted_locals.Add (hoisted);
  288. }
  289. if (ec.CurrentBlock.Explicit != localVariable.Block.Explicit && !(hoisted.Storey is StateMachine))
  290. hoisted.Storey.AddReferenceFromChildrenBlock (ec.CurrentBlock.Explicit);
  291. }
  292. public void CaptureParameter (ResolveContext ec, ParametersBlock.ParameterInfo parameterInfo, ParameterReference parameterReference)
  293. {
  294. if (!(this is StateMachine)) {
  295. ec.CurrentBlock.Explicit.HasCapturedVariable = true;
  296. }
  297. var hoisted = parameterInfo.Parameter.HoistedVariant;
  298. if (parameterInfo.Block.StateMachine != null) {
  299. //
  300. // Another storey in same block exists but state machine does not
  301. // have parameter captured. We need to add it there as well to
  302. // proxy parameter value correctly.
  303. //
  304. if (hoisted == null && parameterInfo.Block.StateMachine != this) {
  305. var storey = parameterInfo.Block.StateMachine;
  306. hoisted = new HoistedParameter (storey, parameterReference);
  307. parameterInfo.Parameter.HoistedVariant = hoisted;
  308. if (storey.hoisted_params == null)
  309. storey.hoisted_params = new List<HoistedParameter> ();
  310. storey.hoisted_params.Add (hoisted);
  311. }
  312. //
  313. // Lift captured parameter from value type storey to reference type one. Otherwise
  314. // any side effects would be done on a copy
  315. //
  316. if (hoisted != null && hoisted.Storey != this && hoisted.Storey is StateMachine) {
  317. if (hoisted_local_params == null)
  318. hoisted_local_params = new List<HoistedParameter> ();
  319. hoisted_local_params.Add (hoisted);
  320. hoisted = null;
  321. }
  322. }
  323. if (hoisted == null) {
  324. hoisted = new HoistedParameter (this, parameterReference);
  325. parameterInfo.Parameter.HoistedVariant = hoisted;
  326. if (hoisted_params == null)
  327. hoisted_params = new List<HoistedParameter> ();
  328. hoisted_params.Add (hoisted);
  329. }
  330. //
  331. // Register link between current block and parameter storey. It will
  332. // be used when setting up storey definition to deploy storey reference
  333. // when parameters are used from multiple blocks
  334. //
  335. if (ec.CurrentBlock.Explicit != parameterInfo.Block) {
  336. hoisted.Storey.AddReferenceFromChildrenBlock (ec.CurrentBlock.Explicit);
  337. }
  338. }
  339. TypeExpr CreateStoreyTypeExpression (EmitContext ec)
  340. {
  341. //
  342. // Create an instance of storey type
  343. //
  344. TypeExpr storey_type_expr;
  345. if (CurrentTypeParameters != null) {
  346. //
  347. // Use current method type parameter (MVAR) for top level storey only. All
  348. // nested storeys use class type parameter (VAR)
  349. //
  350. var tparams = ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null ?
  351. ec.CurrentAnonymousMethod.Storey.CurrentTypeParameters :
  352. ec.CurrentTypeParameters;
  353. TypeArguments targs = new TypeArguments ();
  354. //
  355. // Use type parameter name instead of resolved type parameter
  356. // specification to resolve to correctly nested type parameters
  357. //
  358. for (int i = 0; i < tparams.Count; ++i)
  359. targs.Add (new SimpleName (tparams [i].Name, Location)); // new TypeParameterExpr (tparams[i], Location));
  360. storey_type_expr = new GenericTypeExpr (Definition, targs, Location);
  361. } else {
  362. storey_type_expr = new TypeExpression (CurrentType, Location);
  363. }
  364. return storey_type_expr;
  365. }
  366. public void SetNestedStoryParent (AnonymousMethodStorey parentStorey)
  367. {
  368. Parent = parentStorey;
  369. spec.IsGeneric = false;
  370. spec.DeclaringType = parentStorey.CurrentType;
  371. MemberName.TypeParameters = null;
  372. }
  373. protected override bool DoResolveTypeParameters ()
  374. {
  375. // Although any storey can have type parameters they are all clones of method type
  376. // parameters therefore have to mutate MVAR references in any of cloned constraints
  377. if (CurrentTypeParameters != null) {
  378. for (int i = 0; i < CurrentTypeParameters.Count; ++i) {
  379. var spec = CurrentTypeParameters[i].Type;
  380. spec.BaseType = mutator.Mutate (spec.BaseType);
  381. if (spec.InterfacesDefined != null) {
  382. var mutated = new TypeSpec[spec.InterfacesDefined.Length];
  383. for (int ii = 0; ii < mutated.Length; ++ii) {
  384. mutated[ii] = mutator.Mutate (spec.InterfacesDefined[ii]);
  385. }
  386. spec.InterfacesDefined = mutated;
  387. }
  388. if (spec.TypeArguments != null) {
  389. spec.TypeArguments = mutator.Mutate (spec.TypeArguments);
  390. }
  391. }
  392. }
  393. //
  394. // Update parent cache as we most likely passed the point
  395. // where the cache was constructed
  396. //
  397. Parent.CurrentType.MemberCache.AddMember (this.spec);
  398. return true;
  399. }
  400. //
  401. // Initializes all hoisted variables
  402. //
  403. public void EmitStoreyInstantiation (EmitContext ec, ExplicitBlock block)
  404. {
  405. // There can be only one instance variable for each storey type
  406. if (Instance != null)
  407. throw new InternalErrorException ();
  408. //
  409. // Create an instance of this storey
  410. //
  411. ResolveContext rc = new ResolveContext (ec.MemberContext);
  412. rc.CurrentBlock = block;
  413. var storey_type_expr = CreateStoreyTypeExpression (ec);
  414. var source = new New (storey_type_expr, null, Location).Resolve (rc);
  415. //
  416. // When the current context is async (or iterator) lift local storey
  417. // instantiation to the currect storey
  418. //
  419. if (ec.CurrentAnonymousMethod is StateMachineInitializer && (block.HasYield || block.HasAwait)) {
  420. //
  421. // Unfortunately, normal capture mechanism could not be used because we are
  422. // too late in the pipeline and standart assign cannot be used either due to
  423. // recursive nature of GetStoreyInstanceExpression
  424. //
  425. var field = ec.CurrentAnonymousMethod.Storey.AddCompilerGeneratedField (
  426. LocalVariable.GetCompilerGeneratedName (block), storey_type_expr, true);
  427. field.Define ();
  428. field.Emit ();
  429. var fexpr = new FieldExpr (field, Location);
  430. fexpr.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, Location);
  431. fexpr.EmitAssign (ec, source, false, false);
  432. Instance = fexpr;
  433. } else {
  434. var local = TemporaryVariableReference.Create (source.Type, block, Location);
  435. if (source.Type.IsStruct) {
  436. local.LocalInfo.CreateBuilder (ec);
  437. } else {
  438. local.EmitAssign (ec, source);
  439. }
  440. Instance = local;
  441. }
  442. EmitHoistedFieldsInitialization (rc, ec);
  443. // TODO: Implement properly
  444. //SymbolWriter.DefineScopeVariable (ID, Instance.Builder);
  445. }
  446. void EmitHoistedFieldsInitialization (ResolveContext rc, EmitContext ec)
  447. {
  448. //
  449. // Initialize all storey reference fields by using local or hoisted variables
  450. //
  451. if (used_parent_storeys != null) {
  452. foreach (StoreyFieldPair sf in used_parent_storeys) {
  453. //
  454. // Get instance expression of storey field
  455. //
  456. Expression instace_expr = GetStoreyInstanceExpression (ec);
  457. var fs = sf.Field.Spec;
  458. if (TypeManager.IsGenericType (instace_expr.Type))
  459. fs = MemberCache.GetMember (instace_expr.Type, fs);
  460. FieldExpr f_set_expr = new FieldExpr (fs, Location);
  461. f_set_expr.InstanceExpression = instace_expr;
  462. // TODO: CompilerAssign expression
  463. SimpleAssign a = new SimpleAssign (f_set_expr, sf.Storey.GetStoreyInstanceExpression (ec));
  464. if (a.Resolve (rc) != null)
  465. a.EmitStatement (ec);
  466. }
  467. }
  468. //
  469. // Initialize hoisted `this' only once, everywhere else will be
  470. // referenced indirectly
  471. //
  472. if (initialize_hoisted_this) {
  473. rc.CurrentBlock.AddScopeStatement (new ThisInitializer (hoisted_this, hoisted_this_parent));
  474. }
  475. //
  476. // Setting currect anonymous method to null blocks any further variable hoisting
  477. //
  478. AnonymousExpression ae = ec.CurrentAnonymousMethod;
  479. ec.CurrentAnonymousMethod = null;
  480. if (hoisted_params != null) {
  481. EmitHoistedParameters (ec, hoisted_params);
  482. }
  483. ec.CurrentAnonymousMethod = ae;
  484. }
  485. protected virtual void EmitHoistedParameters (EmitContext ec, List<HoistedParameter> hoisted)
  486. {
  487. foreach (HoistedParameter hp in hoisted) {
  488. if (hp == null)
  489. continue;
  490. //
  491. // Parameters could be proxied via local fields for value type storey
  492. //
  493. if (hoisted_local_params != null) {
  494. var local_param = hoisted_local_params.Find (l => l.Parameter.Parameter == hp.Parameter.Parameter);
  495. var source = new FieldExpr (local_param.Field, Location);
  496. source.InstanceExpression = new CompilerGeneratedThis (CurrentType, Location);
  497. hp.EmitAssign (ec, source, false, false);
  498. continue;
  499. }
  500. hp.EmitHoistingAssignment (ec);
  501. }
  502. }
  503. //
  504. // Returns a field which holds referenced storey instance
  505. //
  506. Field GetReferencedStoreyField (AnonymousMethodStorey storey)
  507. {
  508. if (used_parent_storeys == null)
  509. return null;
  510. foreach (StoreyFieldPair sf in used_parent_storeys) {
  511. if (sf.Storey == storey)
  512. return sf.Field;
  513. }
  514. return null;
  515. }
  516. //
  517. // Creates storey instance expression regardless of currect IP
  518. //
  519. public Expression GetStoreyInstanceExpression (EmitContext ec)
  520. {
  521. AnonymousExpression am = ec.CurrentAnonymousMethod;
  522. //
  523. // Access from original block -> storey
  524. //
  525. if (am == null)
  526. return Instance;
  527. //
  528. // Access from anonymous method implemented as a static -> storey
  529. //
  530. if (am.Storey == null)
  531. return Instance;
  532. Field f = am.Storey.GetReferencedStoreyField (this);
  533. if (f == null) {
  534. if (am.Storey == this) {
  535. //
  536. // Access from inside of same storey (S -> S)
  537. //
  538. return new CompilerGeneratedThis (CurrentType, Location);
  539. }
  540. //
  541. // External field access
  542. //
  543. return Instance;
  544. }
  545. //
  546. // Storey was cached to local field
  547. //
  548. FieldExpr f_ind = new FieldExpr (f, Location);
  549. f_ind.InstanceExpression = new CompilerGeneratedThis (CurrentType, Location);
  550. return f_ind;
  551. }
  552. protected virtual string GetVariableMangledName (LocalVariable local_info)
  553. {
  554. //
  555. // No need to mangle anonymous method hoisted variables cause they
  556. // are hoisted in their own scopes
  557. //
  558. return local_info.Name;
  559. }
  560. public HoistedThis HoistedThis {
  561. get {
  562. return hoisted_this;
  563. }
  564. set {
  565. hoisted_this = value;
  566. }
  567. }
  568. public IList<ExplicitBlock> ReferencesFromChildrenBlock {
  569. get { return children_references; }
  570. }
  571. }
  572. public abstract class HoistedVariable
  573. {
  574. //
  575. // Hoisted version of variable references used in expression
  576. // tree has to be delayed until we know its location. The variable
  577. // doesn't know its location until all stories are calculated
  578. //
  579. class ExpressionTreeVariableReference : Expression
  580. {
  581. readonly HoistedVariable hv;
  582. public ExpressionTreeVariableReference (HoistedVariable hv)
  583. {
  584. this.hv = hv;
  585. }
  586. public override bool ContainsEmitWithAwait ()
  587. {
  588. return false;
  589. }
  590. public override Expression CreateExpressionTree (ResolveContext ec)
  591. {
  592. return hv.CreateExpressionTree ();
  593. }
  594. protected override Expression DoResolve (ResolveContext ec)
  595. {
  596. eclass = ExprClass.Value;
  597. type = ec.Module.PredefinedTypes.Expression.Resolve ();
  598. return this;
  599. }
  600. public override void Emit (EmitContext ec)
  601. {
  602. ResolveContext rc = new ResolveContext (ec.MemberContext);
  603. Expression e = hv.GetFieldExpression (ec).CreateExpressionTree (rc, false);
  604. // This should never fail
  605. e = e.Resolve (rc);
  606. if (e != null)
  607. e.Emit (ec);
  608. }
  609. }
  610. protected readonly AnonymousMethodStorey storey;
  611. protected Field field;
  612. Dictionary<AnonymousExpression, FieldExpr> cached_inner_access; // TODO: Hashtable is too heavyweight
  613. FieldExpr cached_outer_access;
  614. protected HoistedVariable (AnonymousMethodStorey storey, string name, TypeSpec type)
  615. : this (storey, storey.AddCapturedVariable (name, type))
  616. {
  617. }
  618. protected HoistedVariable (AnonymousMethodStorey storey, Field field)
  619. {
  620. this.storey = storey;
  621. this.field = field;
  622. }
  623. public Field Field {
  624. get {
  625. return field;
  626. }
  627. }
  628. public AnonymousMethodStorey Storey {
  629. get {
  630. return storey;
  631. }
  632. }
  633. public void AddressOf (EmitContext ec, AddressOp mode)
  634. {
  635. GetFieldExpression (ec).AddressOf (ec, mode);
  636. }
  637. public Expression CreateExpressionTree ()
  638. {
  639. return new ExpressionTreeVariableReference (this);
  640. }
  641. public void Emit (EmitContext ec)
  642. {
  643. GetFieldExpression (ec).Emit (ec);
  644. }
  645. public Expression EmitToField (EmitContext ec)
  646. {
  647. return GetFieldExpression (ec);
  648. }
  649. //
  650. // Creates field access expression for hoisted variable
  651. //
  652. protected virtual FieldExpr GetFieldExpression (EmitContext ec)
  653. {
  654. if (ec.CurrentAnonymousMethod == null || ec.CurrentAnonymousMethod.Storey == null) {
  655. if (cached_outer_access != null)
  656. return cached_outer_access;
  657. //
  658. // When setting top-level hoisted variable in generic storey
  659. // change storey generic types to method generic types (VAR -> MVAR)
  660. //
  661. if (storey.Instance.Type.IsGenericOrParentIsGeneric) {
  662. var fs = MemberCache.GetMember (storey.Instance.Type, field.Spec);
  663. cached_outer_access = new FieldExpr (fs, field.Location);
  664. } else {
  665. cached_outer_access = new FieldExpr (field, field.Location);
  666. }
  667. cached_outer_access.InstanceExpression = storey.GetStoreyInstanceExpression (ec);
  668. return cached_outer_access;
  669. }
  670. FieldExpr inner_access;
  671. if (cached_inner_access != null) {
  672. if (!cached_inner_access.TryGetValue (ec.CurrentAnonymousMethod, out inner_access))
  673. inner_access = null;
  674. } else {
  675. inner_access = null;
  676. cached_inner_access = new Dictionary<AnonymousExpression, FieldExpr> (4);
  677. }
  678. if (inner_access == null) {
  679. if (field.Parent.IsGenericOrParentIsGeneric) {
  680. var fs = MemberCache.GetMember (field.Parent.CurrentType, field.Spec);
  681. inner_access = new FieldExpr (fs, field.Location);
  682. } else {
  683. inner_access = new FieldExpr (field, field.Location);
  684. }
  685. inner_access.InstanceExpression = storey.GetStoreyInstanceExpression (ec);
  686. cached_inner_access.Add (ec.CurrentAnonymousMethod, inner_access);
  687. }
  688. return inner_access;
  689. }
  690. public void Emit (EmitContext ec, bool leave_copy)
  691. {
  692. GetFieldExpression (ec).Emit (ec, leave_copy);
  693. }
  694. public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound)
  695. {
  696. GetFieldExpression (ec).EmitAssign (ec, source, leave_copy, false);
  697. }
  698. }
  699. public class HoistedParameter : HoistedVariable
  700. {
  701. sealed class HoistedFieldAssign : CompilerAssign
  702. {
  703. public HoistedFieldAssign (Expression target, Expression source)
  704. : base (target, source, target.Location)
  705. {
  706. }
  707. protected override Expression ResolveConversions (ResolveContext ec)
  708. {
  709. //
  710. // Implicit conversion check fails for hoisted type arguments
  711. // as they are of different types (!!0 x !0)
  712. //
  713. return this;
  714. }
  715. }
  716. readonly ParameterReference parameter;
  717. public HoistedParameter (AnonymousMethodStorey scope, ParameterReference par)
  718. : base (scope, par.Name, par.Type)
  719. {
  720. this.parameter = par;
  721. }
  722. public HoistedParameter (HoistedParameter hp, string name)
  723. : base (hp.storey, name, hp.parameter.Type)
  724. {
  725. this.parameter = hp.parameter;
  726. }
  727. #region Properties
  728. public bool IsAssigned { get; set; }
  729. public ParameterReference Parameter {
  730. get {
  731. return parameter;
  732. }
  733. }
  734. #endregion
  735. public void EmitHoistingAssignment (EmitContext ec)
  736. {
  737. //
  738. // Remove hoisted redirection to emit assignment from original parameter
  739. //
  740. var temp = parameter.Parameter.HoistedVariant;
  741. parameter.Parameter.HoistedVariant = null;
  742. var a = new HoistedFieldAssign (GetFieldExpression (ec), parameter);
  743. a.EmitStatement (ec);
  744. parameter.Parameter.HoistedVariant = temp;
  745. }
  746. }
  747. class HoistedLocalVariable : HoistedVariable
  748. {
  749. public HoistedLocalVariable (AnonymousMethodStorey storey, LocalVariable local, string name)
  750. : base (storey, name, local.Type)
  751. {
  752. }
  753. }
  754. public class HoistedThis : HoistedVariable
  755. {
  756. public HoistedThis (AnonymousMethodStorey storey, Field field)
  757. : base (storey, field)
  758. {
  759. }
  760. }
  761. //
  762. // Anonymous method expression as created by parser
  763. //
  764. public class AnonymousMethodExpression : Expression
  765. {
  766. //
  767. // Special conversion for nested expression tree lambdas
  768. //
  769. class Quote : ShimExpression
  770. {
  771. public Quote (Expression expr)
  772. : base (expr)
  773. {
  774. }
  775. public override Expression CreateExpressionTree (ResolveContext ec)
  776. {
  777. var args = new Arguments (1);
  778. args.Add (new Argument (expr.CreateExpressionTree (ec)));
  779. return CreateExpressionFactoryCall (ec, "Quote", args);
  780. }
  781. protected override Expression DoResolve (ResolveContext rc)
  782. {
  783. expr = expr.Resolve (rc);
  784. if (expr == null)
  785. return null;
  786. eclass = expr.eclass;
  787. type = expr.Type;
  788. return this;
  789. }
  790. }
  791. readonly Dictionary<TypeSpec, Expression> compatibles;
  792. public ParametersBlock Block;
  793. public AnonymousMethodExpression (Location loc)
  794. {
  795. this.loc = loc;
  796. this.compatibles = new Dictionary<TypeSpec, Expression> ();
  797. }
  798. #region Properties
  799. public override string ExprClassName {
  800. get {
  801. return "anonymous method";
  802. }
  803. }
  804. public virtual bool HasExplicitParameters {
  805. get {
  806. return Parameters != ParametersCompiled.Undefined;
  807. }
  808. }
  809. public override bool IsSideEffectFree {
  810. get {
  811. return true;
  812. }
  813. }
  814. public ParametersCompiled Parameters {
  815. get {
  816. return Block.Parameters;
  817. }
  818. }
  819. public bool IsAsync {
  820. get;
  821. internal set;
  822. }
  823. public ReportPrinter TypeInferenceReportPrinter {
  824. get; set;
  825. }
  826. #endregion
  827. //
  828. // Returns true if the body of lambda expression can be implicitly
  829. // converted to the delegate of type `delegate_type'
  830. //
  831. public bool ImplicitStandardConversionExists (ResolveContext ec, TypeSpec delegate_type)
  832. {
  833. using (ec.With (ResolveContext.Options.InferReturnType, false)) {
  834. using (ec.Set (ResolveContext.Options.ProbingMode)) {
  835. var prev = ec.Report.SetPrinter (TypeInferenceReportPrinter ?? new NullReportPrinter ());
  836. var res = Compatible (ec, delegate_type) != null;
  837. ec.Report.SetPrinter (prev);
  838. return res;
  839. }
  840. }
  841. }
  842. TypeSpec CompatibleChecks (ResolveContext ec, TypeSpec delegate_type)
  843. {
  844. if (delegate_type.IsDelegate)
  845. return delegate_type;
  846. if (delegate_type.IsExpressionTreeType) {
  847. delegate_type = delegate_type.TypeArguments [0];
  848. if (delegate_type.IsDelegate)
  849. return delegate_type;
  850. ec.Report.Error (835, loc, "Cannot convert `{0}' to an expression tree of non-delegate type `{1}'",
  851. GetSignatureForError (), delegate_type.GetSignatureForError ());
  852. return null;
  853. }
  854. ec.Report.Error (1660, loc, "Cannot convert `{0}' to non-delegate type `{1}'",
  855. GetSignatureForError (), delegate_type.GetSignatureForError ());
  856. return null;
  857. }
  858. protected bool VerifyExplicitParameters (ResolveContext ec, TypeInferenceContext tic, TypeSpec delegate_type, AParametersCollection parameters)
  859. {
  860. if (VerifyParameterCompatibility (ec, tic, delegate_type, parameters, ec.IsInProbingMode))
  861. return true;
  862. if (!ec.IsInProbingMode)
  863. ec.Report.Error (1661, loc,
  864. "Cannot convert `{0}' to delegate type `{1}' since there is a parameter mismatch",
  865. GetSignatureForError (), delegate_type.GetSignatureForError ());
  866. return false;
  867. }
  868. protected bool VerifyParameterCompatibility (ResolveContext ec, TypeInferenceContext tic, TypeSpec delegate_type, AParametersCollection invoke_pd, bool ignore_errors)
  869. {
  870. if (Parameters.Count != invoke_pd.Count) {
  871. if (ignore_errors)
  872. return false;
  873. ec.Report.Error (1593, loc, "Delegate `{0}' does not take `{1}' arguments",
  874. delegate_type.GetSignatureForError (), Parameters.Count.ToString ());
  875. return false;
  876. }
  877. bool has_implicit_parameters = !HasExplicitParameters;
  878. bool error = false;
  879. for (int i = 0; i < Parameters.Count; ++i) {
  880. Parameter.Modifier p_mod = invoke_pd.FixedParameters [i].ModFlags;
  881. if (Parameters.FixedParameters [i].ModFlags != p_mod && p_mod != Parameter.Modifier.PARAMS) {
  882. if (ignore_errors)
  883. return false;
  884. if (p_mod == Parameter.Modifier.NONE)
  885. ec.Report.Error (1677, Parameters[i].Location, "Parameter `{0}' should not be declared with the `{1}' keyword",
  886. (i + 1).ToString (), Parameter.GetModifierSignature (Parameters [i].ModFlags));
  887. else
  888. ec.Report.Error (1676, Parameters[i].Location, "Parameter `{0}' must be declared with the `{1}' keyword",
  889. (i+1).ToString (), Parameter.GetModifierSignature (p_mod));
  890. error = true;
  891. }
  892. if (has_implicit_parameters)
  893. continue;
  894. TypeSpec type = invoke_pd.Types [i];
  895. if (tic != null)
  896. type = tic.InflateGenericArgument (ec, type);
  897. if (!TypeSpecComparer.IsEqual (type, Parameters.Types [i])) {
  898. if (ignore_errors)
  899. return false;
  900. ec.Report.Error (1678, Parameters [i].Location, "Parameter `{0}' is declared as type `{1}' but should be `{2}'",
  901. (i+1).ToString (),
  902. Parameters.Types [i].GetSignatureForError (),
  903. invoke_pd.Types [i].GetSignatureForError ());
  904. error = true;
  905. }
  906. }
  907. return !error;
  908. }
  909. //
  910. // Infers type arguments based on explicit arguments
  911. //
  912. public bool ExplicitTypeInference (TypeInferenceContext type_inference, TypeSpec delegate_type)
  913. {
  914. if (!HasExplicitParameters)
  915. return false;
  916. if (!delegate_type.IsDelegate) {
  917. if (!delegate_type.IsExpressionTreeType)
  918. return false;
  919. delegate_type = TypeManager.GetTypeArguments (delegate_type) [0];
  920. if (!delegate_type.IsDelegate)
  921. return false;
  922. }
  923. AParametersCollection d_params = Delegate.GetParameters (delegate_type);
  924. if (d_params.Count != Parameters.Count)
  925. return false;
  926. var ptypes = Parameters.Types;
  927. var dtypes = d_params.Types;
  928. for (int i = 0; i < Parameters.Count; ++i) {
  929. if (type_inference.ExactInference (ptypes[i], dtypes[i]) == 0) {
  930. //
  931. // Continue when 0 (quick path) does not mean inference failure. Checking for
  932. // same type handles cases like int -> int
  933. //
  934. if (ptypes[i] == dtypes[i])
  935. continue;
  936. return false;
  937. }
  938. }
  939. return true;
  940. }
  941. public TypeSpec InferReturnType (ResolveContext ec, TypeInferenceContext tic, TypeSpec delegate_type)
  942. {
  943. Expression expr;
  944. AnonymousExpression am;
  945. if (compatibles.TryGetValue (delegate_type, out expr)) {
  946. am = expr as AnonymousExpression;
  947. return am == null ? null : am.ReturnType;
  948. }
  949. using (ec.Set (ResolveContext.Options.ProbingMode | ResolveContext.Options.InferReturnType)) {
  950. ReportPrinter prev;
  951. if (TypeInferenceReportPrinter != null) {
  952. prev = ec.Report.SetPrinter (TypeInferenceReportPrinter);
  953. } else {
  954. prev = null;
  955. }
  956. var body = CompatibleMethodBody (ec, tic, null, delegate_type);
  957. if (body != null) {
  958. am = body.Compatible (ec, body);
  959. } else {
  960. am = null;
  961. }
  962. if (TypeInferenceReportPrinter != null) {
  963. ec.Report.SetPrinter (prev);
  964. }
  965. }
  966. if (am == null)
  967. return null;
  968. // compatibles.Add (delegate_type, am);
  969. return am.ReturnType;
  970. }
  971. public override bool ContainsEmitWithAwait ()
  972. {
  973. return false;
  974. }
  975. //
  976. // Returns AnonymousMethod container if this anonymous method
  977. // expression can be implicitly converted to the delegate type `delegate_type'
  978. //
  979. public Expression Compatible (ResolveContext ec, TypeSpec type)
  980. {
  981. Expression am;
  982. if (compatibles.TryGetValue (type, out am))
  983. return am;
  984. TypeSpec delegate_type = CompatibleChecks (ec, type);
  985. if (delegate_type == null)
  986. return null;
  987. //
  988. // At this point its the first time we know the return type that is
  989. // needed for the anonymous method. We create the method here.
  990. //
  991. var invoke_mb = Delegate.GetInvokeMethod (delegate_type);
  992. TypeSpec return_type = invoke_mb.ReturnType;
  993. //
  994. // Second: the return type of the delegate must be compatible with
  995. // the anonymous type. Instead of doing a pass to examine the block
  996. // we satisfy the rule by setting the return type on the EmitContext
  997. // to be the delegate type return type.
  998. //
  999. var body = CompatibleMethodBody (ec, null, return_type, delegate_type);
  1000. if (body == null)
  1001. return null;
  1002. bool etree_conversion = delegate_type != type;
  1003. try {
  1004. if (etree_conversion) {
  1005. if (ec.HasSet (ResolveContext.Options.ExpressionTreeConversion)) {
  1006. //
  1007. // Nested expression tree lambda use same scope as parent
  1008. // lambda, this also means no variable capturing between this
  1009. // and parent scope
  1010. //
  1011. am = body.Compatible (ec, ec.CurrentAnonymousMethod);
  1012. //
  1013. // Quote nested expression tree
  1014. //
  1015. if (am != null)
  1016. am = new Quote (am);
  1017. } else {
  1018. int errors = ec.Report.Errors;
  1019. if (Block.IsAsync) {
  1020. ec.Report.Error (1989, loc, "Async lambda expressions cannot be converted to expression trees");
  1021. }
  1022. using (ec.Set (ResolveContext.Options.ExpressionTreeConversion)) {
  1023. am = body.Compatible (ec);
  1024. }
  1025. //
  1026. // Rewrite expressions into expression tree when targeting Expression<T>
  1027. //
  1028. if (am != null && errors == ec.Report.Errors)
  1029. am = CreateExpressionTree (ec, delegate_type);
  1030. }
  1031. } else {
  1032. am = body.Compatible (ec);
  1033. if (body.DirectMethodGroupConversion != null) {
  1034. var errors_printer = new SessionReportPrinter ();
  1035. var old = ec.Report.SetPrinter (errors_printer);
  1036. var expr = new ImplicitDelegateCreation (delegate_type, body.DirectMethodGroupConversion, loc) {
  1037. AllowSpecialMethodsInvocation = true
  1038. }.Resolve (ec);
  1039. ec.Report.SetPrinter (old);
  1040. if (expr != null && errors_printer.ErrorsCount == 0)
  1041. am = expr;
  1042. }
  1043. }
  1044. } catch (CompletionResult) {
  1045. throw;
  1046. } catch (FatalException) {
  1047. throw;
  1048. } catch (Exception e) {
  1049. throw new InternalErrorException (e, loc);
  1050. }
  1051. if (!ec.IsInProbingMode && !etree_conversion) {
  1052. compatibles.Add (type, am ?? EmptyExpression.Null);
  1053. }
  1054. return am;
  1055. }
  1056. protected virtual Expression CreateExpressionTree (ResolveContext ec, TypeSpec delegate_type)
  1057. {
  1058. return CreateExpressionTree (ec);
  1059. }
  1060. public override Expression CreateExpressionTree (ResolveContext ec)
  1061. {
  1062. ec.Report.Error (1946, loc, "An anonymous method cannot be converted to an expression tree");
  1063. return null;
  1064. }
  1065. protected virtual ParametersCompiled ResolveParameters (ResolveContext ec, TypeInferenceContext tic, TypeSpec delegate_type)
  1066. {
  1067. var delegate_parameters = Delegate.GetParameters (delegate_type);
  1068. if (Parameters == ParametersCompiled.Undefined) {
  1069. //
  1070. // We provide a set of inaccessible parameters
  1071. //
  1072. Parameter[] fixedpars = new Parameter[delegate_parameters.Count];
  1073. for (int i = 0; i < delegate_parameters.Count; i++) {
  1074. Parameter.Modifier i_mod = delegate_parameters.FixedParameters [i].ModFlags;
  1075. if ((i_mod & Parameter.Modifier.OUT) != 0) {
  1076. if (!ec.IsInProbingMode) {
  1077. ec.Report.Error (1688, loc,
  1078. "Cannot convert anonymous method block without a parameter list to delegate type `{0}' because it has one or more `out' parameters",
  1079. delegate_type.GetSignatureForError ());
  1080. }
  1081. return null;
  1082. }
  1083. fixedpars[i] = new Parameter (
  1084. new TypeExpression (delegate_parameters.Types [i], loc), null,
  1085. delegate_parameters.FixedParameters [i].ModFlags, null, loc);
  1086. }
  1087. return ParametersCompiled.CreateFullyResolved (fixedpars, delegate_parameters.Types);
  1088. }
  1089. if (!VerifyExplicitParameters (ec, tic, delegate_type, delegate_parameters)) {
  1090. return null;
  1091. }
  1092. return Parameters;
  1093. }
  1094. protected override Expression DoResolve (ResolveContext rc)
  1095. {
  1096. if (rc.HasSet (ResolveContext.Options.ConstantScope)) {
  1097. rc.Report.Error (1706, loc, "Anonymous methods and lambda expressions cannot be used in the current context");
  1098. return null;
  1099. }
  1100. //
  1101. // Update top-level block generated duting parsing with actual top-level block
  1102. //
  1103. if (rc.HasAny (ResolveContext.Options.FieldInitializerScope | ResolveContext.Options.BaseInitializer) && rc.CurrentMemberDefinition.Parent.PartialContainer.PrimaryConstructorParameters != null) {
  1104. var tb = rc.ConstructorBlock.ParametersBlock.TopBlock;
  1105. if (Block.TopBlock != tb) {
  1106. Block b = Block;
  1107. while (b.Parent != Block.TopBlock && b != Block.TopBlock)
  1108. b = b.Parent;
  1109. b.Parent = tb;
  1110. tb.IncludeBlock (Block, Block.TopBlock);
  1111. b.ParametersBlock.TopBlock = tb;
  1112. }
  1113. }
  1114. eclass = ExprClass.Value;
  1115. //
  1116. // This hack means `The type is not accessible
  1117. // anywhere', we depend on special conversion
  1118. // rules.
  1119. //
  1120. type = InternalType.AnonymousMethod;
  1121. if (!DoResolveParameters (rc))
  1122. return null;
  1123. return this;
  1124. }
  1125. protected virtual bool DoResolveParameters (ResolveContext rc)
  1126. {
  1127. return Parameters.Resolve (rc);
  1128. }
  1129. public override void Emit (EmitContext ec)
  1130. {
  1131. // nothing, as we only exist to not do anything.
  1132. }
  1133. public static void Error_AddressOfCapturedVar (ResolveContext rc, IVariableReference var, Location loc)
  1134. {
  1135. if (rc.CurrentAnonymousMethod is AsyncInitializer)
  1136. return;
  1137. rc.Report.Error (1686, loc,
  1138. "Local variable or parameter `{0}' cannot have their address taken and be used inside an anonymous method, lambda expression or query expression",
  1139. var.Name);
  1140. }
  1141. public override string GetSignatureForError ()
  1142. {
  1143. return ExprClassName;
  1144. }
  1145. AnonymousMethodBody CompatibleMethodBody (ResolveContext ec, TypeInferenceContext tic, TypeSpec return_type, TypeSpec delegate_type)
  1146. {
  1147. ParametersCompiled p = ResolveParameters (ec, tic, delegate_type);
  1148. if (p == null)
  1149. return null;
  1150. ParametersBlock b = ec.IsInProbingMode ? (ParametersBlock) Block.PerformClone () : Block;
  1151. if (b.IsAsync) {
  1152. var rt = return_type;
  1153. if (rt != null && rt.Kind != MemberKind.Void && rt != ec.Module.PredefinedTypes.Task.TypeSpec && !rt.IsGenericTask) {
  1154. ec.Report.Error (4010, loc, "Cannot convert async {0} to delegate type `{1}'",
  1155. GetSignatureForError (), delegate_type.GetSignatureForError ());
  1156. return null;
  1157. }
  1158. b = b.ConvertToAsyncTask (ec, ec.CurrentMemberDefinition.Parent.PartialContainer, p, return_type, delegate_type, loc);
  1159. }
  1160. return CompatibleMethodFactory (return_type ?? InternalType.ErrorType, delegate_type, p, b);
  1161. }
  1162. protected virtual AnonymousMethodBody CompatibleMethodFactory (TypeSpec return_type, TypeSpec delegate_type, ParametersCompiled p, ParametersBlock b)
  1163. {
  1164. return new AnonymousMethodBody (p, b, return_type, delegate_type, loc);
  1165. }
  1166. protected override void CloneTo (CloneContext clonectx, Expression t)
  1167. {
  1168. AnonymousMethodExpression target = (AnonymousMethodExpression) t;
  1169. target.Block = (ParametersBlock) clonectx.LookupBlock (Block);
  1170. }
  1171. public override object Accept (StructuralVisitor visitor)
  1172. {
  1173. return visitor.Visit (this);
  1174. }
  1175. }
  1176. //
  1177. // Abstract expression for any block which requires variables hoisting
  1178. //
  1179. public abstract class AnonymousExpression : ExpressionStatement
  1180. {
  1181. protected class AnonymousMethodMethod : Method
  1182. {
  1183. public readonly AnonymousExpression AnonymousMethod;
  1184. public readonly AnonymousMethodStorey Storey;
  1185. public AnonymousMethodMethod (TypeDefinition parent, AnonymousExpression am, AnonymousMethodStorey storey,
  1186. TypeExpr return_type,
  1187. Modifiers mod, MemberName name,
  1188. ParametersCompiled parameters)
  1189. : base (parent, return_type, mod | Modifiers.COMPILER_GENERATED,
  1190. name, parameters, null)
  1191. {
  1192. this.AnonymousMethod = am;
  1193. this.Storey = storey;
  1194. Parent.PartialContainer.Members.Add (this);
  1195. Block = new ToplevelBlock (am.block, parameters);
  1196. }
  1197. public override EmitContext CreateEmitContext (ILGenerator ig, SourceMethodBuilder sourceMethod)
  1198. {
  1199. EmitContext ec = new EmitContext (this, ig, ReturnType, sourceMethod);
  1200. ec.CurrentAnonymousMethod = AnonymousMethod;
  1201. return ec;
  1202. }
  1203. protected override void DefineTypeParameters ()
  1204. {
  1205. // Type parameters were cloned
  1206. }
  1207. protected override bool ResolveMemberType ()
  1208. {
  1209. if (!base.ResolveMemberType ())
  1210. return false;
  1211. if (Storey != null && Storey.Mutator != null) {
  1212. if (!parameters.IsEmpty) {
  1213. var mutated = Storey.Mutator.Mutate (parameters.Types);
  1214. if (mutated != parameters.Types)
  1215. parameters = ParametersCompiled.CreateFullyResolved ((Parameter[]) parameters.FixedParameters, mutated);
  1216. }
  1217. member_type = Storey.Mutator.Mutate (member_type);
  1218. }
  1219. return true;
  1220. }
  1221. public override void Emit ()
  1222. {
  1223. if (MethodBuilder == null) {
  1224. Define ();
  1225. }
  1226. base.Emit ();
  1227. }
  1228. }
  1229. protected readonly ParametersBlock block;
  1230. public TypeSpec ReturnType;
  1231. protected AnonymousExpression (ParametersBlock block, TypeSpec return_type, Location loc)
  1232. {
  1233. this.ReturnType = return_type;
  1234. this.block = block;
  1235. this.loc = loc;
  1236. }
  1237. public abstract string ContainerType { get; }
  1238. public abstract bool IsIterator { get; }
  1239. public abstract AnonymousMethodStorey Storey { get; }
  1240. //
  1241. // The block that makes up the body for the anonymous method
  1242. //
  1243. public ParametersBlock Block {
  1244. get {
  1245. return block;
  1246. }
  1247. }
  1248. public AnonymousExpression Compatible (ResolveContext ec)
  1249. {
  1250. return Compatible (ec, this);
  1251. }
  1252. public AnonymousExpression Compatible (ResolveContext ec, AnonymousExpression ae)
  1253. {
  1254. if (block.Resolved)
  1255. return this;
  1256. // TODO: Implement clone
  1257. BlockContext aec = new BlockContext (ec, block, ReturnType);
  1258. aec.CurrentAnonymousMethod = ae;
  1259. var am = this as AnonymousMethodBody;
  1260. if (ec.HasSet (ResolveContext.Options.InferReturnType) && am != null) {
  1261. am.ReturnTypeInference = new TypeInferenceContext ();
  1262. }
  1263. var bc = ec as BlockContext;
  1264. if (bc != null) {
  1265. aec.AssignmentInfoOffset = bc.AssignmentInfoOffset;
  1266. aec.EnclosingLoop = bc.EnclosingLoop;
  1267. aec.EnclosingLoopOrSwitch = bc.EnclosingLoopOrSwitch;
  1268. aec.Switch = bc.Switch;
  1269. }
  1270. var errors = ec.Report.Errors;
  1271. bool res = Block.Resolve (aec);
  1272. if (res && errors == ec.Report.Errors) {
  1273. MarkReachable (new Reachability ());
  1274. if (!CheckReachableExit (ec.Report)) {
  1275. return null;
  1276. }
  1277. if (bc != null)
  1278. bc.AssignmentInfoOffset = aec.AssignmentInfoOffset;
  1279. }
  1280. if (am != null && am.ReturnTypeInference != null) {
  1281. am.ReturnTypeInference.FixAllTypes (ec);
  1282. ReturnType = am.ReturnTypeInference.InferredTypeArguments [0];
  1283. am.ReturnTypeInference = null;
  1284. //
  1285. // If e is synchronous the inferred return type is T
  1286. // If e is asynchronous and the body of F is either an expression classified as nothing
  1287. // or a statement block where no return statements have expressions, the inferred return type is Task
  1288. // If e is async and has an inferred result type T, the inferred return type is Task<T>
  1289. //
  1290. if (block.IsAsync && ReturnType != null) {
  1291. ReturnType = ReturnType.Kind == MemberKind.Void ?
  1292. ec.Module.PredefinedTypes.Task.TypeSpec :
  1293. ec.Module.PredefinedTypes.TaskGeneric.TypeSpec.MakeGenericType (ec, new [] { ReturnType });
  1294. }
  1295. }
  1296. if (res && errors != ec.Report.Errors)
  1297. return null;
  1298. return res ? this : null;
  1299. }
  1300. public override bool ContainsEmitWithAwait ()
  1301. {
  1302. return false;
  1303. }
  1304. bool CheckReachableExit (Report report)
  1305. {
  1306. if (block.HasReachableClosingBrace && ReturnType.Kind != MemberKind.Void) {
  1307. // FIXME: Flow-analysis on MoveNext generated code
  1308. if (!IsIterator) {
  1309. report.Error (1643, StartLocation,
  1310. "Not all code paths return a value in anonymous method of type `{0}'", GetSignatureForError ());
  1311. return false;
  1312. }
  1313. }
  1314. return true;
  1315. }
  1316. public override void FlowAnalysis (FlowAnalysisContext fc)
  1317. {
  1318. // We are reachable, mark block body reachable too
  1319. MarkReachable (new Reachability ());
  1320. CheckReachableExit (fc.Report);
  1321. var das = fc.BranchDefiniteAssignment ();
  1322. var prev_pb = fc.ParametersBlock;
  1323. fc.ParametersBlock = Block;
  1324. var da_ontrue = fc.DefiniteAssignmentOnTrue;
  1325. var da_onfalse = fc.DefiniteAssignmentOnFalse;
  1326. fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse = null;
  1327. block.FlowAnalysis (fc);
  1328. fc.ParametersBlock = prev_pb;
  1329. fc.DefiniteAssignment = das;
  1330. fc.DefiniteAssignmentOnTrue = da_ontrue;
  1331. fc.DefiniteAssignmentOnFalse = da_onfalse;
  1332. }
  1333. public override void MarkReachable (Reachability rc)
  1334. {
  1335. block.MarkReachable (rc);
  1336. }
  1337. public void SetHasThisAccess ()
  1338. {
  1339. ExplicitBlock b = block;
  1340. do {
  1341. if (b.HasCapturedThis)
  1342. return;
  1343. b.HasCapturedThis = true;
  1344. b = b.Parent == null ? null : b.Parent.Explicit;
  1345. } while (b != null);
  1346. }
  1347. }
  1348. public class AnonymousMethodBody : AnonymousExpression
  1349. {
  1350. protected readonly ParametersCompiled parameters;
  1351. AnonymousMethodStorey storey;
  1352. AnonymousMethodMethod method;
  1353. Field am_cache;
  1354. string block_name;
  1355. TypeInferenceContext return_inference;
  1356. public AnonymousMethodBody (ParametersCompiled parameters,
  1357. ParametersBlock block, TypeSpec return_type, TypeSpec delegate_type,
  1358. Location loc)
  1359. : base (block, return_type, loc)
  1360. {
  1361. this.type = delegate_type;
  1362. this.parameters = parameters;
  1363. }
  1364. #region Properties
  1365. public override string ContainerType {
  1366. get { return "anonymous method"; }
  1367. }
  1368. //
  1369. // Method-group instance for lambdas which can be replaced with
  1370. // simple method group call
  1371. //
  1372. public MethodGroupExpr DirectMethodGroupConversion {
  1373. get; set;
  1374. }
  1375. public override bool IsIterator {
  1376. get {
  1377. return false;
  1378. }
  1379. }
  1380. public ParametersCompiled Parameters {
  1381. get {
  1382. return parameters;
  1383. }
  1384. }
  1385. public TypeInferenceContext ReturnTypeInference {
  1386. get {
  1387. return return_inference;
  1388. }
  1389. set {
  1390. return_inference = value;
  1391. }
  1392. }
  1393. public override AnonymousMethodStorey Storey {
  1394. get {
  1395. return storey;
  1396. }
  1397. }
  1398. #endregion
  1399. public override Expression CreateExpressionTree (ResolveContext ec)
  1400. {
  1401. ec.Report.Error (1945, loc, "An expression tree cannot contain an anonymous method expression");
  1402. return null;
  1403. }
  1404. bool Define (ResolveContext ec)
  1405. {
  1406. if (!Block.Resolved && Compatible (ec) == null)
  1407. return false;
  1408. if (block_name == null) {
  1409. MemberCore mc = (MemberCore) ec.MemberContext;
  1410. block_name = mc.MemberName.Basename;
  1411. }
  1412. return true;
  1413. }
  1414. //
  1415. // Creates a host for the anonymous method
  1416. //
  1417. AnonymousMethodMethod DoCreateMethodHost (EmitContext ec)
  1418. {
  1419. //
  1420. // Anonymous method body can be converted to
  1421. //
  1422. // 1, an instance method in current scope when only `this' is hoisted
  1423. // 2, a static method in current scope when neither `this' nor any variable is hoisted
  1424. // 3, an instance method in compiler generated storey when any hoisted variable exists
  1425. //
  1426. Modifiers modifiers;
  1427. TypeDefinition parent = null;
  1428. TypeParameters hoisted_tparams = null;
  1429. ParametersCompiled method_parameters = parameters;
  1430. var src_block = Block.Original.Explicit;
  1431. if (src_block.HasCapturedVariable || src_block.HasCapturedThis) {
  1432. parent = storey = FindBestMethodStorey ();
  1433. if (storey == null) {
  1434. var top_block = src_block.ParametersBlock.TopBlock;
  1435. var sm = top_block.StateMachine;
  1436. if (src_block.HasCapturedThis) {
  1437. //
  1438. // Remove hoisted 'this' request when simple instance method is
  1439. // enough. No hoisted variables only 'this' and don't need to
  1440. // propagate this to value type state machine.
  1441. //
  1442. StateMachine sm_parent;
  1443. var pb = src_block.ParametersBlock;
  1444. do {
  1445. sm_parent = pb.StateMachine;
  1446. pb = pb.Parent == null ? null : pb.Parent.ParametersBlock;
  1447. } while (sm_parent == null && pb != null);
  1448. if (sm_parent == null) {
  1449. top_block.RemoveThisReferenceFromChildrenBlock (src_block);
  1450. } else if (sm_parent.Kind == MemberKind.Struct) {
  1451. //
  1452. // Special case where parent class is used to emit instance method
  1453. // because currect storey is of value type (async host) and we cannot
  1454. // use ldftn on non-boxed instances either to share mutated state
  1455. //
  1456. parent = sm_parent.Parent.PartialContainer;
  1457. hoisted_tparams = sm_parent.OriginalTypeParameters;
  1458. } else if (sm is IteratorStorey) {
  1459. //
  1460. // For iterators we can host everything in one class
  1461. //
  1462. parent = storey = sm;
  1463. }
  1464. }
  1465. }
  1466. modifiers = storey != null ? Modifiers.INTERNAL : Modifiers.PRIVATE;
  1467. } else {
  1468. if (ec.CurrentAnonymousMethod != null)
  1469. parent = storey = ec.CurrentAnonymousMethod.Storey;
  1470. modifiers = Modifiers.STATIC | Modifiers.PRIVATE;
  1471. //
  1472. // Convert generated method to closed delegate method where unused
  1473. // this argument is generated during compilation which speeds up dispatch
  1474. // by about 25%
  1475. //
  1476. method_parameters = ParametersCompiled.Prefix (method_parameters,
  1477. new Parameter (null, null, 0, null, loc), ec.Module.Compiler.BuiltinTypes.Object);
  1478. }
  1479. if (storey == null && hoisted_tparams == null)
  1480. hoisted_tparams = ec.CurrentTypeParameters;
  1481. if (parent == null)
  1482. parent = ec.CurrentTypeDefinition.Parent.PartialContainer;
  1483. string name = CompilerGeneratedContainer.MakeName (parent != storey ? block_name : null,
  1484. "m", null, parent.PartialContainer.CounterAnonymousMethods++);
  1485. MemberName member_name;
  1486. if (hoisted_tparams != null) {
  1487. var type_params = new TypeParameters (hoisted_tparams.Count);
  1488. for (int i = 0; i < hoisted_tparams.Count; ++i) {
  1489. type_params.Add (hoisted_tparams[i].CreateHoistedCopy (null));
  1490. }
  1491. member_name = new MemberName (name, type_params, Location);
  1492. } else {
  1493. member_name = new MemberName (name, Location);
  1494. }
  1495. return new AnonymousMethodMethod (parent,
  1496. this, storey, new TypeExpression (ReturnType, Location), modifiers,
  1497. member_name, method_parameters);
  1498. }
  1499. protected override Expression DoResolve (ResolveContext ec)
  1500. {
  1501. if (!Define (ec))
  1502. return null;
  1503. eclass = ExprClass.Value;
  1504. return this;
  1505. }
  1506. public override void Emit (EmitContext ec)
  1507. {
  1508. //
  1509. // Use same anonymous method implementation for scenarios where same
  1510. // code is used from multiple blocks, e.g. field initializers
  1511. //
  1512. if (method == null) {
  1513. //
  1514. // Delay an anonymous method definition to avoid emitting unused code
  1515. // for unreachable blocks or expression trees
  1516. //
  1517. method = DoCreateMethodHost (ec);
  1518. method.Define ();
  1519. method.PrepareEmit ();
  1520. }
  1521. bool is_static = (method.ModFlags & Modifiers.STATIC) != 0;
  1522. if (is_static && am_cache == null && !ec.IsStaticConstructor) {
  1523. //
  1524. // Creates a field cache to store delegate instance if it's not generic
  1525. //
  1526. if (!method.MemberName.IsGeneric) {
  1527. var parent = method.Parent.PartialContainer;
  1528. int id = parent.AnonymousMethodsCounter++;
  1529. var cache_type = storey != null && storey.Mutator != null ? storey.Mutator.Mutate (type) : type;
  1530. am_cache = new Field (parent, new TypeExpression (cache_type, loc),
  1531. Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
  1532. new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "am$cache", id), loc), null);
  1533. am_cache.Define ();
  1534. parent.AddField (am_cache);
  1535. } else {
  1536. // TODO: Implement caching of generated generic static methods
  1537. //
  1538. // Idea:
  1539. //
  1540. // Some extra class is needed to capture variable generic type
  1541. // arguments. Maybe we could re-use anonymous types, with a unique
  1542. // anonymous method id, but they are quite heavy.
  1543. //
  1544. // Consider : "() => typeof(T);"
  1545. //
  1546. // We need something like
  1547. // static class Wrap<Tn, Tm, DelegateType> {
  1548. // public static DelegateType cache;
  1549. // }
  1550. //
  1551. // We then specialize local variable to capture all generic parameters
  1552. // and delegate type, e.g. "Wrap<Ta, Tb, DelegateTypeInst> cache;"
  1553. //
  1554. }
  1555. }
  1556. Label l_initialized = ec.DefineLabel ();
  1557. if (am_cache != null) {
  1558. ec.Emit (OpCodes.Ldsfld, am_cache.Spec);
  1559. ec.Emit (OpCodes.Brtrue_S, l_initialized);
  1560. }
  1561. //
  1562. // Load method delegate implementation
  1563. //
  1564. if (is_static) {
  1565. ec.EmitNull ();
  1566. } else if (storey != null) {
  1567. Expression e = storey.GetStoreyInstanceExpression (ec).Resolve (new ResolveContext (ec.MemberContext));
  1568. if (e != null) {
  1569. e.Emit (ec);
  1570. }
  1571. } else {
  1572. ec.EmitThis ();
  1573. //
  1574. // Special case for value type storey where this is not lifted but
  1575. // droped off to parent class
  1576. //
  1577. if (ec.CurrentAnonymousMethod != null && ec.AsyncTaskStorey != null)
  1578. ec.Emit (OpCodes.Ldfld, ec.AsyncTaskStorey.HoistedThis.Field.Spec);
  1579. }
  1580. var delegate_method = method.Spec;
  1581. if (storey != null && storey.MemberName.IsGeneric) {
  1582. TypeSpec t = storey.Instance.Type;
  1583. //
  1584. // Mutate anonymous method instance type if we are in nested
  1585. // hoisted generic anonymous method storey
  1586. //
  1587. if (ec.IsAnonymousStoreyMutateRequired) {
  1588. t = storey.Mutator.Mutate (t);
  1589. }
  1590. ec.Emit (OpCodes.Ldftn, TypeBuilder.GetMethod (t.GetMetaInfo (), (MethodInfo) delegate_method.GetMetaInfo ()));
  1591. } else {
  1592. if (delegate_method.IsGeneric) {
  1593. TypeParameterSpec[] tparams;
  1594. var sm = ec.CurrentAnonymousMethod == null ? null : ec.CurrentAnonymousMethod.Storey as StateMachine;
  1595. if (sm != null && sm.OriginalTypeParameters != null) {
  1596. tparams = sm.CurrentTypeParameters.Types;
  1597. } else {
  1598. tparams = method.TypeParameters;
  1599. }
  1600. delegate_method = delegate_method.MakeGenericMethod (ec.MemberContext, tparams);
  1601. }
  1602. ec.Emit (OpCodes.Ldftn, delegate_method);
  1603. }
  1604. var constructor_method = Delegate.GetConstructor (type);
  1605. ec.Emit (OpCodes.Newobj, constructor_method);
  1606. if (am_cache != null) {
  1607. ec.Emit (OpCodes.Stsfld, am_cache.Spec);
  1608. ec.MarkLabel (l_initialized);
  1609. ec.Emit (OpCodes.Ldsfld, am_cache.Spec);
  1610. }
  1611. }
  1612. public override void EmitStatement (EmitContext ec)
  1613. {
  1614. throw new NotImplementedException ();
  1615. }
  1616. //
  1617. // Look for the best storey for this anonymous method
  1618. //
  1619. AnonymousMethodStorey FindBestMethodStorey ()
  1620. {
  1621. //
  1622. // Use the nearest parent block which has a storey
  1623. //
  1624. for (Block b = Block.Parent; b != null; b = b.Parent) {
  1625. AnonymousMethodStorey s = b.Explicit.AnonymousMethodStorey;
  1626. if (s != null)
  1627. return s;
  1628. }
  1629. return null;
  1630. }
  1631. public override string GetSignatureForError ()
  1632. {
  1633. return type.GetSignatureForError ();
  1634. }
  1635. }
  1636. //
  1637. // Anonymous type container
  1638. //
  1639. public class AnonymousTypeClass : CompilerGeneratedContainer
  1640. {
  1641. public const string ClassNamePrefix = "<>__AnonType";
  1642. public const string SignatureForError = "anonymous type";
  1643. readonly IList<AnonymousTypeParameter> parameters;
  1644. private AnonymousTypeClass (ModuleContainer parent, MemberName name, IList<AnonymousTypeParameter> parameters, Location loc)
  1645. : base (parent, name, parent.Evaluator != null ? Modifiers.PUBLIC : Modifiers.INTERNAL)
  1646. {
  1647. this.parameters = parameters;
  1648. }
  1649. public static AnonymousTypeClass Create (TypeContainer parent, IList<AnonymousTypeParameter> parameters, Location loc)
  1650. {
  1651. string name = ClassNamePrefix + parent.Module.CounterAnonymousTypes++;
  1652. ParametersCompiled all_parameters;
  1653. TypeParameters tparams = null;
  1654. SimpleName[] t_args;
  1655. if (parameters.Count == 0) {
  1656. all_parameters = ParametersCompiled.EmptyReadOnlyParameters;
  1657. t_args = null;
  1658. } else {
  1659. t_args = new SimpleName[parameters.Count];
  1660. tparams = new TypeParameters ();
  1661. Parameter[] ctor_params = new Parameter[parameters.Count];
  1662. for (int i = 0; i < parameters.Count; ++i) {
  1663. AnonymousTypeParameter p = parameters[i];
  1664. for (int ii = 0; ii < i; ++ii) {
  1665. if (parameters[ii].Name == p.Name) {
  1666. parent.Compiler.Report.Error (833, parameters[ii].Location,
  1667. "`{0}': An anonymous type cannot have multiple properties with the same name",
  1668. p.Name);
  1669. p = new AnonymousTypeParameter (null, "$" + i.ToString (), p.Location);
  1670. parameters[i] = p;
  1671. break;
  1672. }
  1673. }
  1674. t_args[i] = new SimpleName ("<" + p.Name + ">__T", p.Location);
  1675. tparams.Add (new TypeParameter (i, new MemberName (t_args[i].Name, p.Location), null, null, Variance.None));
  1676. ctor_params[i] = new Parameter (t_args[i], p.Name, Parameter.Modifier.NONE, null, p.Location);
  1677. }
  1678. all_parameters = new ParametersCompiled (ctor_params);
  1679. }
  1680. //
  1681. // Create generic anonymous type host with generic arguments
  1682. // named upon properties names
  1683. //
  1684. AnonymousTypeClass a_type = new AnonymousTypeClass (parent.Module, new MemberName (name, tparams, loc), parameters, loc);
  1685. Constructor c = new Constructor (a_type, name, Modifiers.PUBLIC | Modifiers.DEBUGGER_HIDDEN,
  1686. null, all_parameters, loc);
  1687. c.Block = new ToplevelBlock (parent.Module.Compiler, c.ParameterInfo, loc);
  1688. //
  1689. // Create fields and constructor body with field initialization
  1690. //
  1691. bool error = false;
  1692. for (int i = 0; i < parameters.Count; ++i) {
  1693. AnonymousTypeParameter p = parameters [i];
  1694. Field f = new Field (a_type, t_args [i], Modifiers.PRIVATE | Modifiers.READONLY | Modifiers.DEBUGGER_HIDDEN,
  1695. new MemberName ("<" + p.Name + ">", p.Location), null);
  1696. if (!a_type.AddField (f)) {
  1697. error = true;
  1698. continue;
  1699. }
  1700. c.Block.AddStatement (new StatementExpression (
  1701. new SimpleAssign (new MemberAccess (new This (p.Location), f.Name),
  1702. c.Block.GetParameterReference (i, p.Location))));
  1703. ToplevelBlock get_block = new ToplevelBlock (parent.Module.Compiler, p.Location);
  1704. get_block.AddStatement (new Return (
  1705. new MemberAccess (new This (p.Location), f.Name), p.Location));
  1706. Property prop = new Property (a_type, t_args [i], Modifiers.PUBLIC,
  1707. new MemberName (p.Name, p.Location), null);
  1708. prop.Get = new Property.GetMethod (prop, 0, null, p.Location);
  1709. prop.Get.Block = get_block;
  1710. a_type.AddMember (prop);
  1711. }
  1712. if (error)
  1713. return null;
  1714. a_type.AddConstructor (c);
  1715. return a_type;
  1716. }
  1717. protected override bool DoDefineMembers ()
  1718. {
  1719. if (!base.DoDefineMembers ())
  1720. return false;
  1721. Location loc = Location;
  1722. var equals_parameters = ParametersCompiled.CreateFullyResolved (
  1723. new Parameter (new TypeExpression (Compiler.BuiltinTypes.Object, loc), "obj", 0, null, loc), Compiler.BuiltinTypes.Object);
  1724. Method equals = new Method (this, new TypeExpression (Compiler.BuiltinTypes.Bool, loc),
  1725. Modifiers.PUBLIC | Modifiers.OVERRIDE | Modifiers.DEBUGGER_HIDDEN, new MemberName ("Equals", loc),
  1726. equals_parameters, null);
  1727. equals_parameters[0].Resolve (equals, 0);
  1728. Method tostring = new Method (this, new TypeExpression (Compiler.BuiltinTypes.String, loc),
  1729. Modifiers.PUBLIC | Modifiers.OVERRIDE | Modifiers.DEBUGGER_HIDDEN, new MemberName ("ToString", loc),
  1730. ParametersCompiled.EmptyReadOnlyParameters, null);
  1731. ToplevelBlock equals_block = new ToplevelBlock (Compiler, equals.ParameterInfo, loc);
  1732. TypeExpr current_type;
  1733. if (CurrentTypeParameters != null) {
  1734. var targs = new TypeArguments ();
  1735. for (int i = 0; i < CurrentTypeParameters.Count; ++i) {
  1736. targs.Add (new TypeParameterExpr (CurrentTypeParameters[i], Location));
  1737. }
  1738. current_type = new GenericTypeExpr (Definition, targs, loc);
  1739. } else {
  1740. current_type = new TypeExpression (Definition, loc);
  1741. }
  1742. var li_other = LocalVariable.CreateCompilerGenerated (CurrentType, equals_block, loc);
  1743. equals_block.AddStatement (new BlockVariable (new TypeExpression (li_other.Type, loc), li_other));
  1744. var other_variable = new LocalVariableReference (li_other, loc);
  1745. MemberAccess system_collections_generic = new MemberAccess (new MemberAccess (
  1746. new QualifiedAliasMember ("global", "System", loc), "Collections", loc), "Generic", loc);
  1747. Expression rs_equals = null;
  1748. Expression string_concat = new StringConstant (Compiler.BuiltinTypes, "{", loc);
  1749. Expression rs_hashcode = new IntConstant (Compiler.BuiltinTypes, -2128831035, loc);
  1750. for (int i = 0; i < parameters.Count; ++i) {
  1751. var p = parameters [i];
  1752. var f = (Field) Members [i * 2];
  1753. MemberAccess equality_comparer = new MemberAccess (new MemberAccess (
  1754. system_collections_generic, "EqualityComparer",
  1755. new TypeArguments (new SimpleName (CurrentTypeParameters [i].Name, loc)), loc),
  1756. "Default", loc);
  1757. Arguments arguments_equal = new Arguments (2);
  1758. arguments_equal.Add (new Argument (new MemberAccess (new This (f.Location), f.Name)));
  1759. arguments_equal.Add (new Argument (new MemberAccess (other_variable, f.Name)));
  1760. Expression field_equal = new Invocation (new MemberAccess (equality_comparer,
  1761. "Equals", loc), arguments_equal);
  1762. Arguments arguments_hashcode = new Arguments (1);
  1763. arguments_hashcode.Add (new Argument (new MemberAccess (new This (f.Location), f.Name)));
  1764. Expression field_hashcode = new Invocation (new MemberAccess (equality_comparer,
  1765. "GetHashCode", loc), arguments_hashcode);
  1766. IntConstant FNV_prime = new IntConstant (Compiler.BuiltinTypes, 16777619, loc);
  1767. rs_hashcode = new Binary (Binary.Operator.Multiply,
  1768. new Binary (Binary.Operator.ExclusiveOr, rs_hashcode, field_hashcode),
  1769. FNV_prime);
  1770. Expression field_to_string = new Conditional (new BooleanExpression (new Binary (Binary.Operator.Inequality,
  1771. new MemberAccess (new This (f.Location), f.Name), new NullLiteral (loc))),
  1772. new Invocation (new MemberAccess (
  1773. new MemberAccess (new This (f.Location), f.Name), "ToString"), null),
  1774. new StringConstant (Compiler.BuiltinTypes, string.Empty, loc), loc);
  1775. if (rs_equals == null) {
  1776. rs_equals = field_equal;
  1777. string_concat = new Binary (Binary.Operator.Addition,
  1778. string_concat,
  1779. new Binary (Binary.Operator.Addition,
  1780. new StringConstant (Compiler.BuiltinTypes, " " + p.Name + " = ", loc),
  1781. field_to_string));
  1782. continue;
  1783. }
  1784. //
  1785. // Implementation of ToString () body using string concatenation
  1786. //
  1787. string_concat = new Binary (Binary.Operator.Addition,
  1788. new Binary (Binary.Operator.Addition,
  1789. string_concat,
  1790. new StringConstant (Compiler.BuiltinTypes, ", " + p.Name + " = ", loc)),
  1791. field_to_string);
  1792. rs_equals = new Binary (Binary.Operator.LogicalAnd, rs_equals, field_equal);
  1793. }
  1794. string_concat = new Binary (Binary.Operator.Addition,
  1795. string_concat,
  1796. new StringConstant (Compiler.BuiltinTypes, " }", loc));
  1797. //
  1798. // Equals (object obj) override
  1799. //
  1800. var other_variable_assign = new TemporaryVariableReference (li_other, loc);
  1801. equals_block.AddStatement (new StatementExpression (
  1802. new SimpleAssign (other_variable_assign,
  1803. new As (equals_block.GetParameterReference (0, loc),
  1804. current_type, loc), loc)));
  1805. Expression equals_test = new Binary (Binary.Operator.Inequality, other_variable, new NullLiteral (loc));
  1806. if (rs_equals != null)
  1807. equals_test = new Binary (Binary.Operator.LogicalAnd, equals_test, rs_equals);
  1808. equals_block.AddStatement (new Return (equals_test, loc));
  1809. equals.Block = equals_block;
  1810. equals.Define ();
  1811. Members.Add (equals);
  1812. //
  1813. // GetHashCode () override
  1814. //
  1815. Method hashcode = new Method (this, new TypeExpression (Compiler.BuiltinTypes.Int, loc),
  1816. Modifiers.PUBLIC | Modifiers.OVERRIDE | Modifiers.DEBUGGER_HIDDEN,
  1817. new MemberName ("GetHashCode", loc),
  1818. ParametersCompiled.EmptyReadOnlyParameters, null);
  1819. //
  1820. // Modified FNV with good avalanche behavior and uniform
  1821. // distribution with larger hash sizes.
  1822. //
  1823. // const int FNV_prime = 16777619;
  1824. // int hash = (int) 2166136261;
  1825. // foreach (int d in data)
  1826. // hash = (hash ^ d) * FNV_prime;
  1827. // hash += hash << 13;
  1828. // hash ^= hash >> 7;
  1829. // hash += hash << 3;
  1830. // hash ^= hash >> 17;
  1831. // hash += hash << 5;
  1832. ToplevelBlock hashcode_top = new ToplevelBlock (Compiler, loc);
  1833. Block hashcode_block = new Block (hashcode_top, loc, loc);
  1834. hashcode_top.AddStatement (new Unchecked (hashcode_block, loc));
  1835. var li_hash = LocalVariable.CreateCompilerGenerated (Compiler.BuiltinTypes.Int, hashcode_top, loc);
  1836. hashcode_block.AddStatement (new BlockVariable (new TypeExpression (li_hash.Type, loc), li_hash));
  1837. LocalVariableReference hash_variable_assign = new LocalVariableReference (li_hash, loc);
  1838. hashcode_block.AddStatement (new StatementExpression (
  1839. new SimpleAssign (hash_variable_assign, rs_hashcode)));
  1840. var hash_variable = new LocalVariableReference (li_hash, loc);
  1841. hashcode_block.AddStatement (new StatementExpression (
  1842. new CompoundAssign (Binary.Operator.Addition, hash_variable,
  1843. new Binary (Binary.Operator.LeftShift, hash_variable, new IntConstant (Compiler.BuiltinTypes, 13, loc)))));
  1844. hashcode_block.AddStatement (new StatementExpression (
  1845. new CompoundAssign (Binary.Operator.ExclusiveOr, hash_variable,
  1846. new Binary (Binary.Operator.RightShift, hash_variable, new IntConstant (Compiler.BuiltinTypes, 7, loc)))));
  1847. hashcode_block.AddStatement (new StatementExpression (
  1848. new CompoundAssign (Binary.Operator.Addition, hash_variable,
  1849. new Binary (Binary.Operator.LeftShift, hash_variable, new IntConstant (Compiler.BuiltinTypes, 3, loc)))));
  1850. hashcode_block.AddStatement (new StatementExpression (
  1851. new CompoundAssign (Binary.Operator.ExclusiveOr, hash_variable,
  1852. new Binary (Binary.Operator.RightShift, hash_variable, new IntConstant (Compiler.BuiltinTypes, 17, loc)))));
  1853. hashcode_block.AddStatement (new StatementExpression (
  1854. new CompoundAssign (Binary.Operator.Addition, hash_variable,
  1855. new Binary (Binary.Operator.LeftShift, hash_variable, new IntConstant (Compiler.BuiltinTypes, 5, loc)))));
  1856. hashcode_block.AddStatement (new Return (hash_variable, loc));
  1857. hashcode.Block = hashcode_top;
  1858. hashcode.Define ();
  1859. Members.Add (hashcode);
  1860. //
  1861. // ToString () override
  1862. //
  1863. ToplevelBlock tostring_block = new ToplevelBlock (Compiler, loc);
  1864. tostring_block.AddStatement (new Return (string_concat, loc));
  1865. tostring.Block = tostring_block;
  1866. tostring.Define ();
  1867. Members.Add (tostring);
  1868. return true;
  1869. }
  1870. public override string GetSignatureForError ()
  1871. {
  1872. return SignatureForError;
  1873. }
  1874. public override CompilationSourceFile GetCompilationSourceFile ()
  1875. {
  1876. return null;
  1877. }
  1878. public IList<AnonymousTypeParameter> Parameters {
  1879. get {
  1880. return parameters;
  1881. }
  1882. }
  1883. }
  1884. }