PageRenderTime 48ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/Microsoft.Scripting/Generation/GlobalStaticFieldRewriter.cs

https://bitbucket.org/stefanrusek/xronos
C# | 314 lines | 202 code | 51 blank | 61 comment | 15 complexity | f0b26f48a0130af080d7612daa633712 MD5 | raw file
  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) Microsoft Corporation.
  4. *
  5. * This source code is subject to terms and conditions of the Microsoft Public License. A
  6. * copy of the license can be found in the License.html file at the root of this distribution. If
  7. * you cannot locate the Microsoft Public License, please send an email to
  8. * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
  9. * by the terms of the Microsoft Public License.
  10. *
  11. * You must not remove this notice, or any other, from this software.
  12. *
  13. *
  14. * ***************************************************************************/
  15. #if CODEPLEX_40
  16. using System;
  17. #else
  18. using System; using Microsoft;
  19. #endif
  20. using System.Collections.Generic;
  21. using System.Diagnostics;
  22. #if CODEPLEX_40
  23. using System.Linq.Expressions;
  24. #else
  25. using Microsoft.Linq.Expressions;
  26. #endif
  27. using System.Reflection;
  28. using System.Reflection.Emit;
  29. using Microsoft.Scripting.Ast;
  30. using Microsoft.Scripting.Runtime;
  31. using Microsoft.Scripting.Utils;
  32. namespace Microsoft.Scripting.Generation {
  33. /// <summary>
  34. /// Rewrites globals to static fields on a type
  35. /// Also rewrites constants to static fields
  36. /// </summary>
  37. internal sealed class GlobalStaticFieldRewriter : GlobalOptimizedRewriter {
  38. private readonly Dictionary<GlobalVariableExpression, FieldBuilder> _fields = new Dictionary<GlobalVariableExpression, FieldBuilder>();
  39. private FieldBuilder _contextField;
  40. // TODO: remove this static data, and switch to instance data.
  41. // It's only static currently because NewTypeMaker has a dependency on
  42. // OptimizedScriptCode.InitializeFields
  43. private readonly Dictionary<object, int> _constantCache = new Dictionary<object, int>(ReferenceEqualityComparer<object>.Instance);
  44. private List<FieldBuilder> _staticFields = new List<FieldBuilder>();
  45. private static readonly List<object> _staticData = new List<object>();
  46. private static readonly object _nullVal = new object();
  47. [MultiRuntimeAware]
  48. private static int _lastCheck, _empties;
  49. internal GlobalStaticFieldRewriter(TypeGen typeGen) {
  50. TypeGen = typeGen;
  51. }
  52. protected override Expression VisitLambda<T>(Expression<T> node) {
  53. // only run this for top lambda
  54. if (_contextField == null) {
  55. // Optimization: use the static field codecontext rather than
  56. // the argument. It's faster for the nested functions that
  57. // would otherwise need to close over the context argument.
  58. _contextField = TypeGen.TypeBuilder.DefineField(
  59. CodeContext.ContextFieldName,
  60. typeof(CodeContext),
  61. FieldAttributes.Public | FieldAttributes.Static
  62. );
  63. Context = Expression.Field(null, _contextField);
  64. }
  65. return base.VisitLambda(node);
  66. }
  67. protected override Expression MakeWrapper(GlobalVariableExpression variable) {
  68. Debug.Assert(!_fields.ContainsKey(variable));
  69. FieldBuilder field = TypeGen.TypeBuilder.DefineField(
  70. variable.Name,
  71. typeof(ModuleGlobalWrapper),
  72. FieldAttributes.Assembly | FieldAttributes.Static
  73. );
  74. _fields.Add(variable, field);
  75. return Expression.Field(null, field);
  76. }
  77. #region runtime constant support
  78. protected override Expression VisitConstant(ConstantExpression node) {
  79. object data = node.Value;
  80. Type type = node.Type;
  81. // if the constant can be emitted into IL, nothing to do
  82. if (CompilerHelpers.CanEmitConstant(data, type)) {
  83. return node;
  84. }
  85. type = TypeUtils.GetConstantType(type);
  86. int index;
  87. if (!_constantCache.TryGetValue(data, out index)) {
  88. int number = AddStaticData(data);
  89. FieldBuilder field = TypeGen.AddStaticField(type, "#Constant" + number);
  90. index = _staticFields.Count;
  91. _staticFields.Add(field);
  92. _constantCache.Add(data, index);
  93. }
  94. return Expression.Field(null, _staticFields[index]);
  95. }
  96. private static int AddStaticData(object data) {
  97. lock (_staticData) {
  98. if (_empties != 0) {
  99. while (_lastCheck < _staticData.Count) {
  100. if (_staticData[_lastCheck] == null) {
  101. _staticData[_lastCheck] = data == null ? _nullVal : data;
  102. _empties--;
  103. return _lastCheck;
  104. }
  105. _lastCheck++;
  106. }
  107. }
  108. _lastCheck = 0;
  109. _staticData.Add(data == null ? _nullVal : data);
  110. return _staticData.Count - 1;
  111. }
  112. }
  113. internal static object GetConstantData(int index) {
  114. lock (_staticData) {
  115. object res = _staticData[index];
  116. _staticData[index] = null;
  117. _empties++;
  118. Debug.Assert(res != null);
  119. return res == _nullVal ? null : res;
  120. }
  121. }
  122. internal static object GetConstantDataReusable(int index) {
  123. lock (_staticData) {
  124. object res = _staticData[index];
  125. Debug.Assert(res != null);
  126. return res == _nullVal ? null : res;
  127. }
  128. }
  129. #endregion
  130. internal void EmitDictionary() {
  131. MakeGetMethod();
  132. MakeSetMethod();
  133. MakeRawKeysMethod();
  134. MakeInitialization();
  135. }
  136. #region EmitDictionary implementation
  137. private void MakeInitialization() {
  138. TypeGen.TypeBuilder.AddInterfaceImplementation(typeof(IModuleDictionaryInitialization));
  139. MethodInfo baseMethod = typeof(IModuleDictionaryInitialization).GetMethod("InitializeModuleDictionary");
  140. ILGen cg = TypeGen.DefineExplicitInterfaceImplementation(baseMethod);
  141. Label ok = cg.DefineLabel();
  142. cg.EmitFieldGet(_contextField);
  143. cg.Emit(OpCodes.Ldnull);
  144. cg.Emit(OpCodes.Ceq);
  145. cg.Emit(OpCodes.Brtrue_S, ok);
  146. cg.EmitNew(typeof(InvalidOperationException), Type.EmptyTypes);
  147. cg.Emit(OpCodes.Throw);
  148. cg.MarkLabel(ok);
  149. // arg0 -> this
  150. // arg1 -> MyModuleDictType.ContextSlot
  151. cg.EmitLoadArg(1);
  152. cg.EmitFieldSet(_contextField);
  153. ConstructorInfo wrapperCtor = typeof(ModuleGlobalWrapper).GetConstructor(new Type[] { typeof(CodeContext), typeof(SymbolId) });
  154. foreach (KeyValuePair<GlobalVariableExpression, FieldBuilder> kv in _fields) {
  155. // wrapper = new ModuleGlobalWrapper(context, name);
  156. cg.EmitLoadArg(1);
  157. EmitSymbolId(cg, SymbolTable.StringToId(kv.Key.Name));
  158. cg.Emit(OpCodes.Newobj, wrapperCtor);
  159. cg.Emit(OpCodes.Stsfld, kv.Value);
  160. }
  161. cg.Emit(OpCodes.Ret);
  162. }
  163. //
  164. // This generates a method like the following:
  165. //
  166. // TryGetExtraValue(int name, object out value) {
  167. // if (name1 == name) {
  168. // value = type.name1Slot.RawValue;
  169. // return value != Uninitialized.Instance;
  170. // }
  171. // if (name2 == name) {
  172. // value = type.name2Slot.RawValue;
  173. // return value != Uninitialized.Instance;
  174. // }
  175. // ...
  176. // return false
  177. // }
  178. private void MakeGetMethod() {
  179. MethodInfo baseMethod = typeof(CustomSymbolDictionary).GetMethod("TryGetExtraValue", BindingFlags.NonPublic | BindingFlags.Instance);
  180. ILGen cg = TypeGen.DefineMethodOverride(baseMethod);
  181. foreach (KeyValuePair<GlobalVariableExpression, FieldBuilder> kv in _fields) {
  182. SymbolId name = SymbolTable.StringToId(kv.Key.Name);
  183. EmitSymbolId(cg, name);
  184. // arg0 -> this
  185. cg.EmitLoadArg(1);
  186. cg.EmitCall(typeof(SymbolId), "op_Equality");
  187. Label next = cg.DefineLabel();
  188. cg.Emit(OpCodes.Brfalse_S, next);
  189. cg.EmitLoadArg(2);
  190. // Expects to push as an object.
  191. EmitGetRawFromObject(cg, kv.Value);
  192. cg.Emit(OpCodes.Stind_Ref);
  193. EmitGetRawFromObject(cg, kv.Value);
  194. cg.EmitFieldGet(typeof(Uninitialized), "Instance");
  195. cg.Emit(OpCodes.Ceq);
  196. cg.Emit(OpCodes.Not);
  197. cg.Emit(OpCodes.Ret);
  198. cg.MarkLabel(next);
  199. }
  200. cg.EmitInt(0);
  201. cg.Emit(OpCodes.Ret);
  202. }
  203. private static void EmitGetRawFromObject(ILGen cg, FieldBuilder wrapper) {
  204. cg.EmitFieldGet(wrapper);
  205. cg.EmitPropertyGet(typeof(ModuleGlobalWrapper), "RawValue");
  206. }
  207. // This generates a method like the following:
  208. //
  209. // TrySetExtraValue(object name, object value) {
  210. // if (name1 == name) {
  211. // type.name1Slot = value;
  212. // return 1;
  213. // }
  214. // if (name2 == name) {
  215. // type.name2Slot = value;
  216. // return 1;
  217. // }
  218. // ...
  219. // return 0
  220. // }
  221. private void MakeSetMethod() {
  222. MethodInfo baseMethod = typeof(CustomSymbolDictionary).GetMethod("TrySetExtraValue", BindingFlags.NonPublic | BindingFlags.Instance);
  223. ILGen cg = TypeGen.DefineMethodOverride(baseMethod);
  224. foreach (KeyValuePair<GlobalVariableExpression, FieldBuilder> kv in _fields) {
  225. SymbolId name = SymbolTable.StringToId(kv.Key.Name);
  226. EmitSymbolId(cg, name);
  227. // arg0 -> this
  228. cg.EmitLoadArg(1);
  229. cg.EmitCall(typeof(SymbolId), "op_Equality");
  230. Label next = cg.DefineLabel();
  231. cg.Emit(OpCodes.Brfalse_S, next);
  232. cg.EmitFieldGet(kv.Value);
  233. cg.EmitLoadArg(2);
  234. cg.EmitPropertySet(typeof(ModuleGlobalWrapper), "CurrentValue");
  235. cg.EmitInt(1);
  236. cg.Emit(OpCodes.Ret);
  237. cg.MarkLabel(next);
  238. }
  239. cg.EmitInt(0);
  240. cg.Emit(OpCodes.Ret);
  241. }
  242. private ILGen MakeRawKeysMethod() {
  243. FieldBuilder rawKeysCache = TypeGen.AddStaticField(typeof(SymbolId[]), "ExtraKeysCache");
  244. ILGen init = TypeGen.TypeInitializer;
  245. init.EmitInt(_fields.Count);
  246. init.Emit(OpCodes.Newarr, typeof(SymbolId));
  247. int current = 0;
  248. foreach (GlobalVariableExpression variable in _fields.Keys) {
  249. init.Emit(OpCodes.Dup);
  250. init.EmitInt(current++);
  251. EmitSymbolId(init, SymbolTable.StringToId(variable.Name));
  252. init.EmitStoreElement(typeof(SymbolId));
  253. }
  254. init.Emit(OpCodes.Stsfld, rawKeysCache);
  255. MethodInfo baseMethod = typeof(CustomSymbolDictionary).GetMethod("GetExtraKeys", BindingFlags.Public | BindingFlags.Instance);
  256. ILGen cg = TypeGen.DefineExplicitInterfaceImplementation(baseMethod);
  257. cg.Emit(OpCodes.Ldsfld, rawKeysCache);
  258. cg.Emit(OpCodes.Ret);
  259. return cg;
  260. }
  261. #endregion
  262. }
  263. }