/Microsoft.Scripting/Runtime/ScriptingRuntimeHelpers.cs

https://bitbucket.org/stefanrusek/xronos · C# · 290 lines · 185 code · 40 blank · 65 comment · 35 complexity · 7814d81416c8bbd869ddae2b4f68b11a 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;
  21. using System.Collections.Generic;
  22. using System.Diagnostics;
  23. using System.Reflection;
  24. using System.Runtime.CompilerServices;
  25. #if !CODEPLEX_40
  26. using Microsoft.Runtime.CompilerServices;
  27. #endif
  28. using Microsoft.Scripting.Actions;
  29. using Microsoft.Scripting.Generation;
  30. using Microsoft.Scripting.Utils;
  31. namespace Microsoft.Scripting.Runtime {
  32. /// <summary>
  33. /// These are some generally useful helper methods. Currently the only methods are those to
  34. /// cached boxed representations of commonly used primitive types so that they can be shared.
  35. /// This is useful to most dynamic languages that use object as a universal type.
  36. ///
  37. /// The methods in RuntimeHelepers are caleld by the generated code. From here the methods may
  38. /// dispatch to other parts of the runtime to get bulk of the work done, but the entry points
  39. /// should be here.
  40. /// </summary>
  41. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")]
  42. public static partial class ScriptingRuntimeHelpers {
  43. private const int MIN_CACHE = -100;
  44. private const int MAX_CACHE = 1000;
  45. private static readonly object[] cache = MakeCache();
  46. /// <summary>
  47. /// A singleton boxed boolean true.
  48. /// </summary>
  49. public static readonly object True = true;
  50. /// <summary>
  51. ///A singleton boxed boolean false.
  52. /// </summary>
  53. public static readonly object False = false;
  54. internal static readonly MethodInfo BooleanToObjectMethod = typeof(ScriptingRuntimeHelpers).GetMethod("BooleanToObject");
  55. internal static readonly MethodInfo Int32ToObjectMethod = typeof(ScriptingRuntimeHelpers).GetMethod("Int32ToObject");
  56. private static object[] MakeCache() {
  57. object[] result = new object[MAX_CACHE - MIN_CACHE];
  58. for (int i = 0; i < result.Length; i++) {
  59. result[i] = (object)(i + MIN_CACHE);
  60. }
  61. return result;
  62. }
  63. /// <summary>
  64. /// Gets a singleton boxed value for the given integer if possible, otherwise boxes the integer.
  65. /// </summary>
  66. /// <param name="value">The value to box.</param>
  67. /// <returns>The boxed value.</returns>
  68. public static object Int32ToObject(Int32 value) {
  69. // caches improves pystone by ~5-10% on MS .Net 1.1, this is a very integer intense app
  70. // TODO: investigate if this still helps perf. There's evidence that it's harmful on
  71. // .NET 3.5 and 4.0
  72. if (value < MAX_CACHE && value >= MIN_CACHE) {
  73. return cache[value - MIN_CACHE];
  74. }
  75. return (object)value;
  76. }
  77. private static readonly string[] chars = MakeSingleCharStrings();
  78. private static string[] MakeSingleCharStrings() {
  79. string[] result = new string[255];
  80. for (char ch = (char)0; ch < result.Length; ch++) {
  81. result[ch] = new string(ch, 1);
  82. }
  83. return result;
  84. }
  85. public static object BooleanToObject(bool value) {
  86. return value ? True : False;
  87. }
  88. public static string CharToString(char ch) {
  89. if (ch < 255) return chars[ch];
  90. return new string(ch, 1);
  91. }
  92. public static ArgumentTypeException SimpleTypeError(string message) {
  93. return new ArgumentTypeException(message);
  94. }
  95. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] // TODO: fix
  96. public static Exception CannotConvertError(Type toType, object value) {
  97. return SimpleTypeError(String.Format("Cannot convert {0}({1}) to {2}", CompilerHelpers.GetType(value).Name, value, toType.Name));
  98. }
  99. public static Exception SimpleAttributeError(string message) {
  100. //TODO: localize
  101. return new MissingMemberException(message);
  102. }
  103. public static void ThrowUnboundLocalError(SymbolId name) {
  104. throw Error.ReferencedBeforeAssignment(SymbolTable.IdToString(name));
  105. }
  106. public static object ReadOnlyAssignError(bool field, string fieldName) {
  107. if (field) {
  108. throw Error.FieldReadonly(fieldName);
  109. } else {
  110. throw Error.PropertyReadonly(fieldName);
  111. }
  112. }
  113. public static DynamicStackFrame[] GetDynamicStackFrames(Exception e) {
  114. return GetDynamicStackFrames(e, true);
  115. }
  116. public static DynamicStackFrame[] GetDynamicStackFrames(Exception e, bool filter) {
  117. List<DynamicStackFrame> frames = e.Data[typeof(DynamicStackFrame)] as List<DynamicStackFrame>;
  118. if (frames == null) {
  119. // we may have missed a dynamic catch, and our host is looking
  120. // for the exception...
  121. frames = ExceptionHelpers.AssociateDynamicStackFrames(e);
  122. ExceptionHelpers.DynamicStackFrames = null;
  123. }
  124. if (frames == null) {
  125. return new DynamicStackFrame[0];
  126. }
  127. if (!filter) return frames.ToArray();
  128. #if !SILVERLIGHT
  129. frames = new List<DynamicStackFrame>(frames);
  130. List<DynamicStackFrame> res = new List<DynamicStackFrame>();
  131. // the list of _stackFrames we build up in ScriptingRuntimeHelpers can have
  132. // too many frames if exceptions are thrown from script code and
  133. // caught outside w/o calling GetDynamicStackFrames. Therefore we
  134. // filter down to only script frames which we know are associated
  135. // w/ the exception here.
  136. try {
  137. StackTrace outermostTrace = new StackTrace(e);
  138. IList<StackTrace> otherTraces = ExceptionHelpers.GetExceptionStackTraces(e) ?? new List<StackTrace>();
  139. List<StackFrame> clrFrames = new List<StackFrame>();
  140. foreach (StackTrace trace in otherTraces) {
  141. clrFrames.AddRange(trace.GetFrames() ?? new StackFrame[0]); // rare, sometimes GetFrames returns null
  142. }
  143. clrFrames.AddRange(outermostTrace.GetFrames() ?? new StackFrame[0]); // rare, sometimes GetFrames returns null
  144. int lastFound = 0;
  145. foreach (StackFrame clrFrame in clrFrames) {
  146. MethodBase method = clrFrame.GetMethod();
  147. for (int j = lastFound; j < frames.Count; j++) {
  148. MethodBase other = frames[j].GetMethod();
  149. // method info's don't always compare equal, check based
  150. // upon name/module/declaring type which will always be a correct
  151. // check for dynamic methods.
  152. if (method.Module == other.Module &&
  153. method.DeclaringType == other.DeclaringType &&
  154. method.Name == other.Name) {
  155. res.Add(frames[j]);
  156. frames.RemoveAt(j);
  157. lastFound = j;
  158. break;
  159. }
  160. }
  161. }
  162. } catch (MemberAccessException) {
  163. // can't access new StackTrace(e) due to security
  164. }
  165. return res.ToArray();
  166. #else
  167. return frames.ToArray();
  168. #endif
  169. }
  170. /// <summary>
  171. /// Helper method to create an instance. Work around for Silverlight where Activator.CreateInstance
  172. /// is SecuritySafeCritical.
  173. ///
  174. /// TODO: Why can't we just emit the right thing for default(T)?
  175. /// It's always null for reference types and it's well defined for value types
  176. /// </summary>
  177. public static T CreateInstance<T>() {
  178. return default(T);
  179. }
  180. // TODO: can't we just emit a new array?
  181. public static T[] CreateArray<T>(int args) {
  182. return new T[args];
  183. }
  184. /// <summary>
  185. /// EventInfo.EventHandlerType getter is marked SecuritySafeCritical in CoreCLR
  186. /// This method is to get to the property without using Reflection
  187. /// </summary>
  188. /// <param name="eventInfo"></param>
  189. /// <returns></returns>
  190. public static Type GetEventHandlerType(EventInfo eventInfo) {
  191. ContractUtils.RequiresNotNull(eventInfo, "eventInfo");
  192. return eventInfo.EventHandlerType;
  193. }
  194. public static IList<string> GetStringMembers(IList<object> members) {
  195. List<string> res = new List<string>();
  196. foreach (object o in members) {
  197. string str = o as string;
  198. if (str != null) {
  199. res.Add(str);
  200. }
  201. }
  202. return res;
  203. }
  204. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] // TODO: fix
  205. public static void SetEvent(EventTracker eventTracker, object value) {
  206. EventTracker et = value as EventTracker;
  207. if (et != null) {
  208. if (et != eventTracker) {
  209. throw Error.UnexpectedEvent(eventTracker.DeclaringType.Name,
  210. eventTracker.Name,
  211. et.DeclaringType.Name,
  212. et.Name);
  213. }
  214. return;
  215. }
  216. BoundMemberTracker bmt = value as BoundMemberTracker;
  217. if (bmt == null) {
  218. throw Error.ExpectedBoundEvent(CompilerHelpers.GetType(value).Name);
  219. }
  220. if (bmt.BoundTo.MemberType != TrackerTypes.Event) throw Error.ExpectedBoundEvent(bmt.BoundTo.MemberType.ToString());
  221. if (bmt.BoundTo != eventTracker) throw Error.UnexpectedEvent(
  222. eventTracker.DeclaringType.Name,
  223. eventTracker.Name,
  224. bmt.BoundTo.DeclaringType.Name,
  225. bmt.BoundTo.Name);
  226. }
  227. // TODO: just emit this in the generated code
  228. public static bool CheckDictionaryMembers(IDictionary dict, string[] names) {
  229. if (dict.Count != names.Length) return false;
  230. foreach (string name in names) {
  231. if (!dict.Contains(name)) {
  232. return false;
  233. }
  234. }
  235. return true;
  236. }
  237. // TODO: just emit this in the generated code
  238. public static T IncorrectBoxType<T>(object received) {
  239. throw Error.UnexpectedType("StrongBox<" + typeof(T).Name + ">", CompilerHelpers.GetType(received).Name);
  240. }
  241. public static void InitializeSymbols(Type t) {
  242. foreach (FieldInfo fi in t.GetFields()) {
  243. if (fi.FieldType == typeof(SymbolId)) {
  244. Debug.Assert(((SymbolId)fi.GetValue(null)) == SymbolId.Empty);
  245. fi.SetValue(null, SymbolTable.StringToId(fi.Name));
  246. }
  247. }
  248. }
  249. }
  250. }