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

/Runtime/Microsoft.Scripting/Runtime/ScopeStorage.cs

http://github.com/IronLanguages/main
C# | 562 lines | 359 code | 69 blank | 134 comment | 39 complexity | e8dcee0e481a6cf54c6690a6d09f433e MD5 | raw file
Possible License(s): CPL-1.0, BSD-3-Clause, ISC, GPL-2.0, MPL-2.0-no-copyleft-exception
  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) Microsoft Corporation.
  4. *
  5. * This source code is subject to terms and conditions of the Apache License, Version 2.0. 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 Apache License, Version 2.0, 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 Apache License, Version 2.0.
  10. *
  11. * You must not remove this notice, or any other, from this software.
  12. *
  13. *
  14. * ***************************************************************************/
  15. #if FEATURE_CORE_DLR
  16. using System.Linq.Expressions;
  17. #else
  18. using Microsoft.Scripting.Ast;
  19. using dynamic = System.Object;
  20. #endif
  21. using System;
  22. using System.Collections.Generic;
  23. using System.Threading;
  24. using System.Dynamic;
  25. using System.Reflection;
  26. using Microsoft.Scripting.Utils;
  27. namespace Microsoft.Scripting {
  28. /// <summary>
  29. /// Provides optimized and cacheable support for scope storage.
  30. ///
  31. /// This is the default object used for storing values in a scope.
  32. ///
  33. /// </summary>
  34. /// <remarks>
  35. /// The implementation uses a case-insensitive dictionary which holds
  36. /// onto ScopeVariableIgnoreCase objects. The SVIC's hold onto ScopeVariable
  37. /// objects for each possible casing.
  38. /// </remarks>
  39. public sealed class ScopeStorage : IDynamicMetaObjectProvider {
  40. private readonly Dictionary<string, ScopeVariableIgnoreCase> _storage = new Dictionary<string, ScopeVariableIgnoreCase>(StringComparer.OrdinalIgnoreCase);
  41. /// <summary>
  42. /// Gets the named value from the scope optionally ignoring case.
  43. ///
  44. /// If the named value is not present an InvalidOperationException is raised.
  45. /// </summary>
  46. public dynamic GetValue(string name, bool ignoreCase) {
  47. object res;
  48. if (GetScopeVariable(name, ignoreCase).TryGetValue(out res)) {
  49. return res;
  50. }
  51. throw new KeyNotFoundException("no value");
  52. }
  53. /// <summary>
  54. /// Attempts to get the named value from the scope optionally ignoring the case.
  55. ///
  56. /// Returns true if the value is present, false if it is not.
  57. /// </summary>
  58. public bool TryGetValue(string name, bool ignoreCase, out dynamic value) {
  59. if (HasVariable(name)) {
  60. object objValue;
  61. if (GetScopeVariable(name, ignoreCase).TryGetValue(out objValue)) {
  62. value = objValue;
  63. return true;
  64. }
  65. }
  66. value = null;
  67. return false;
  68. }
  69. /// <summary>
  70. /// Sets the named value in the scope optionally ignoring the case.
  71. /// </summary>
  72. public void SetValue(string name, bool ignoreCase, object value) {
  73. GetScopeVariable(name, ignoreCase).SetValue(value);
  74. }
  75. /// <summary>
  76. /// Deletes the named value from the scope optionally ignoring the case.
  77. /// </summary>
  78. public bool DeleteValue(string name, bool ignoreCase) {
  79. if (!HasVariable(name)) {
  80. return false;
  81. }
  82. return GetScopeVariable(name, ignoreCase).DeleteValue();
  83. }
  84. /// <summary>
  85. /// Checks if the named value is present in the scope optionally ignoring the case.
  86. /// </summary>
  87. public bool HasValue(string name, bool ignoreCase) {
  88. if (!HasVariable(name)) {
  89. return false;
  90. }
  91. return GetScopeVariable(name, ignoreCase).HasValue;
  92. }
  93. /// <summary>
  94. /// Gets the IScopeVariable for the scope optionally ignoring case.
  95. ///
  96. /// The IScopeVariable can be held onto and get/set/deleted without performing
  97. /// a dictionary lookup on subsequent accesses.
  98. /// </summary>
  99. public IScopeVariable GetScopeVariable(string name, bool ignoreCase) {
  100. if (ignoreCase) {
  101. return GetScopeVariableIgnoreCase(name);
  102. }
  103. return GetScopeVariable(name);
  104. }
  105. /// <summary>
  106. /// Gets the ScopeVariable for the scope in a case-sensitive manner.
  107. ///
  108. /// The ScopeVariable can be held onto and get/set/deleted without performing
  109. /// a dictionary lookup on subsequent accesses.
  110. /// </summary>
  111. public ScopeVariable GetScopeVariable(string name) {
  112. return GetScopeVariableIgnoreCase(name).GetCaseSensitiveStorage(name);
  113. }
  114. /// <summary>
  115. /// Gets the ScopeVariableIgnoreCase for the scope in a case-insensitive manner.
  116. ///
  117. /// The ScopeVariable can be held onto and get/set/deleted without performing
  118. /// a dictionary lookup on subsequent accesses.
  119. /// </summary>
  120. public ScopeVariableIgnoreCase GetScopeVariableIgnoreCase(string name) {
  121. ScopeVariableIgnoreCase storageInfo;
  122. lock (_storage) {
  123. if (!_storage.TryGetValue(name, out storageInfo)) {
  124. _storage[name] = storageInfo = new ScopeVariableIgnoreCase(name);
  125. }
  126. return storageInfo;
  127. }
  128. }
  129. /// <summary>
  130. /// Provides convenient case-sensitive value access.
  131. /// </summary>
  132. public dynamic this[string index] {
  133. get {
  134. return GetValue(index, false);
  135. }
  136. set {
  137. SetValue(index, false, (object)value);
  138. }
  139. }
  140. /// <summary>
  141. /// Returns all of the member names which currently have values in the scope.
  142. ///
  143. /// The list contains all available casings.
  144. /// </summary>
  145. public IList<string> GetMemberNames() {
  146. List<string> res = new List<string>();
  147. lock (_storage) {
  148. foreach (var storage in _storage.Values) {
  149. storage.AddNames(res);
  150. }
  151. }
  152. return res;
  153. }
  154. /// <summary>
  155. /// Returns all of the member names and their associated values from the scope.
  156. ///
  157. /// The list contains all available casings.
  158. /// </summary>
  159. public IList<KeyValuePair<string, dynamic>> GetItems() {
  160. List<KeyValuePair<string, dynamic>> res = new List<KeyValuePair<string, dynamic>>();
  161. lock (_storage) {
  162. foreach (var storage in _storage.Values) {
  163. storage.AddItems(res);
  164. }
  165. }
  166. return res;
  167. }
  168. private bool HasVariable(string name) {
  169. lock (_storage) {
  170. return _storage.ContainsKey(name);
  171. }
  172. }
  173. #region IDynamicMetaObjectProvider Members
  174. public DynamicMetaObject GetMetaObject(Expression parameter) {
  175. return new Meta(parameter, this);
  176. }
  177. class Meta : DynamicMetaObject {
  178. public Meta(Expression parameter, ScopeStorage storage)
  179. : base(parameter, BindingRestrictions.Empty, storage) {
  180. }
  181. public override DynamicMetaObject BindGetMember(GetMemberBinder binder) {
  182. return DynamicTryGetValue(binder.Name, binder.IgnoreCase,
  183. binder.FallbackGetMember(this).Expression,
  184. (tmp) => tmp
  185. );
  186. }
  187. public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args) {
  188. return DynamicTryGetValue(binder.Name, binder.IgnoreCase,
  189. binder.FallbackInvokeMember(this, args).Expression,
  190. (tmp) => binder.FallbackInvoke(new DynamicMetaObject(tmp, BindingRestrictions.Empty), args, null).Expression
  191. );
  192. }
  193. private DynamicMetaObject DynamicTryGetValue(string name, bool ignoreCase, Expression fallback, Func<Expression, Expression> resultOp) {
  194. IScopeVariable variable = Value.GetScopeVariable(name, ignoreCase);
  195. var tmp = Expression.Parameter(typeof(object));
  196. return new DynamicMetaObject(
  197. Expression.Block(
  198. new[] { tmp },
  199. Expression.Condition(
  200. Expression.Call(
  201. Variable(variable),
  202. variable.GetType().GetMethod("TryGetValue"),
  203. tmp
  204. ),
  205. ExpressionUtils.Convert(resultOp(tmp), typeof(object)),
  206. ExpressionUtils.Convert(fallback, typeof(object))
  207. )
  208. ),
  209. BindingRestrictions.GetInstanceRestriction(Expression, Value)
  210. );
  211. }
  212. private static Expression Variable(IScopeVariable variable) {
  213. return Expression.Convert(
  214. Expression.Property(
  215. Expression.Constant(((IWeakReferencable)variable).WeakReference),
  216. typeof(WeakReference).GetProperty("Target")
  217. ),
  218. variable.GetType()
  219. );
  220. }
  221. public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value) {
  222. IScopeVariable variable = Value.GetScopeVariable(binder.Name, binder.IgnoreCase);
  223. var objExpression = ExpressionUtils.Convert(value.Expression, typeof(object));
  224. return new DynamicMetaObject(
  225. Expression.Block(
  226. Expression.Call(
  227. Variable(variable),
  228. variable.GetType().GetMethod("SetValue"),
  229. objExpression
  230. ),
  231. objExpression
  232. ),
  233. BindingRestrictions.GetInstanceRestriction(Expression, Value)
  234. );
  235. }
  236. public override DynamicMetaObject BindDeleteMember(DeleteMemberBinder binder) {
  237. IScopeVariable variable = Value.GetScopeVariable(binder.Name, binder.IgnoreCase);
  238. return new DynamicMetaObject(
  239. Expression.Condition(
  240. Expression.Call(
  241. Variable(variable),
  242. variable.GetType().GetMethod("DeleteValue")
  243. ),
  244. Expression.Default(binder.ReturnType),
  245. binder.FallbackDeleteMember(this).Expression
  246. ),
  247. BindingRestrictions.GetInstanceRestriction(Expression, Value)
  248. );
  249. }
  250. public override IEnumerable<string> GetDynamicMemberNames() {
  251. return Value.GetMemberNames();
  252. }
  253. public new ScopeStorage Value {
  254. get {
  255. return (ScopeStorage)base.Value;
  256. }
  257. }
  258. }
  259. #endregion
  260. }
  261. /// <summary>
  262. /// Provides a common interface for accessing both case sensitive and
  263. /// case insensitive variable storage.
  264. /// </summary>
  265. public interface IScopeVariable {
  266. /// <summary>
  267. /// True if the scope has a value, false if it does not.
  268. /// </summary>
  269. bool HasValue {
  270. get;
  271. }
  272. /// <summary>
  273. /// Atempts to get the value. If a value is assigned it returns true otherwise
  274. /// it returns false.
  275. /// </summary>
  276. bool TryGetValue(out dynamic value);
  277. /// <summary>
  278. /// Sets the current value in the scope.
  279. /// </summary>
  280. void SetValue(object value);
  281. /// <summary>
  282. /// Removes the current value from the scope.
  283. /// </summary>
  284. bool DeleteValue();
  285. }
  286. internal interface IWeakReferencable {
  287. WeakReference WeakReference {
  288. get;
  289. }
  290. }
  291. /// <summary>
  292. /// Boxes the value for storage in a scope. Languages or consumers of the scope
  293. /// can save this value and use it to get/set the current value in the scope for
  294. /// commonly accessed values.
  295. ///
  296. /// ScopeVariables are case sensitive and will only refer to a single value.
  297. /// </summary>
  298. public sealed class ScopeVariable : IScopeVariable, IWeakReferencable {
  299. private object _value;
  300. private WeakReference _weakref;
  301. private static readonly object _novalue = new object();
  302. internal ScopeVariable() {
  303. _value = _novalue;
  304. }
  305. #region Public APIs
  306. /// <summary>
  307. /// True if the scope has a value, false if it does not.
  308. /// </summary>
  309. public bool HasValue {
  310. get {
  311. return _value != _novalue;
  312. }
  313. }
  314. /// <summary>
  315. /// Atempts to get the value. If a value is assigned it returns true otherwise
  316. /// it returns false.
  317. /// </summary>
  318. public bool TryGetValue(out dynamic value) {
  319. value = _value;
  320. if ((object)value != _novalue) {
  321. return true;
  322. }
  323. value = null;
  324. return false;
  325. }
  326. /// <summary>
  327. /// Sets the current value in the scope.
  328. /// </summary>
  329. public void SetValue(object value) {
  330. _value = value;
  331. }
  332. /// <summary>
  333. /// Removes the current value from the scope.
  334. /// </summary>
  335. public bool DeleteValue() {
  336. return Interlocked.Exchange(ref _value, _novalue) != _novalue;
  337. }
  338. #endregion
  339. #region IWeakReferencable Members
  340. public WeakReference WeakReference {
  341. get {
  342. if (_weakref == null) {
  343. _weakref = new WeakReference(this);
  344. }
  345. return _weakref;
  346. }
  347. }
  348. #endregion
  349. }
  350. /// <summary>
  351. /// Boxes the value for storage in a scope. Languages or consumers of the scope
  352. /// can save this value and use it to get/set the current value in the scope for
  353. /// commonly accessed values.
  354. ///
  355. /// ScopeVariablesIgnoreCase are case insensitive and may access different casings
  356. /// depending on how other gets/sets occur in the scope.
  357. /// </summary>
  358. public sealed class ScopeVariableIgnoreCase : IScopeVariable, IWeakReferencable {
  359. private readonly string _firstCasing;
  360. private readonly ScopeVariable _firstVariable;
  361. private WeakReference _weakref;
  362. private Dictionary<string, ScopeVariable> _overflow;
  363. internal ScopeVariableIgnoreCase(string casing) {
  364. _firstCasing = casing;
  365. _firstVariable = new ScopeVariable();
  366. }
  367. #region Public APIs
  368. /// <summary>
  369. /// True if the scope has a value, false if it does not.
  370. /// </summary>
  371. public bool HasValue {
  372. get {
  373. if (_firstVariable.HasValue) {
  374. return true;
  375. }
  376. if (_overflow != null) {
  377. lock (_overflow) {
  378. foreach (var entry in _overflow) {
  379. if (entry.Value.HasValue) {
  380. return true;
  381. }
  382. }
  383. }
  384. }
  385. return false;
  386. }
  387. }
  388. /// <summary>
  389. /// Atempts to get the value. If a value is assigned it returns true otherwise
  390. /// it returns false.
  391. /// </summary>
  392. public bool TryGetValue(out dynamic value) {
  393. object objValue;
  394. if (_firstVariable.TryGetValue(out objValue)) {
  395. value = objValue;
  396. return true;
  397. }
  398. if (_overflow != null) {
  399. lock (_overflow) {
  400. foreach (var entry in _overflow) {
  401. if (entry.Value.TryGetValue(out objValue)) {
  402. value = objValue;
  403. return true;
  404. }
  405. }
  406. }
  407. }
  408. value = null;
  409. return false;
  410. }
  411. /// <summary>
  412. /// Sets the current value in the scope.
  413. /// </summary>
  414. public void SetValue(object value) {
  415. _firstVariable.SetValue(value);
  416. }
  417. /// <summary>
  418. /// Removes the current value from the scope.
  419. /// </summary>
  420. public bool DeleteValue() {
  421. bool res = _firstVariable.DeleteValue();
  422. if (_overflow != null) {
  423. lock (_overflow) {
  424. foreach (var entry in _overflow) {
  425. res = entry.Value.DeleteValue() || res;
  426. }
  427. }
  428. }
  429. return res;
  430. }
  431. #endregion
  432. #region Implementation Details
  433. internal ScopeVariable GetCaseSensitiveStorage(string name) {
  434. if (name == _firstCasing) {
  435. // common case, only 1 casing available
  436. return _firstVariable;
  437. }
  438. return GetStorageSlow(name);
  439. }
  440. internal void AddNames(List<string> list) {
  441. if (_firstVariable.HasValue) {
  442. list.Add(_firstCasing);
  443. }
  444. if (_overflow != null) {
  445. lock (_overflow) {
  446. foreach (var element in _overflow) {
  447. if (element.Value.HasValue) {
  448. list.Add(element.Key);
  449. }
  450. }
  451. }
  452. }
  453. }
  454. internal void AddItems(List<KeyValuePair<string, object>> list) {
  455. object value;
  456. if (_firstVariable.TryGetValue(out value)) {
  457. list.Add(new KeyValuePair<string, object>(_firstCasing, value));
  458. }
  459. if (_overflow != null) {
  460. lock (_overflow) {
  461. foreach (var element in _overflow) {
  462. if (element.Value.TryGetValue(out value)) {
  463. list.Add(new KeyValuePair<string, object>(element.Key, value));
  464. }
  465. }
  466. }
  467. }
  468. }
  469. private ScopeVariable GetStorageSlow(string name) {
  470. if (_overflow == null) {
  471. Interlocked.CompareExchange(ref _overflow, new Dictionary<string, ScopeVariable>(), null);
  472. }
  473. lock (_overflow) {
  474. ScopeVariable res;
  475. if (!_overflow.TryGetValue(name, out res)) {
  476. _overflow[name] = res = new ScopeVariable();
  477. }
  478. return res;
  479. }
  480. }
  481. #endregion
  482. #region IWeakReferencable Members
  483. public WeakReference WeakReference {
  484. get {
  485. if (_weakref == null) {
  486. _weakref = new WeakReference(this);
  487. }
  488. return _weakref;
  489. }
  490. }
  491. #endregion
  492. }
  493. }