PageRenderTime 46ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/mcs/class/System.Core/System.Linq.Expressions/EmitContext.cs

https://bitbucket.org/foobar22/mono
C# | 610 lines | 461 code | 121 blank | 28 comment | 35 complexity | 435349dfef2171233697492a9fe41ba9 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, CC-BY-SA-3.0, GPL-2.0, Unlicense, Apache-2.0, LGPL-2.0
  1. //
  2. // EmitContext.cs
  3. //
  4. // Author:
  5. // Miguel de Icaza (miguel@novell.com)
  6. // Jb Evain (jbevain@novell.com)
  7. //
  8. // (C) 2008 Novell, Inc. (http://www.novell.com)
  9. //
  10. // Permission is hereby granted, free of charge, to any person obtaining
  11. // a copy of this software and associated documentation files (the
  12. // "Software"), to deal in the Software without restriction, including
  13. // without limitation the rights to use, copy, modify, merge, publish,
  14. // distribute, sublicense, and/or sell copies of the Software, and to
  15. // permit persons to whom the Software is furnished to do so, subject to
  16. // the following conditions:
  17. //
  18. // The above copyright notice and this permission notice shall be
  19. // included in all copies or substantial portions of the Software.
  20. //
  21. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  22. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  23. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  24. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  25. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  26. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  27. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  28. //
  29. #if !FULL_AOT_RUNTIME
  30. using System;
  31. using System.Collections.ObjectModel;
  32. using System.Collections.Generic;
  33. using System.IO;
  34. using System.Linq;
  35. using System.Reflection;
  36. using System.Reflection.Emit;
  37. using System.Runtime.CompilerServices;
  38. namespace System.Linq.Expressions {
  39. class CompilationContext {
  40. class ParameterReplacer : ExpressionTransformer {
  41. CompilationContext context;
  42. ExecutionScope scope;
  43. object [] locals;
  44. public ParameterReplacer (CompilationContext context, ExecutionScope scope, object [] locals)
  45. {
  46. this.context = context;
  47. this.scope = scope;
  48. this.locals = locals;
  49. }
  50. protected override Expression VisitParameter (ParameterExpression parameter)
  51. {
  52. var scope = this.scope;
  53. var locals = this.locals;
  54. while (scope != null) {
  55. int position = IndexOfHoistedLocal (scope, parameter);
  56. if (position != -1)
  57. return ReadHoistedLocalFromArray (locals, position);
  58. locals = scope.Locals;
  59. scope = scope.Parent;
  60. }
  61. return parameter;
  62. }
  63. Expression ReadHoistedLocalFromArray (object [] locals, int position)
  64. {
  65. return Expression.Field (
  66. Expression.Convert (
  67. Expression.ArrayIndex (
  68. Expression.Constant (locals),
  69. Expression.Constant (position)),
  70. locals [position].GetType ()),
  71. "Value");
  72. }
  73. int IndexOfHoistedLocal (ExecutionScope scope, ParameterExpression parameter)
  74. {
  75. return context.units [scope.compilation_unit].IndexOfHoistedLocal (parameter);
  76. }
  77. }
  78. class HoistedVariableDetector : ExpressionVisitor {
  79. Dictionary<ParameterExpression, LambdaExpression> parameter_to_lambda =
  80. new Dictionary<ParameterExpression, LambdaExpression> ();
  81. Dictionary<LambdaExpression, List<ParameterExpression>> hoisted_map;
  82. LambdaExpression lambda;
  83. public Dictionary<LambdaExpression, List<ParameterExpression>> Process (LambdaExpression lambda)
  84. {
  85. Visit (lambda);
  86. return hoisted_map;
  87. }
  88. protected override void VisitLambda (LambdaExpression lambda)
  89. {
  90. this.lambda = lambda;
  91. foreach (var parameter in lambda.Parameters)
  92. parameter_to_lambda [parameter] = lambda;
  93. base.VisitLambda (lambda);
  94. }
  95. protected override void VisitParameter (ParameterExpression parameter)
  96. {
  97. if (lambda.Parameters.Contains (parameter))
  98. return;
  99. Hoist (parameter);
  100. }
  101. void Hoist (ParameterExpression parameter)
  102. {
  103. LambdaExpression lambda;
  104. if (!parameter_to_lambda.TryGetValue (parameter, out lambda))
  105. return;
  106. if (hoisted_map == null)
  107. hoisted_map = new Dictionary<LambdaExpression, List<ParameterExpression>> ();
  108. List<ParameterExpression> hoisted;
  109. if (!hoisted_map.TryGetValue (lambda, out hoisted)) {
  110. hoisted = new List<ParameterExpression> ();
  111. hoisted_map [lambda] = hoisted;
  112. }
  113. hoisted.Add (parameter);
  114. }
  115. }
  116. List<object> globals = new List<object> ();
  117. List<EmitContext> units = new List<EmitContext> ();
  118. Dictionary<LambdaExpression, List<ParameterExpression>> hoisted_map;
  119. public int AddGlobal (object global)
  120. {
  121. return AddItemToList (global, globals);
  122. }
  123. public object [] GetGlobals ()
  124. {
  125. return globals.ToArray ();
  126. }
  127. static int AddItemToList<T> (T item, IList<T> list)
  128. {
  129. list.Add (item);
  130. return list.Count - 1;
  131. }
  132. public int AddCompilationUnit (LambdaExpression lambda)
  133. {
  134. DetectHoistedVariables (lambda);
  135. return AddCompilationUnit (null, lambda);
  136. }
  137. public int AddCompilationUnit (EmitContext parent, LambdaExpression lambda)
  138. {
  139. var context = new EmitContext (this, parent, lambda);
  140. var unit = AddItemToList (context, units);
  141. context.Emit ();
  142. return unit;
  143. }
  144. void DetectHoistedVariables (LambdaExpression lambda)
  145. {
  146. hoisted_map = new HoistedVariableDetector ().Process (lambda);
  147. }
  148. public List<ParameterExpression> GetHoistedLocals (LambdaExpression lambda)
  149. {
  150. if (hoisted_map == null)
  151. return null;
  152. List<ParameterExpression> hoisted;
  153. hoisted_map.TryGetValue (lambda, out hoisted);
  154. return hoisted;
  155. }
  156. public object [] CreateHoistedLocals (int unit)
  157. {
  158. var hoisted = GetHoistedLocals (units [unit].Lambda);
  159. return new object [hoisted == null ? 0 : hoisted.Count];
  160. }
  161. public Expression IsolateExpression (ExecutionScope scope, object [] locals, Expression expression)
  162. {
  163. return new ParameterReplacer (this, scope, locals).Transform (expression);
  164. }
  165. public Delegate CreateDelegate ()
  166. {
  167. return CreateDelegate (0, new ExecutionScope (this));
  168. }
  169. public Delegate CreateDelegate (int unit, ExecutionScope scope)
  170. {
  171. return units [unit].CreateDelegate (scope);
  172. }
  173. }
  174. class EmitContext {
  175. CompilationContext context;
  176. EmitContext parent;
  177. LambdaExpression lambda;
  178. DynamicMethod method;
  179. LocalBuilder hoisted_store;
  180. List<ParameterExpression> hoisted;
  181. public readonly ILGenerator ig;
  182. public bool HasHoistedLocals {
  183. get { return hoisted != null && hoisted.Count > 0; }
  184. }
  185. public LambdaExpression Lambda {
  186. get { return lambda; }
  187. }
  188. public EmitContext (CompilationContext context, EmitContext parent, LambdaExpression lambda)
  189. {
  190. this.context = context;
  191. this.parent = parent;
  192. this.lambda = lambda;
  193. this.hoisted = context.GetHoistedLocals (lambda);
  194. method = new DynamicMethod (
  195. "lambda_method",
  196. lambda.GetReturnType (),
  197. CreateParameterTypes (lambda.Parameters),
  198. typeof (ExecutionScope),
  199. true);
  200. ig = method.GetILGenerator ();
  201. }
  202. public void Emit ()
  203. {
  204. if (HasHoistedLocals)
  205. EmitStoreHoistedLocals ();
  206. lambda.EmitBody (this);
  207. }
  208. static Type [] CreateParameterTypes (IList<ParameterExpression> parameters)
  209. {
  210. var types = new Type [parameters.Count + 1];
  211. types [0] = typeof (ExecutionScope);
  212. for (int i = 0; i < parameters.Count; i++)
  213. types [i + 1] = parameters [i].Type;
  214. return types;
  215. }
  216. public bool IsLocalParameter (ParameterExpression parameter, ref int position)
  217. {
  218. position = lambda.Parameters.IndexOf (parameter);
  219. if (position > -1) {
  220. position++;
  221. return true;
  222. }
  223. return false;
  224. }
  225. public Delegate CreateDelegate (ExecutionScope scope)
  226. {
  227. return method.CreateDelegate (lambda.Type, scope);
  228. }
  229. public void Emit (Expression expression)
  230. {
  231. expression.Emit (this);
  232. }
  233. public LocalBuilder EmitStored (Expression expression)
  234. {
  235. var local = ig.DeclareLocal (expression.Type);
  236. expression.Emit (this);
  237. ig.Emit (OpCodes.Stloc, local);
  238. return local;
  239. }
  240. public void EmitLoadAddress (Expression expression)
  241. {
  242. ig.Emit (OpCodes.Ldloca, EmitStored (expression));
  243. }
  244. public void EmitLoadEnum (Expression expression)
  245. {
  246. expression.Emit (this);
  247. ig.Emit (OpCodes.Box, expression.Type);
  248. }
  249. public void EmitLoadEnum (LocalBuilder local)
  250. {
  251. ig.Emit (OpCodes.Ldloc, local);
  252. ig.Emit (OpCodes.Box, local.LocalType);
  253. }
  254. public void EmitLoadSubject (Expression expression)
  255. {
  256. if (expression.Type.IsEnum) {
  257. EmitLoadEnum (expression);
  258. return;
  259. }
  260. if (expression.Type.IsValueType) {
  261. EmitLoadAddress (expression);
  262. return;
  263. }
  264. Emit (expression);
  265. }
  266. public void EmitLoadSubject (LocalBuilder local)
  267. {
  268. if (local.LocalType.IsEnum) {
  269. EmitLoadEnum (local);
  270. return;
  271. }
  272. if (local.LocalType.IsValueType) {
  273. EmitLoadAddress (local);
  274. return;
  275. }
  276. EmitLoad (local);
  277. }
  278. public void EmitLoadAddress (LocalBuilder local)
  279. {
  280. ig.Emit (OpCodes.Ldloca, local);
  281. }
  282. public void EmitLoad (LocalBuilder local)
  283. {
  284. ig.Emit (OpCodes.Ldloc, local);
  285. }
  286. public void EmitCall (LocalBuilder local, IList<Expression> arguments, MethodInfo method)
  287. {
  288. EmitLoadSubject (local);
  289. EmitArguments (method, arguments);
  290. EmitCall (method);
  291. }
  292. public void EmitCall (LocalBuilder local, MethodInfo method)
  293. {
  294. EmitLoadSubject (local);
  295. EmitCall (method);
  296. }
  297. public void EmitCall (Expression expression, MethodInfo method)
  298. {
  299. if (!method.IsStatic)
  300. EmitLoadSubject (expression);
  301. EmitCall (method);
  302. }
  303. public void EmitCall (Expression expression, IList<Expression> arguments, MethodInfo method)
  304. {
  305. if (!method.IsStatic)
  306. EmitLoadSubject (expression);
  307. EmitArguments (method, arguments);
  308. EmitCall (method);
  309. }
  310. void EmitArguments (MethodInfo method, IList<Expression> arguments)
  311. {
  312. var parameters = method.GetParameters ();
  313. for (int i = 0; i < parameters.Length; i++) {
  314. var parameter = parameters [i];
  315. var argument = arguments [i];
  316. if (parameter.ParameterType.IsByRef) {
  317. ig.Emit (OpCodes.Ldloca, EmitStored (argument));
  318. continue;
  319. }
  320. Emit (arguments [i]);
  321. }
  322. }
  323. public void EmitCall (MethodInfo method)
  324. {
  325. ig.Emit (
  326. method.IsVirtual ? OpCodes.Callvirt : OpCodes.Call,
  327. method);
  328. }
  329. public void EmitNullableHasValue (LocalBuilder local)
  330. {
  331. EmitCall (local, "get_HasValue");
  332. }
  333. public void EmitNullableInitialize (LocalBuilder local)
  334. {
  335. ig.Emit (OpCodes.Ldloca, local);
  336. ig.Emit (OpCodes.Initobj, local.LocalType);
  337. ig.Emit (OpCodes.Ldloc, local);
  338. }
  339. public void EmitNullableGetValue (LocalBuilder local)
  340. {
  341. EmitCall (local, "get_Value");
  342. }
  343. public void EmitNullableGetValueOrDefault (LocalBuilder local)
  344. {
  345. EmitCall (local, "GetValueOrDefault");
  346. }
  347. void EmitCall (LocalBuilder local, string method_name)
  348. {
  349. EmitCall (local, local.LocalType.GetMethod (method_name, Type.EmptyTypes));
  350. }
  351. public void EmitNullableNew (Type of)
  352. {
  353. ig.Emit (OpCodes.Newobj, of.GetConstructor (new [] { of.GetFirstGenericArgument () }));
  354. }
  355. public void EmitCollection<T> (IEnumerable<T> collection) where T : Expression
  356. {
  357. foreach (var expression in collection)
  358. expression.Emit (this);
  359. }
  360. public void EmitCollection (IEnumerable<ElementInit> initializers, LocalBuilder local)
  361. {
  362. foreach (var initializer in initializers)
  363. initializer.Emit (this, local);
  364. }
  365. public void EmitCollection (IEnumerable<MemberBinding> bindings, LocalBuilder local)
  366. {
  367. foreach (var binding in bindings)
  368. binding.Emit (this, local);
  369. }
  370. public void EmitIsInst (Expression expression, Type candidate)
  371. {
  372. expression.Emit (this);
  373. var type = expression.Type;
  374. if (type.IsValueType)
  375. ig.Emit (OpCodes.Box, type);
  376. ig.Emit (OpCodes.Isinst, candidate);
  377. }
  378. public void EmitScope ()
  379. {
  380. ig.Emit (OpCodes.Ldarg_0);
  381. }
  382. public void EmitReadGlobal (object global)
  383. {
  384. EmitReadGlobal (global, global.GetType ());
  385. }
  386. public void EmitLoadGlobals ()
  387. {
  388. EmitScope ();
  389. ig.Emit (OpCodes.Ldfld, typeof (ExecutionScope).GetField ("Globals"));
  390. }
  391. public void EmitReadGlobal (object global, Type type)
  392. {
  393. EmitLoadGlobals ();
  394. ig.Emit (OpCodes.Ldc_I4, AddGlobal (global, type));
  395. ig.Emit (OpCodes.Ldelem, typeof (object));
  396. EmitLoadStrongBoxValue (type);
  397. }
  398. public void EmitLoadStrongBoxValue (Type type)
  399. {
  400. var strongbox = type.MakeStrongBoxType ();
  401. ig.Emit (OpCodes.Isinst, strongbox);
  402. ig.Emit (OpCodes.Ldfld, strongbox.GetField ("Value"));
  403. }
  404. int AddGlobal (object value, Type type)
  405. {
  406. return context.AddGlobal (CreateStrongBox (value, type));
  407. }
  408. public void EmitCreateDelegate (LambdaExpression lambda)
  409. {
  410. EmitScope ();
  411. ig.Emit (OpCodes.Ldc_I4, AddChildContext (lambda));
  412. if (hoisted_store != null)
  413. ig.Emit (OpCodes.Ldloc, hoisted_store);
  414. else
  415. ig.Emit (OpCodes.Ldnull);
  416. ig.Emit (OpCodes.Callvirt, typeof (ExecutionScope).GetMethod ("CreateDelegate"));
  417. ig.Emit (OpCodes.Castclass, lambda.Type);
  418. }
  419. void EmitStoreHoistedLocals ()
  420. {
  421. EmitHoistedLocalsStore ();
  422. for (int i = 0; i < hoisted.Count; i++)
  423. EmitStoreHoistedLocal (i, hoisted [i]);
  424. }
  425. void EmitStoreHoistedLocal (int position, ParameterExpression parameter)
  426. {
  427. ig.Emit (OpCodes.Ldloc, hoisted_store);
  428. ig.Emit (OpCodes.Ldc_I4, position);
  429. parameter.Emit (this);
  430. EmitCreateStrongBox (parameter.Type);
  431. ig.Emit (OpCodes.Stelem, typeof (object));
  432. }
  433. public void EmitLoadHoistedLocalsStore ()
  434. {
  435. ig.Emit (OpCodes.Ldloc, hoisted_store);
  436. }
  437. void EmitCreateStrongBox (Type type)
  438. {
  439. ig.Emit (OpCodes.Newobj, type.MakeStrongBoxType ().GetConstructor (new [] { type }));
  440. }
  441. void EmitHoistedLocalsStore ()
  442. {
  443. EmitScope ();
  444. hoisted_store = ig.DeclareLocal (typeof (object []));
  445. ig.Emit (OpCodes.Callvirt, typeof (ExecutionScope).GetMethod ("CreateHoistedLocals"));
  446. ig.Emit (OpCodes.Stloc, hoisted_store);
  447. }
  448. public void EmitLoadLocals ()
  449. {
  450. ig.Emit (OpCodes.Ldfld, typeof (ExecutionScope).GetField ("Locals"));
  451. }
  452. public void EmitParentScope ()
  453. {
  454. ig.Emit (OpCodes.Ldfld, typeof (ExecutionScope).GetField ("Parent"));
  455. }
  456. public void EmitIsolateExpression ()
  457. {
  458. ig.Emit (OpCodes.Callvirt, typeof (ExecutionScope).GetMethod ("IsolateExpression"));
  459. }
  460. public int IndexOfHoistedLocal (ParameterExpression parameter)
  461. {
  462. if (!HasHoistedLocals)
  463. return -1;
  464. return hoisted.IndexOf (parameter);
  465. }
  466. public bool IsHoistedLocal (ParameterExpression parameter, ref int level, ref int position)
  467. {
  468. if (parent == null)
  469. return false;
  470. if (parent.hoisted != null) {
  471. position = parent.hoisted.IndexOf (parameter);
  472. if (position > -1)
  473. return true;
  474. }
  475. level++;
  476. return parent.IsHoistedLocal (parameter, ref level, ref position);
  477. }
  478. int AddChildContext (LambdaExpression lambda)
  479. {
  480. return context.AddCompilationUnit (this, lambda);
  481. }
  482. static object CreateStrongBox (object value, Type type)
  483. {
  484. return Activator.CreateInstance (
  485. type.MakeStrongBoxType (), value);
  486. }
  487. }
  488. }
  489. #endif