PageRenderTime 55ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 0ms

/Microsoft.Scripting/Hosting/ScriptScope.cs

https://bitbucket.org/stefanrusek/xronos
C# | 349 lines | 218 code | 36 blank | 95 comment | 4 complexity | 960ae5eeab0437e87ba7d1d0e5d3d8c9 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.Collections.ObjectModel;
  22. using System.Diagnostics;
  23. #if CODEPLEX_40
  24. using System.Linq.Expressions;
  25. #else
  26. using Microsoft.Linq.Expressions;
  27. #endif
  28. using System.Runtime.Remoting;
  29. using System.Runtime.Serialization;
  30. #if CODEPLEX_40
  31. using System.Dynamic;
  32. #else
  33. using Microsoft.Scripting;
  34. #endif
  35. using Microsoft.Scripting.Runtime;
  36. using Microsoft.Scripting.Utils;
  37. using AstUtils = Microsoft.Scripting.Ast.Utils;
  38. namespace Microsoft.Scripting.Hosting {
  39. /// <summary>
  40. /// A ScriptScope is a unit of execution for code. It consists of a global Scope which
  41. /// all code executes in. A ScriptScope can have an arbitrary initializer and arbitrary
  42. /// reloader.
  43. ///
  44. /// ScriptScope is not thread safe. Host should either lock when multiple threads could
  45. /// access the same module or should make a copy for each thread.
  46. ///
  47. /// Hosting API counterpart for <see cref="Scope"/>.
  48. /// </summary>
  49. #if SILVERLIGHT
  50. public sealed class ScriptScope : IDynamicMetaObjectProvider {
  51. #else
  52. [DebuggerTypeProxy(typeof(ScriptScope.DebugView))]
  53. public sealed class ScriptScope : MarshalByRefObject, IDynamicMetaObjectProvider {
  54. #endif
  55. private readonly Scope _scope;
  56. private readonly ScriptEngine _engine;
  57. internal ScriptScope(ScriptEngine engine, Scope scope) {
  58. Assert.NotNull(engine);
  59. Assert.NotNull(scope);
  60. _scope = scope;
  61. _engine = engine;
  62. }
  63. internal Scope Scope {
  64. get { return _scope; }
  65. }
  66. /// <summary>
  67. /// Gets an engine for the language associated with this scope.
  68. /// Returns invariant engine if the scope is language agnostic.
  69. /// </summary>
  70. public ScriptEngine Engine {
  71. get {
  72. // InvariantContext should not have an engine
  73. // TODO: If _engine itself could be set to null, we wouldn't
  74. // need this check
  75. if (_engine.LanguageContext is InvariantContext) {
  76. return null;
  77. }
  78. return _engine;
  79. }
  80. }
  81. /// <summary>
  82. /// Gets a value stored in the scope under the given name.
  83. /// </summary>
  84. /// <exception cref="MissingMemberException">The specified name is not defined in the scope.</exception>
  85. /// <exception cref="ArgumentNullException"><paramref name="name"/> is a <c>null</c> reference.</exception>
  86. public object GetVariable(string name) {
  87. return _scope.LookupName(_engine.LanguageContext, SymbolTable.StringToId(name));
  88. }
  89. /// <summary>
  90. /// Gets a value stored in the scope under the given name.
  91. /// Converts the result to the specified type using the conversion that the language associated with the scope defines.
  92. /// If no language is associated with the scope, the default CLR conversion is attempted.
  93. /// </summary>
  94. /// <exception cref="MissingMemberException">The specified name is not defined in the scope.</exception>
  95. /// <exception cref="ArgumentNullException"><paramref name="name"/> is a <c>null</c> reference.</exception>
  96. public T GetVariable<T>(string name) {
  97. return _engine.Operations.ConvertTo<T>(_engine.GetVariable(this, name));
  98. }
  99. /// <summary>
  100. /// Tries to get a value stored in the scope under the given name.
  101. /// </summary>
  102. /// <exception cref="ArgumentNullException"><paramref name="name"/> is a <c>null</c> reference.</exception>
  103. public bool TryGetVariable(string name, out object value) {
  104. return _scope.TryGetName(SymbolTable.StringToId(name), out value);
  105. }
  106. /// <summary>
  107. /// Tries to get a value stored in the scope under the given name.
  108. /// Converts the result to the specified type using the conversion that the language associated with the scope defines.
  109. /// If no language is associated with the scope, the default CLR conversion is attempted.
  110. /// </summary>
  111. /// <exception cref="ArgumentNullException"><paramref name="name"/> is a <c>null</c> reference.</exception>
  112. public bool TryGetVariable<T>(string name, out T value) {
  113. object result;
  114. if (_scope.TryGetName(SymbolTable.StringToId(name), out result)) {
  115. value = _engine.Operations.ConvertTo<T>(result);
  116. return true;
  117. }
  118. value = default(T);
  119. return false;
  120. }
  121. /// <summary>
  122. /// Sets the name to the specified value.
  123. /// </summary>
  124. /// <exception cref="ArgumentNullException"><paramref name="name"/> is a <c>null</c> reference.</exception>
  125. public void SetVariable(string name, object value) {
  126. _scope.SetName(SymbolTable.StringToId(name), value);
  127. }
  128. #if !SILVERLIGHT
  129. /// <summary>
  130. /// Gets a handle for a value stored in the scope under the given name.
  131. /// </summary>
  132. /// <exception cref="MissingMemberException">The specified name is not defined in the scope.</exception>
  133. /// <exception cref="ArgumentNullException"><paramref name="name"/> is a <c>null</c> reference.</exception>
  134. public ObjectHandle GetVariableHandle(string name) {
  135. return new ObjectHandle(GetVariable(name));
  136. }
  137. /// <summary>
  138. /// Tries to get a handle for a value stored in the scope under the given name.
  139. /// Returns <c>true</c> if there is such name, <c>false</c> otherwise.
  140. /// </summary>
  141. /// <exception cref="ArgumentNullException"><paramref name="name"/> is a <c>null</c> reference.</exception>
  142. public bool TryGetVariableHandle(string name, out ObjectHandle handle) {
  143. object value;
  144. if (TryGetVariable(name, out value)) {
  145. handle = new ObjectHandle(value);
  146. return true;
  147. } else {
  148. handle = null;
  149. return false;
  150. }
  151. }
  152. /// <summary>
  153. /// Sets the name to the specified value.
  154. /// </summary>
  155. /// <exception cref="SerializationException">
  156. /// The value held by the handle isn't from the scope's app-domain and isn't serializable or MarshalByRefObject.
  157. /// </exception>
  158. /// <exception cref="ArgumentNullException"><paramref name="name"/> or <paramref name="handle"/> is a <c>null</c> reference.</exception>
  159. public void SetVariable(string name, ObjectHandle handle) {
  160. ContractUtils.RequiresNotNull(handle, "handle");
  161. SetVariable(name, handle.Unwrap());
  162. }
  163. #endif
  164. /// <summary>
  165. /// Determines if this context or any outer scope contains the defined name.
  166. /// </summary>
  167. /// <exception cref="ArgumentNullException"><paramref name="name"/> is a <c>null</c> reference.</exception>
  168. public bool ContainsVariable(string name) {
  169. return _scope.ContainsName(SymbolTable.StringToId(name));
  170. }
  171. /// <summary>
  172. /// Removes the variable of the given name from this scope.
  173. /// </summary>
  174. /// <returns><c>true</c> if the value existed in the scope before it has been removed.</returns>
  175. /// <exception cref="ArgumentNullException"><paramref name="name"/> is a <c>null</c> reference.</exception>
  176. public bool RemoveVariable(string name) {
  177. return _scope.TryRemoveName(SymbolTable.StringToId(name));
  178. }
  179. /// <summary>
  180. /// Gets a list of variable names stored in the scope.
  181. /// </summary>
  182. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
  183. public IEnumerable<string> GetVariableNames() {
  184. // Remoting: we eagerly enumerate all variables to avoid cross domain calls for each item.
  185. var result = new List<string>();
  186. foreach (var entry in _scope.Items) {
  187. result.Add(SymbolTable.IdToString(entry.Key));
  188. }
  189. result.TrimExcess();
  190. return result;
  191. }
  192. /// <summary>
  193. /// Gets an array of variable names and their values stored in the scope.
  194. /// </summary>
  195. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
  196. public IEnumerable<KeyValuePair<string, object>> GetItems() {
  197. // Remoting: we eagerly enumerate all variables to avoid cross domain calls for each item.
  198. var result = new List<KeyValuePair<string, object>>();
  199. foreach (var entry in _scope.Items) {
  200. result.Add(new KeyValuePair<string, object>(SymbolTable.IdToString(entry.Key), entry.Value));
  201. }
  202. result.TrimExcess();
  203. return result;
  204. }
  205. #region DebugView
  206. #if !SILVERLIGHT
  207. internal sealed class DebugView {
  208. private readonly ScriptScope _scope;
  209. public DebugView(ScriptScope scope) {
  210. Assert.NotNull(scope);
  211. _scope = scope;
  212. }
  213. public ScriptEngine Language {
  214. get { return _scope._engine; }
  215. }
  216. public System.Collections.Hashtable Variables {
  217. get {
  218. System.Collections.Hashtable result = new System.Collections.Hashtable();
  219. foreach (KeyValuePair<string, object> variable in _scope.GetItems()) {
  220. result[variable.Key] = variable.Value;
  221. }
  222. return result;
  223. }
  224. }
  225. }
  226. #endif
  227. #endregion
  228. #region IDynamicMetaObjectProvider implementation
  229. DynamicMetaObject IDynamicMetaObjectProvider.GetMetaObject(Expression parameter) {
  230. return new Meta(parameter, this);
  231. }
  232. private sealed class Meta : DynamicMetaObject {
  233. internal Meta(Expression parameter, ScriptScope scope)
  234. : base(parameter, BindingRestrictions.Empty, scope) {
  235. }
  236. // TODO: support for IgnoreCase in underlying ScriptScope APIs
  237. public override DynamicMetaObject BindGetMember(GetMemberBinder action) {
  238. var result = Expression.Variable(typeof(object), "result");
  239. var fallback = action.FallbackGetMember(this);
  240. return new DynamicMetaObject(
  241. Expression.Block(
  242. new ParameterExpression[] { result },
  243. Expression.Condition(
  244. Expression.Call(
  245. AstUtils.Convert(Expression, typeof(ScriptScope)),
  246. typeof(ScriptScope).GetMethod("TryGetVariable", new[] { typeof(string), typeof(object).MakeByRefType() }),
  247. AstUtils.Constant(action.Name),
  248. result
  249. ),
  250. result,
  251. AstUtils.Convert(fallback.Expression, typeof(object))
  252. )
  253. ),
  254. BindingRestrictionsHelpers.GetRuntimeTypeRestriction(Expression, typeof(ScriptScope)).Merge(fallback.Restrictions)
  255. );
  256. }
  257. // TODO: support for IgnoreCase in underlying ScriptScope APIs
  258. public override DynamicMetaObject BindSetMember(SetMemberBinder action, DynamicMetaObject value) {
  259. return new DynamicMetaObject(
  260. Expression.Call(
  261. AstUtils.Convert(Expression, typeof(ScriptScope)),
  262. typeof(ScriptScope).GetMethod("SetVariable", new[] { typeof(string), typeof(object) }),
  263. AstUtils.Constant(action.Name),
  264. AstUtils.Convert(value.Expression, typeof(object))
  265. ),
  266. Restrictions.Merge(value.Restrictions).Merge(BindingRestrictionsHelpers.GetRuntimeTypeRestriction(Expression, typeof(ScriptScope)))
  267. );
  268. }
  269. // TODO: support for IgnoreCase in underlying ScriptScope APIs
  270. public override DynamicMetaObject BindDeleteMember(DeleteMemberBinder action) {
  271. var fallback = action.FallbackDeleteMember(this);
  272. return new DynamicMetaObject(
  273. Expression.IfThenElse(
  274. Expression.Call(
  275. AstUtils.Convert(Expression, typeof(ScriptScope)),
  276. typeof(ScriptScope).GetMethod("RemoveVariable"),
  277. AstUtils.Constant(action.Name)
  278. ),
  279. AstUtils.Empty(),
  280. fallback.Expression
  281. ),
  282. Restrictions.Merge(BindingRestrictionsHelpers.GetRuntimeTypeRestriction(Expression, typeof(ScriptScope))).Merge(fallback.Restrictions)
  283. );
  284. }
  285. // TODO: support for IgnoreCase in underlying ScriptScope APIs
  286. public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder action, DynamicMetaObject[] args) {
  287. var fallback = action.FallbackInvokeMember(this, args);
  288. var result = Expression.Variable(typeof(object), "result");
  289. var fallbackInvoke = action.FallbackInvoke(new DynamicMetaObject(result, BindingRestrictions.Empty), args, null);
  290. return new DynamicMetaObject(
  291. Expression.Block(
  292. new ParameterExpression[] { result },
  293. Expression.Condition(
  294. Expression.Call(
  295. AstUtils.Convert(Expression, typeof(ScriptScope)),
  296. typeof(ScriptScope).GetMethod("TryGetVariable", new[] { typeof(string), typeof(object).MakeByRefType() }),
  297. AstUtils.Constant(action.Name),
  298. result
  299. ),
  300. AstUtils.Convert(fallbackInvoke.Expression, typeof(object)),
  301. AstUtils.Convert(fallback.Expression, typeof(object))
  302. )
  303. ),
  304. BindingRestrictions.Combine(args).Merge(BindingRestrictionsHelpers.GetRuntimeTypeRestriction(Expression, typeof(ScriptScope))).Merge(fallback.Restrictions)
  305. );
  306. }
  307. public override IEnumerable<string> GetDynamicMemberNames() {
  308. return ((ScriptScope)Value).GetVariableNames();
  309. }
  310. }
  311. #endregion
  312. }
  313. }