EmitContext.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  1. //
  2. // EmitContext.cs
  3. //
  4. // Author:
  5. // Miguel de Icaza ([email protected])
  6. // Jb Evain ([email protected])
  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. using System;
  30. using System.Collections.ObjectModel;
  31. using System.Collections.Generic;
  32. using System.IO;
  33. using System.Linq;
  34. using System.Reflection;
  35. using System.Reflection.Emit;
  36. using System.Runtime.CompilerServices;
  37. namespace System.Linq.Expressions {
  38. class CompilationContext {
  39. class HoistedVariableDetector : ExpressionVisitor {
  40. Dictionary<ParameterExpression, LambdaExpression> parameter_to_lambda =
  41. new Dictionary<ParameterExpression, LambdaExpression> ();
  42. Dictionary<LambdaExpression, List<ParameterExpression>> hoisted_map;
  43. LambdaExpression lambda;
  44. public Dictionary<LambdaExpression, List<ParameterExpression>> Process (LambdaExpression lambda)
  45. {
  46. Visit (lambda);
  47. return hoisted_map;
  48. }
  49. protected override void VisitLambda (LambdaExpression lambda)
  50. {
  51. this.lambda = lambda;
  52. foreach (var parameter in lambda.Parameters)
  53. parameter_to_lambda [parameter] = lambda;
  54. base.VisitLambda (lambda);
  55. }
  56. protected override void VisitParameter (ParameterExpression parameter)
  57. {
  58. if (lambda.Parameters.Contains (parameter))
  59. return;
  60. Hoist (parameter);
  61. }
  62. void Hoist (ParameterExpression parameter)
  63. {
  64. LambdaExpression lambda;
  65. if (!parameter_to_lambda.TryGetValue (parameter, out lambda))
  66. return;
  67. if (hoisted_map == null)
  68. hoisted_map = new Dictionary<LambdaExpression, List<ParameterExpression>> ();
  69. List<ParameterExpression> hoisted;
  70. if (!hoisted_map.TryGetValue (lambda, out hoisted)) {
  71. hoisted = new List<ParameterExpression> ();
  72. hoisted_map [lambda] = hoisted;
  73. }
  74. hoisted.Add (parameter);
  75. }
  76. }
  77. List<object> globals = new List<object> ();
  78. List<EmitContext> units = new List<EmitContext> ();
  79. Dictionary<LambdaExpression, List<ParameterExpression>> hoisted_map;
  80. public int AddGlobal (object global)
  81. {
  82. return AddItemToList (global, globals);
  83. }
  84. public object [] GetGlobals ()
  85. {
  86. return globals.ToArray ();
  87. }
  88. static int AddItemToList<T> (T item, IList<T> list)
  89. {
  90. list.Add (item);
  91. return list.Count - 1;
  92. }
  93. public int AddCompilationUnit (LambdaExpression lambda)
  94. {
  95. DetectHoistedVariables (lambda);
  96. return AddCompilationUnit (null, lambda);
  97. }
  98. public int AddCompilationUnit (EmitContext parent, LambdaExpression lambda)
  99. {
  100. var context = new EmitContext (this, parent, lambda);
  101. var unit = AddItemToList (context, units);
  102. context.Emit ();
  103. return unit;
  104. }
  105. void DetectHoistedVariables (LambdaExpression lambda)
  106. {
  107. hoisted_map = new HoistedVariableDetector ().Process (lambda);
  108. }
  109. public List<ParameterExpression> GetHoistedLocals (LambdaExpression lambda)
  110. {
  111. if (hoisted_map == null)
  112. return null;
  113. List<ParameterExpression> hoisted;
  114. hoisted_map.TryGetValue (lambda, out hoisted);
  115. return hoisted;
  116. }
  117. public object [] CreateHoistedLocals (int unit)
  118. {
  119. var hoisted = GetHoistedLocals (units [unit].Lambda);
  120. return new object [hoisted == null ? 0 : hoisted.Count];
  121. }
  122. public Delegate CreateDelegate ()
  123. {
  124. return CreateDelegate (0, new ExecutionScope (this));
  125. }
  126. public Delegate CreateDelegate (int unit, ExecutionScope scope)
  127. {
  128. return units [unit].CreateDelegate (scope);
  129. }
  130. }
  131. class EmitContext {
  132. CompilationContext context;
  133. EmitContext parent;
  134. LambdaExpression lambda;
  135. DynamicMethod method;
  136. LocalBuilder hoisted_store;
  137. List<ParameterExpression> hoisted;
  138. public readonly ILGenerator ig;
  139. public bool HasHoistedLocals {
  140. get { return hoisted != null && hoisted.Count > 0; }
  141. }
  142. public LambdaExpression Lambda {
  143. get { return lambda; }
  144. }
  145. public EmitContext (CompilationContext context, EmitContext parent, LambdaExpression lambda)
  146. {
  147. this.context = context;
  148. this.parent = parent;
  149. this.lambda = lambda;
  150. this.hoisted = context.GetHoistedLocals (lambda);
  151. method = new DynamicMethod (
  152. "lambda_method",
  153. lambda.GetReturnType (),
  154. CreateParameterTypes (lambda.Parameters),
  155. typeof (ExecutionScope),
  156. true);
  157. ig = method.GetILGenerator ();
  158. }
  159. public void Emit ()
  160. {
  161. if (HasHoistedLocals)
  162. EmitStoreHoistedLocals ();
  163. lambda.EmitBody (this);
  164. }
  165. static Type [] CreateParameterTypes (IList<ParameterExpression> parameters)
  166. {
  167. var types = new Type [parameters.Count + 1];
  168. types [0] = typeof (ExecutionScope);
  169. for (int i = 0; i < parameters.Count; i++)
  170. types [i + 1] = parameters [i].Type;
  171. return types;
  172. }
  173. public bool IsLocalParameter (ParameterExpression parameter, ref int position)
  174. {
  175. position = lambda.Parameters.IndexOf (parameter);
  176. if (position > -1) {
  177. position++;
  178. return true;
  179. }
  180. return false;
  181. }
  182. public Delegate CreateDelegate (ExecutionScope scope)
  183. {
  184. return method.CreateDelegate (lambda.Type, scope);
  185. }
  186. public void Emit (Expression expression)
  187. {
  188. expression.Emit (this);
  189. }
  190. public LocalBuilder EmitStored (Expression expression)
  191. {
  192. var local = ig.DeclareLocal (expression.Type);
  193. expression.Emit (this);
  194. ig.Emit (OpCodes.Stloc, local);
  195. return local;
  196. }
  197. public void EmitLoadAddress (Expression expression)
  198. {
  199. ig.Emit (OpCodes.Ldloca, EmitStored (expression));
  200. }
  201. public void EmitLoadSubject (Expression expression)
  202. {
  203. if (expression.Type.IsValueType) {
  204. EmitLoadAddress (expression);
  205. return;
  206. }
  207. Emit (expression);
  208. }
  209. public void EmitLoadSubject (LocalBuilder local)
  210. {
  211. if (local.LocalType.IsValueType) {
  212. EmitLoadAddress (local);
  213. return;
  214. }
  215. EmitLoad (local);
  216. }
  217. public void EmitLoadAddress (LocalBuilder local)
  218. {
  219. ig.Emit (OpCodes.Ldloca, local);
  220. }
  221. public void EmitLoad (LocalBuilder local)
  222. {
  223. ig.Emit (OpCodes.Ldloc, local);
  224. }
  225. public void EmitCall (LocalBuilder local, IList<Expression> arguments, MethodInfo method)
  226. {
  227. EmitLoadSubject (local);
  228. EmitArguments (method, arguments);
  229. EmitCall (method);
  230. }
  231. public void EmitCall (LocalBuilder local, MethodInfo method)
  232. {
  233. EmitLoadSubject (local);
  234. EmitCall (method);
  235. }
  236. public void EmitCall (Expression expression, MethodInfo method)
  237. {
  238. if (!method.IsStatic)
  239. EmitLoadSubject (expression);
  240. EmitCall (method);
  241. }
  242. public void EmitCall (Expression expression, IList<Expression> arguments, MethodInfo method)
  243. {
  244. if (!method.IsStatic)
  245. EmitLoadSubject (expression);
  246. EmitArguments (method, arguments);
  247. EmitCall (method);
  248. }
  249. void EmitArguments (MethodInfo method, IList<Expression> arguments)
  250. {
  251. var parameters = method.GetParameters ();
  252. for (int i = 0; i < parameters.Length; i++) {
  253. var parameter = parameters [i];
  254. var argument = arguments [i];
  255. if (parameter.ParameterType.IsByRef) {
  256. ig.Emit (OpCodes.Ldloca, EmitStored (argument));
  257. continue;
  258. }
  259. Emit (arguments [i]);
  260. }
  261. }
  262. public void EmitCall (MethodInfo method)
  263. {
  264. ig.Emit (
  265. method.IsVirtual ? OpCodes.Callvirt : OpCodes.Call,
  266. method);
  267. }
  268. public void EmitNullableHasValue (LocalBuilder local)
  269. {
  270. EmitCall (local, "get_HasValue");
  271. }
  272. public void EmitNullableInitialize (LocalBuilder local)
  273. {
  274. ig.Emit (OpCodes.Ldloca, local);
  275. ig.Emit (OpCodes.Initobj, local.LocalType);
  276. ig.Emit (OpCodes.Ldloc, local);
  277. }
  278. public void EmitNullableGetValue (LocalBuilder local)
  279. {
  280. EmitCall (local, "get_Value");
  281. }
  282. public void EmitNullableGetValueOrDefault (LocalBuilder local)
  283. {
  284. EmitCall (local, "GetValueOrDefault");
  285. }
  286. void EmitCall (LocalBuilder local, string method_name)
  287. {
  288. EmitCall (local, local.LocalType.GetMethod (method_name, Type.EmptyTypes));
  289. }
  290. public void EmitNullableNew (Type of)
  291. {
  292. ig.Emit (OpCodes.Newobj, of.GetConstructor (new [] { of.GetFirstGenericArgument () }));
  293. }
  294. public void EmitCollection<T> (IEnumerable<T> collection) where T : Expression
  295. {
  296. foreach (var expression in collection)
  297. expression.Emit (this);
  298. }
  299. public void EmitCollection (IEnumerable<ElementInit> initializers, LocalBuilder local)
  300. {
  301. foreach (var initializer in initializers)
  302. initializer.Emit (this, local);
  303. }
  304. public void EmitCollection (IEnumerable<MemberBinding> bindings, LocalBuilder local)
  305. {
  306. foreach (var binding in bindings)
  307. binding.Emit (this, local);
  308. }
  309. public void EmitIsInst (Expression expression, Type candidate)
  310. {
  311. expression.Emit (this);
  312. var type = expression.Type;
  313. if (type.IsValueType)
  314. ig.Emit (OpCodes.Box, type);
  315. ig.Emit (OpCodes.Isinst, candidate);
  316. }
  317. public void EmitScope ()
  318. {
  319. ig.Emit (OpCodes.Ldarg_0);
  320. }
  321. public void EmitReadGlobal (object global)
  322. {
  323. EmitReadGlobal (global, global.GetType ());
  324. }
  325. public void EmitReadGlobal (object global, Type type)
  326. {
  327. EmitScope ();
  328. ig.Emit (OpCodes.Ldfld, typeof (ExecutionScope).GetField ("Globals"));
  329. ig.Emit (OpCodes.Ldc_I4, AddGlobal (global, type));
  330. ig.Emit (OpCodes.Ldelem, typeof (object));
  331. EmitLoadStrongBoxValue (type);
  332. }
  333. public void EmitLoadStrongBoxValue (Type type)
  334. {
  335. var strongbox = type.MakeStrongBoxType ();
  336. ig.Emit (OpCodes.Isinst, strongbox);
  337. ig.Emit (OpCodes.Ldfld, strongbox.GetField ("Value"));
  338. }
  339. int AddGlobal (object value, Type type)
  340. {
  341. return context.AddGlobal (CreateStrongBox (value, type));
  342. }
  343. public void EmitCreateDelegate (LambdaExpression lambda)
  344. {
  345. EmitScope ();
  346. ig.Emit (OpCodes.Ldc_I4, AddChildContext (lambda));
  347. if (hoisted_store != null)
  348. ig.Emit (OpCodes.Ldloc, hoisted_store);
  349. else
  350. ig.Emit (OpCodes.Ldnull);
  351. ig.Emit (OpCodes.Callvirt, typeof (ExecutionScope).GetMethod ("CreateDelegate"));
  352. ig.Emit (OpCodes.Castclass, lambda.Type);
  353. }
  354. void EmitStoreHoistedLocals ()
  355. {
  356. EmitHoistedLocalsStore ();
  357. for (int i = 0; i < hoisted.Count; i++)
  358. EmitStoreHoistedLocal (i, hoisted [i]);
  359. }
  360. void EmitStoreHoistedLocal (int position, ParameterExpression parameter)
  361. {
  362. ig.Emit (OpCodes.Ldloc, hoisted_store);
  363. ig.Emit (OpCodes.Ldc_I4, position);
  364. parameter.Emit (this);
  365. EmitCreateStrongBox (parameter.Type);
  366. ig.Emit (OpCodes.Stelem, typeof (object));
  367. }
  368. void EmitCreateStrongBox (Type type)
  369. {
  370. ig.Emit (OpCodes.Newobj, type.MakeStrongBoxType ().GetConstructor (new [] { type }));
  371. }
  372. void EmitHoistedLocalsStore ()
  373. {
  374. EmitScope ();
  375. hoisted_store = ig.DeclareLocal (typeof (object []));
  376. ig.Emit (OpCodes.Callvirt, typeof (ExecutionScope).GetMethod ("CreateHoistedLocals"));
  377. ig.Emit (OpCodes.Stloc, hoisted_store);
  378. }
  379. public void EmitLoadLocals ()
  380. {
  381. ig.Emit (OpCodes.Ldfld, typeof (ExecutionScope).GetField ("Locals"));
  382. }
  383. public void EmitParentScope ()
  384. {
  385. ig.Emit (OpCodes.Ldfld, typeof (ExecutionScope).GetField ("Parent"));
  386. }
  387. public bool IsHoistedLocal (ParameterExpression parameter, ref int level, ref int position)
  388. {
  389. if (parent == null)
  390. return false;
  391. if (parent.hoisted != null) {
  392. position = parent.hoisted.IndexOf (parameter);
  393. if (position > -1)
  394. return true;
  395. }
  396. level++;
  397. return parent.IsHoistedLocal (parameter, ref level, ref position);
  398. }
  399. int AddChildContext (LambdaExpression lambda)
  400. {
  401. return context.AddCompilationUnit (this, lambda);
  402. }
  403. static object CreateStrongBox (object value, Type type)
  404. {
  405. return Activator.CreateInstance (
  406. type.MakeStrongBoxType (), value);
  407. }
  408. }
  409. }