PageRenderTime 35ms CodeModel.GetById 9ms RepoModel.GetById 1ms app.codeStats 0ms

/libs/Microsoft.Scripting/Runtime/Scope.cs

http://ironlua.googlecode.com/
C# | 984 lines | 609 code | 154 blank | 221 comment | 240 complexity | c66631c0aa7b4bd8acecc5c74e5a05d3 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. using System;
  16. using System.Collections.Generic;
  17. using System.Text;
  18. using System.Threading;
  19. using System.Diagnostics;
  20. using System.Reflection;
  21. using Microsoft.Scripting.Ast;
  22. using Microsoft.Scripting.Utils;
  23. using Microsoft.Scripting.Generation;
  24. using System.Runtime.CompilerServices;
  25. namespace Microsoft.Scripting.Runtime {
  26. /// <summary>
  27. /// Represents a context of execution. A context of execution has a set of variables
  28. /// associated with it (its dictionary) and a parent context.
  29. ///
  30. /// When looking up a name from a context first the local context is searched. If the
  31. /// name is not found there the name lookup will be done against the parent context.
  32. ///
  33. /// Scopes can have language-sensitive variables that are only exposed to a single
  34. /// language based upon its calling context. When searching the
  35. /// language-sensitive dictionary is searched first. If no matches are found the lookup
  36. /// is delegated back to the LanguageContext. If the LanguageContext fails to lookup
  37. /// the name it delegates back to the (Host or ScriptEnvironment?)
  38. ///
  39. /// Each member of the Scope can optionally have a certain set of attributes associated
  40. /// with it (ScopeMemberAttributes). These permit members of the scope to be read-only,
  41. /// non-deletable, or hidden from enumeration.
  42. ///
  43. /// Scopes, like IAttrbibuteCollections, support both being indexed by SymbolId for fast
  44. /// access as well as being indexed by object. The preferred access is via SymbolId and
  45. /// object access is provided for languages which require additional semantics. All
  46. /// features supported for feature IDs are also supported for objects (e.g. context-sentsitivity
  47. /// and attributes) but the object API does not contain all the same sets of overloads provided
  48. /// for convenience.
  49. ///
  50. /// TODO: Thread safety
  51. /// </summary>
  52. public class Scope : IMembersList {
  53. private ScopeExtension[]/*!*/ _extensions; // resizable
  54. private readonly LanguageContext/*!*/ _language;
  55. private readonly Scope _parent;
  56. // TODO: obsolete
  57. private IAttributesCollection _dict;
  58. private ScopeAttributeDictionary _attrs;
  59. private ContextSensitiveScope _contextScopes;
  60. private IDictionary<Variable, object> _temps;
  61. private bool _isVisible;
  62. private SourceLocation _sourceLocation;
  63. public LanguageContext/*!*/ Language {
  64. get { return _language; }
  65. }
  66. /// <summary>
  67. /// Creates a new top-level scope with a new empty dictionary. The scope
  68. /// is marked as being visible.
  69. /// </summary>
  70. public Scope(LanguageContext/*!*/ language)
  71. : this(language, null, null) {
  72. }
  73. /// <summary>
  74. /// Creates a new top-level Scope with the provided dictionary
  75. /// </summary>
  76. public Scope(LanguageContext/*!*/ language, IAttributesCollection dictionary)
  77. : this(language, null, dictionary) {
  78. }
  79. /// <summary>
  80. /// Creates a new Scope with the provided parent and dictionary.
  81. /// </summary>
  82. public Scope(LanguageContext/*!*/ language, Scope parent, IAttributesCollection dictionary)
  83. : this(language, parent, dictionary, true) {
  84. }
  85. /// <summary>
  86. /// Creates a new Scope with the provided parent, dictionary and visibility.
  87. /// </summary>
  88. public Scope(LanguageContext/*!*/ language, Scope parent, IAttributesCollection dictionary, bool isVisible) {
  89. Contract.RequiresNotNull(language, "language");
  90. _parent = parent;
  91. _dict = dictionary ?? new SymbolDictionary();
  92. _isVisible = isVisible;
  93. _temps = null;
  94. _extensions = ScopeExtension.EmptyArray;
  95. _language = language;
  96. }
  97. #region TODO
  98. public ScopeExtension GetExtension(ContextId languageContextId) {
  99. return (languageContextId.Id < _extensions.Length) ? _extensions[languageContextId.Id] : null;
  100. }
  101. public ScopeExtension/*!*/ SetExtension(ContextId languageContextId, ScopeExtension/*!*/ extension) {
  102. Contract.RequiresNotNull(extension, "extension");
  103. if (languageContextId.Id >= _extensions.Length) {
  104. Array.Resize(ref _extensions, languageContextId.Id + 1);
  105. }
  106. ScopeExtension original = Interlocked.CompareExchange(ref _extensions[languageContextId.Id], extension, null);
  107. return original ?? extension;
  108. }
  109. #if OBSOLETE
  110. // TODO: workaround
  111. internal void Reloading() {
  112. for (int i = 0; i < _extensions.Length; i++) {
  113. if (_extensions[i] != null) {
  114. _extensions[i].ModuleReloading();
  115. }
  116. }
  117. }
  118. // TODO: workaround
  119. internal void Reloaded() {
  120. for (int i = 0; i < _extensions.Length; i++) {
  121. if (_extensions[i] != null) {
  122. _extensions[i].ModuleReloaded();
  123. }
  124. }
  125. }
  126. #endif
  127. #region TODO: used by Python builtins
  128. /// <summary>
  129. /// Event fired when a module changes.
  130. /// </summary>
  131. public event EventHandler<ModuleChangeEventArgs> ModuleChanged;
  132. /// <summary>
  133. /// Called by the base class to fire the module change event when the
  134. /// module has been modified.
  135. /// </summary>
  136. private void OnModuleChange(ModuleChangeEventArgs e) {
  137. EventHandler<ModuleChangeEventArgs> handler = ModuleChanged;
  138. if (handler != null) {
  139. handler(this, e);
  140. }
  141. }
  142. [SpecialName]
  143. public object GetCustomMember(CodeContext context, string name) {
  144. object value;
  145. if (TryGetName(context.LanguageContext, SymbolTable.StringToId(name), out value)) {
  146. if (value != Uninitialized.Instance) {
  147. return value;
  148. }
  149. }
  150. return OperationFailed.Value;
  151. }
  152. [SpecialName]
  153. public void SetMemberAfter(string name, object value) {
  154. OnModuleChange(new ModuleChangeEventArgs(SymbolTable.StringToId(name), ModuleChangeType.Set, value));
  155. SetName(SymbolTable.StringToId(name), value);
  156. }
  157. [SpecialName]
  158. public bool DeleteMember(CodeContext context, string name) {
  159. if (TryRemoveName(context.LanguageContext, SymbolTable.StringToId(name))) {
  160. OnModuleChange(new ModuleChangeEventArgs(SymbolTable.StringToId(name), ModuleChangeType.Delete));
  161. return true;
  162. }
  163. return false;
  164. }
  165. #endregion
  166. #endregion
  167. #region IMembersList
  168. public IList<object> GetMemberNames(CodeContext context) {
  169. List<object> ret;
  170. if (!context.ModuleContext.ShowCls) {
  171. ret = new List<object>();
  172. foreach (KeyValuePair<object, object> kvp in GetAllItems(context.LanguageContext)) {
  173. if (kvp.Value != Uninitialized.Instance) {
  174. if (kvp.Key is SymbolId) {
  175. ret.Add(SymbolTable.IdToString((SymbolId)kvp.Key));
  176. } else {
  177. ret.Add(kvp.Key);
  178. }
  179. }
  180. }
  181. } else {
  182. ret = new List<object>(GetAllKeys(context.LanguageContext));
  183. }
  184. return ret;
  185. }
  186. #endregion
  187. #region Obsolete (TODO)
  188. /// <summary>
  189. /// Gets the parent of this Scope or null if the Scope has no parent.
  190. /// </summary>
  191. public Scope Parent {
  192. get {
  193. return _parent;
  194. }
  195. }
  196. /// <summary>
  197. /// Gets if the context is visible at this scope. Visibility is a per-language feature that enables
  198. /// languages to include members in the Scope chain but hide them when directly exposed to the user.
  199. /// </summary>
  200. public bool IsVisible {
  201. get {
  202. return _isVisible;
  203. }
  204. }
  205. public SourceLocation SourceLocation {
  206. get {
  207. return _sourceLocation;
  208. }
  209. set {
  210. if (value != SourceLocation.Invalid && value != SourceLocation.None) {
  211. _sourceLocation = value;
  212. }
  213. }
  214. }
  215. /// <summary>
  216. /// Gets the current container for temporary variables. These might need to be nested in a manner
  217. /// different than the Scope objects, so separate functions exist for pushing and popping them relative
  218. /// to the current scope.
  219. /// </summary>
  220. internal IDictionary<Ast.Variable, object> TemporaryStorage {
  221. get {
  222. if (_temps == null) {
  223. _temps = new Dictionary<Variable, object>();
  224. }
  225. return _temps;
  226. }
  227. }
  228. /// <summary>
  229. /// Create a context for keeping track of allocated temporary variables inside of TemporaryStorage.
  230. /// When this function is called, the variables given by paramVars will be set to the values given by paramValues;
  231. /// when the returned object is disposed of, the supplied paramVars and tempVars variables will be removed from temporary storage.
  232. /// </summary>
  233. internal CodeContext GetTemporaryVariableContext(CodeContext context, Variable[] paramVars, object[] paramValues) {
  234. Debug.Assert(paramVars.Length == paramValues.Length);
  235. Scope scope = CloneForTemporaries();
  236. context = new CodeContext(scope, context.LanguageContext, context.ModuleContext);
  237. for (int i = 0; i < paramValues.Length; i++) {
  238. scope.TemporaryStorage[paramVars[i]] = paramValues[i];
  239. }
  240. return context;
  241. }
  242. private Scope CloneForTemporaries() {
  243. Scope s = new Scope(_language, _parent, _dict, _isVisible);
  244. s._attrs = _attrs;
  245. s._contextScopes = _contextScopes;
  246. s._extensions = _extensions;
  247. return s;
  248. }
  249. /// <summary>
  250. /// Returns the list of keys which are available to all languages. Keys marked with the
  251. /// DontEnumerate flag will not be returned.
  252. /// </summary>
  253. public IEnumerable<SymbolId> Keys {
  254. get {
  255. foreach (object name in _dict.Keys) {
  256. string strName = name as string;
  257. if (strName == null) continue;
  258. SymbolId si = SymbolTable.StringToId(strName);
  259. if (_attrs == null || _attrs.CheckEnumerable(si)) {
  260. yield return si;
  261. }
  262. }
  263. }
  264. }
  265. /// <summary>
  266. /// Returns the list of Keys and Items which are available to all langauges. Keys marked
  267. /// with the DontEnumerate flag will not be returned.
  268. /// </summary>
  269. public IEnumerable<KeyValuePair<SymbolId, object>> Items {
  270. get {
  271. foreach (KeyValuePair<SymbolId, object> kvp in _dict.SymbolAttributes) {
  272. if (_attrs == null || _attrs.CheckEnumerable(kvp.Key)) {
  273. yield return kvp;
  274. }
  275. }
  276. }
  277. }
  278. /// <summary>
  279. /// Returns the list of Keys available to all languages in addition to those keys
  280. /// which are only available to the provided LanguageContext.
  281. ///
  282. /// Keys marked with the DontEnumerate flag will not be returned.
  283. /// </summary>
  284. public IEnumerable<SymbolId> GetKeys(LanguageContext context) {
  285. foreach (SymbolId si in _dict.SymbolAttributes.Keys) {
  286. if (_attrs == null || _attrs.CheckEnumerable(si)) yield return si;
  287. }
  288. if (_contextScopes != null) {
  289. foreach (KeyValuePair<object, object> kvp in _contextScopes.GetItems(context)) {
  290. if (kvp.Key is SymbolId) {
  291. if (_dict.ContainsKey((SymbolId)kvp.Key)) continue;
  292. yield return (SymbolId)kvp.Key;
  293. }
  294. }
  295. }
  296. }
  297. /// <summary>
  298. /// Trys to lookup the provided name in the current scope.
  299. /// </summary>
  300. public bool TryGetName(SymbolId name, out object value) {
  301. return TryGetName(_language, name, out value);
  302. }
  303. /// <summary>
  304. /// Trys to lookup the provided name in the current scope. Search includes
  305. /// names that are only visible to the provided LanguageContext.
  306. /// </summary>
  307. public bool TryGetName(LanguageContext context, SymbolId name, out object value) {
  308. if (_contextScopes != null) {
  309. if (_contextScopes.TryGetName(context, name, out value)) {
  310. return true;
  311. }
  312. }
  313. if (_dict.TryGetValue(name, out value)) return true;
  314. value = null;
  315. return false;
  316. }
  317. /// <summary>
  318. /// Trys to lookup the provided name in the current scope's context specific dictionary.
  319. /// Search includes names that are only visible to the provided LanguageContext.
  320. /// </summary>
  321. public bool TryGetNameForContext(LanguageContext context, SymbolId name, out object value) {
  322. if (_contextScopes != null) {
  323. if (_contextScopes.TryGetName(context, name, out value)) {
  324. return true;
  325. }
  326. }
  327. value = null;
  328. return false;
  329. }
  330. /// <summary>
  331. /// Attempts to lookup the provided name in this scope or any outer scope.
  332. /// </summary>
  333. public bool TryLookupName(SymbolId name, out object value) {
  334. return TryLookupName(_language, name, out value);
  335. }
  336. /// <summary>
  337. /// Attempts to lookup the provided name in this scope or any outer scope. Lookup
  338. /// includes searching for names that are only visible to the provided LanguageContext.
  339. /// </summary>
  340. public bool TryLookupName(LanguageContext context, SymbolId name, out object value) {
  341. Scope curScope = this;
  342. do {
  343. if (curScope == this || curScope.IsVisible) {
  344. if (curScope.TryGetName(context, name, out value)) {
  345. return true;
  346. }
  347. }
  348. curScope = curScope.Parent;
  349. } while (curScope != null);
  350. value = null;
  351. return false;
  352. }
  353. /// <summary>
  354. /// Attempts to lookup the provided name in this scope or any outer scope. If the
  355. /// name is not defined MissingMemberException is thrown.
  356. /// </summary>
  357. public object LookupName(SymbolId name) {
  358. return LookupName(_language, name);
  359. }
  360. /// <summary>
  361. /// Attempts to lookup the provided name in this scope or any outer scope. The
  362. /// search includes looking for names that are only visible to the provided LanguageContext.
  363. ///
  364. /// If the name is not defined the language defined MissingName exception is thrown.
  365. /// </summary>
  366. public object LookupName(LanguageContext context, SymbolId name) {
  367. object res;
  368. if (!TryLookupName(context, name, out res)) {
  369. throw context.MissingName(name);
  370. }
  371. return res;
  372. }
  373. /// <summary>
  374. /// Sets the name to the specified value for the current context.
  375. /// </summary>
  376. /// <exception cref="MemberAccessException">The name has already been published and marked as ReadOnly</exception>
  377. public void SetName(SymbolId name, object value) {
  378. if (_attrs != null) _attrs.CheckWritable(name);
  379. _dict[name] = value;
  380. }
  381. /// <summary>
  382. /// Sets the name to the specified value for the current context.
  383. ///
  384. /// Provides the ScopeMemberAttributes which should be set on the provided object.
  385. /// </summary>
  386. /// <exception cref="MemberAccessException">The name has already been published and marked as ReadOnly</exception>
  387. public void SetName(SymbolId name, object value, ScopeMemberAttributes attributes) {
  388. if (_attrs != null) _attrs.CheckWritable(name);
  389. if (_attrs == null) {
  390. _attrs = new ScopeAttributeDictionary();
  391. }
  392. _attrs.Set(name, attributes);
  393. _dict[name] = value;
  394. }
  395. /// <summary>
  396. /// Sets a name that is only available in the specified context.
  397. /// </summary>
  398. /// <exception cref="MemberAccessException">The name has already been published and marked as ReadOnly</exception>
  399. public void SetName(ContextId context, SymbolId name, object value) {
  400. if (_attrs != null) _attrs.CheckWritable(name);
  401. if (context == ContextId.Empty) {
  402. SetName(name, value);
  403. } else {
  404. if (_contextScopes == null) _contextScopes = new ContextSensitiveScope();
  405. _contextScopes.SetName(context, name, value);
  406. }
  407. }
  408. /// <summary>
  409. /// Sets a name that is only available in the specified context.
  410. ///
  411. /// Provides the ScopeMemberAttributes which should be set on the provided object.
  412. /// </summary>
  413. /// <exception cref="MemberAccessException">The name has already been published and marked as ReadOnly</exception>
  414. public void SetName(ContextId context, SymbolId name, object value, ScopeMemberAttributes attributes) {
  415. if (_attrs != null) _attrs.CheckWritable(name);
  416. if (context == ContextId.Empty) {
  417. SetName(name, value);
  418. } else {
  419. if (_contextScopes == null) _contextScopes = new ContextSensitiveScope();
  420. _contextScopes.SetName(context, name, value, attributes);
  421. }
  422. }
  423. /// <summary>
  424. /// Removes all members from the dictionary and any context-sensitive dictionaries.
  425. /// </summary>
  426. public void Clear() {
  427. if (_contextScopes != null) _contextScopes.Clear();
  428. List<object> ids = new List<object>(_dict.Keys);
  429. foreach (object name in ids) {
  430. if (_attrs == null || _attrs.CheckDeletable(name)) {
  431. _dict.RemoveObjectKey(name);
  432. }
  433. }
  434. }
  435. /// <summary>
  436. /// Determines if this context or any outer scope contains the defined name.
  437. /// </summary>
  438. public bool ContainsName(SymbolId name) {
  439. return ContainsName(_language, name);
  440. }
  441. /// <summary>
  442. /// Determines if this context or any outer scope contains the defined name that
  443. /// is available from the provided LanguageContext.
  444. /// </summary>
  445. public bool ContainsName(LanguageContext context, SymbolId name) {
  446. object tmp;
  447. return TryLookupName(context, name, out tmp);
  448. }
  449. /// <summary>
  450. /// Removes the provided name from this scope
  451. /// </summary>
  452. public void RemoveName(SymbolId name) {
  453. RemoveName(_language, name);
  454. }
  455. /// <summary>
  456. /// Removes the provided name from this scope removing names
  457. /// visible to both the current context and all contexts.
  458. /// </summary>
  459. public bool RemoveName(LanguageContext context, SymbolId name) {
  460. if (!TryRemoveName(context, name)) {
  461. throw context.MissingName(name);
  462. }
  463. return true;
  464. }
  465. /// <summary>
  466. /// Removes the provided name from this scope removing names
  467. /// visible to both the current context and all contexts.
  468. /// </summary>
  469. public void RemoveNameForContext(LanguageContext context, SymbolId name) {
  470. if (!TryRemoveForContext(context, name)) {
  471. throw context.MissingName(name);
  472. }
  473. }
  474. /// <summary>
  475. /// Attemps to remove the provided name from this scope
  476. /// </summary>
  477. public bool TryRemoveName(SymbolId name) {
  478. return TryRemoveName(_language, name);
  479. }
  480. /// <summary>
  481. /// Attemps to remove the provided name from this scope removing names visible
  482. /// to both the current context and all contexts.
  483. /// </summary>
  484. public bool TryRemoveName(LanguageContext context, SymbolId name) {
  485. bool fRemoved = false;
  486. if (_contextScopes != null) fRemoved = _contextScopes.TryRemoveName(context, name);
  487. // TODO: Ideally, we could do this without having to do two lookups.
  488. object removedObject;
  489. if ((_attrs == null || _attrs.CheckDeletable(name))
  490. && _dict.TryGetValue(name, out removedObject) && removedObject != Uninitialized.Instance) {
  491. fRemoved = _dict.Remove(name) || fRemoved;
  492. }
  493. return fRemoved;
  494. }
  495. /// <summary>
  496. /// Attemps to remove the provided name from this scope's context specific dictionary
  497. /// </summary>
  498. public bool TryRemoveForContext(LanguageContext context, SymbolId name) {
  499. if (_contextScopes != null) {
  500. return _contextScopes.TryRemoveName(context, name);
  501. }
  502. return false;
  503. }
  504. // Emitted by TupleSlotFactory
  505. /// <summary>
  506. /// Gets the outer-most scope associated with this scope.
  507. /// </summary>
  508. public Scope ModuleScope {
  509. get {
  510. Scope cur = this;
  511. while (cur.Parent != null) cur = cur.Parent;
  512. return cur;
  513. }
  514. }
  515. /// <summary>
  516. /// Default scope dictionary
  517. /// </summary>
  518. public IAttributesCollection Dict {
  519. get {
  520. return _dict;
  521. }
  522. }
  523. #region Object key access
  524. /// <summary>
  525. /// Attemps to remove the provided object name from this scope removing names visible
  526. /// to both the current context and all contexts.
  527. /// </summary>
  528. public bool TryRemoveObjectName(LanguageContext context, object name) {
  529. bool fRemoved = false;
  530. if (_contextScopes != null) fRemoved = _contextScopes.TryRemoveObjectName(context, name);
  531. if (_attrs == null || _attrs.CheckDeletable(name)) {
  532. fRemoved = _dict.RemoveObjectKey(name) || fRemoved;
  533. }
  534. return fRemoved;
  535. }
  536. /// <summary>
  537. /// Trys to lookup the provided name in the current scope. Search includes
  538. /// names that are only visible to the provided LanguageContext.
  539. /// </summary>
  540. public bool TryGetObjectName(LanguageContext context, object name, out object value) {
  541. if (_contextScopes != null) {
  542. if (_contextScopes.TryGetObjectName(context, name, out value)) {
  543. return true;
  544. }
  545. }
  546. if (_dict.TryGetObjectValue(name, out value)) return true;
  547. value = null;
  548. return false;
  549. }
  550. /// <summary>
  551. /// Attempts to lookup the provided object name in this scope or any outer scope. Lookup
  552. /// includes searching for names that are visible to the provided LanguageContext as well
  553. /// as those available to all contexts.
  554. /// </summary>
  555. public bool TryLookupObjectName(LanguageContext context, object name, out object value) {
  556. Scope curScope = this;
  557. do {
  558. if (curScope == this || curScope.IsVisible) {
  559. if (curScope.TryGetObjectName(context, name, out value)) {
  560. return true;
  561. }
  562. }
  563. curScope = curScope.Parent;
  564. } while (curScope != null);
  565. value = null;
  566. return false;
  567. }
  568. /// <summary>
  569. /// Sets the name to the specified value for the current context.
  570. ///
  571. /// The name is an arbitrary object.
  572. /// </summary>
  573. public void SetObjectName(ContextId context, object name, object value, ScopeMemberAttributes attributes) {
  574. int id = context.Id;
  575. if (id == 0) {
  576. if (_attrs != null) _attrs.CheckWritable(name);
  577. _dict.AddObjectKey(name, value);
  578. if (attributes != ScopeMemberAttributes.None) {
  579. if (_attrs == null) _attrs = new ScopeAttributeDictionary();
  580. _attrs.Set(name, attributes);
  581. }
  582. } else {
  583. if (_contextScopes == null) _contextScopes = new ContextSensitiveScope();
  584. _contextScopes.SetObjectName(context, name, value, attributes);
  585. }
  586. }
  587. /// <summary>
  588. /// Returns the list of Keys available to all languages in addition to those keys
  589. /// which are only available to the provided LanguageContext.
  590. ///
  591. /// Keys marked with the DontEnumerate flag will not be returned.
  592. /// </summary>
  593. public IEnumerable<object> GetAllKeys(LanguageContext context) {
  594. foreach (object key in _dict.Keys) {
  595. if (_attrs == null || _attrs.CheckEnumerable(key)) yield return key;
  596. }
  597. if (_contextScopes != null) {
  598. foreach (KeyValuePair<object, object> kvp in _contextScopes.GetItems(context)) {
  599. if (_dict.ContainsObjectKey(kvp.Key)) continue;
  600. if (_attrs == null || _attrs.CheckEnumerable(kvp.Key)) yield return kvp.Key;
  601. }
  602. }
  603. }
  604. /// <summary>
  605. /// Returns the list of Keys and Values available to all languages in addition to those
  606. /// keys which are only available to the provided LanguageContext.
  607. ///
  608. /// Keys marked with DontEnumerate flag will not be returned.
  609. /// </summary>
  610. public IEnumerable<KeyValuePair<object, object>> GetAllItems(LanguageContext context) {
  611. foreach (KeyValuePair<object, object> kvp in _dict) {
  612. if (_attrs == null || _attrs.CheckEnumerable(kvp.Key)) {
  613. yield return kvp;
  614. }
  615. }
  616. if (_contextScopes != null) {
  617. // TODO: Filter dups
  618. foreach (KeyValuePair<object, object> kvp in _contextScopes.GetItems(context)) {
  619. if (_attrs == null || _attrs.CheckEnumerable(kvp.Key)) yield return kvp;
  620. }
  621. }
  622. }
  623. #endregion
  624. /// <summary>
  625. /// Helper class to hold onto all the context-sensitive information for a Scope.
  626. /// </summary>
  627. private class ContextSensitiveScope {
  628. private List<IAttributesCollection> _dicts;
  629. private List<ScopeAttributeDictionary> _attrs;
  630. public ContextSensitiveScope() {
  631. _dicts = new List<IAttributesCollection>();
  632. }
  633. public void SetName(ContextId context, SymbolId name, object value, ScopeMemberAttributes attrs) {
  634. int id = context.Id;
  635. EnsureDictionary(id);
  636. EnsureAttrDict(id);
  637. if (_attrs != null && id < _attrs.Count) _attrs[id].CheckWritable(name);
  638. _dicts[id][name] = value;
  639. _attrs[id].Set(name, attrs);
  640. }
  641. public void SetName(ContextId context, SymbolId name, object value) {
  642. int id = context.Id;
  643. EnsureDictionary(id);
  644. if (_attrs != null && id < _attrs.Count) _attrs[id].CheckWritable(name);
  645. _dicts[id][name] = value;
  646. }
  647. public void SetObjectName(ContextId context, object name, object value, ScopeMemberAttributes attrs) {
  648. int id = context.Id;
  649. EnsureDictionary(id);
  650. if (attrs != ScopeMemberAttributes.None) EnsureAttrDict(id);
  651. if (_attrs != null && id < _attrs.Count) _attrs[id].CheckWritable(name);
  652. _dicts[id].AddObjectKey(name, value);
  653. if (attrs != ScopeMemberAttributes.None) _attrs[id].Set(name, attrs);
  654. }
  655. public bool TryGetObjectName(LanguageContext context, object name, out object value) {
  656. int id = context.ContextId.Id;
  657. if (id < _dicts.Count && _dicts[id] != null) {
  658. if (_dicts[id].TryGetObjectValue(name, out value)) {
  659. return true;
  660. }
  661. }
  662. value = null;
  663. return false;
  664. }
  665. public bool TryGetName(LanguageContext context, SymbolId name, out object value) {
  666. int id = context.ContextId.Id;
  667. if (id < _dicts.Count && _dicts[id] != null) {
  668. if (_dicts[id].TryGetValue(name, out value)) {
  669. return true;
  670. }
  671. }
  672. value = null;
  673. return false;
  674. }
  675. public bool TryRemoveName(LanguageContext context, SymbolId name) {
  676. bool fRemoved = false;
  677. int id = context.ContextId.Id;
  678. if (id < _dicts.Count && _dicts[id] != null) {
  679. if (_attrs == null || id > _attrs.Count || _attrs[id].CheckDeletable(name)) {
  680. fRemoved = _dicts[id].Remove(name);
  681. }
  682. }
  683. return fRemoved;
  684. }
  685. public bool TryRemoveObjectName(LanguageContext context, object name) {
  686. bool fRemoved = false;
  687. int id = context.ContextId.Id;
  688. if (id < _dicts.Count && _dicts[id] != null) {
  689. if (_attrs == null || id > _attrs.Count || _attrs[id].CheckDeletable(name)) {
  690. fRemoved = _dicts[id].RemoveObjectKey(name);
  691. }
  692. }
  693. return fRemoved;
  694. }
  695. public IEnumerable<KeyValuePair<object, object>> GetItems(LanguageContext lc) {
  696. int id = lc.ContextId.Id;
  697. if (id < _dicts.Count && _dicts[id] != null) {
  698. foreach (KeyValuePair<object, object> kvp in _dicts[id]) {
  699. if (_attrs == null || id > _attrs.Count || _attrs[id].CheckEnumerable(kvp.Key)) {
  700. yield return kvp;
  701. }
  702. }
  703. }
  704. yield break;
  705. }
  706. public void Clear() {
  707. if (_attrs == null) {
  708. // fast clear, no attributes preventing us from clearing
  709. _dicts = new List<IAttributesCollection>();
  710. return;
  711. }
  712. for (int i = 0; i < _dicts.Count; i++) {
  713. if (_attrs[i] == null) {
  714. // no per-context attributes preventing us from clearing
  715. _dicts[i] = null;
  716. continue;
  717. }
  718. // need to check each key
  719. if (_dicts[i] != null) {
  720. List<SymbolId> keys = new List<SymbolId>(_dicts[i].SymbolAttributes.Keys);
  721. foreach (SymbolId key in keys) {
  722. if (_attrs == null || i > _attrs.Count || _attrs[i].CheckDeletable(key)) {
  723. _dicts[i].Remove(key);
  724. }
  725. }
  726. }
  727. }
  728. }
  729. private void EnsureDictionary(int id) {
  730. while (_dicts.Count <= id) {
  731. _dicts.Add(null);
  732. }
  733. if (_dicts[id] == null) {
  734. _dicts[id] = new SymbolDictionary();
  735. }
  736. }
  737. private void EnsureAttrDict(int id) {
  738. if (_attrs == null) _attrs = new List<ScopeAttributeDictionary>();
  739. while (_attrs.Count <= id) {
  740. _attrs.Add(null);
  741. }
  742. if (_attrs[id] == null) {
  743. _attrs[id] = new ScopeAttributeDictionary();
  744. }
  745. }
  746. }
  747. /// <summary>
  748. /// Helper class to hold the attributes for both SymbolId and object attributes.
  749. /// </summary>
  750. private class ScopeAttributeDictionary {
  751. private Dictionary<SymbolId, ScopeMemberAttributes> _attrs;
  752. private Dictionary<object, ScopeMemberAttributes> _objectAttrs;
  753. public ScopeAttributeDictionary() {
  754. }
  755. public void Set(SymbolId name, ScopeMemberAttributes value) {
  756. if (_attrs == null) _attrs = new Dictionary<SymbolId, ScopeMemberAttributes>();
  757. _attrs[name] = value;
  758. }
  759. public void Set(object name, ScopeMemberAttributes value) {
  760. if (_objectAttrs == null) _objectAttrs = new Dictionary<object, ScopeMemberAttributes>();
  761. _objectAttrs[name] = value;
  762. }
  763. public void CheckWritable(SymbolId name) {
  764. ScopeMemberAttributes scopeAttrs;
  765. if (_attrs != null && _attrs.TryGetValue(name, out scopeAttrs) && (scopeAttrs & ScopeMemberAttributes.ReadOnly) != 0) {
  766. throw new MemberAccessException("can only write to member " + SymbolTable.IdToString(name));
  767. }
  768. }
  769. public bool CheckDeletable(SymbolId name) {
  770. ScopeMemberAttributes scopeAttrs;
  771. if (_attrs != null && _attrs.TryGetValue(name, out scopeAttrs) && (scopeAttrs & ScopeMemberAttributes.DontDelete) != 0) {
  772. return false;
  773. }
  774. return true;
  775. }
  776. public bool CheckEnumerable(SymbolId name) {
  777. ScopeMemberAttributes scopeAttrs;
  778. return _attrs == null ||
  779. !_attrs.TryGetValue(name, out scopeAttrs) ||
  780. (scopeAttrs & ScopeMemberAttributes.DontEnumerate) == 0;
  781. }
  782. public void CheckWritable(object name) {
  783. if (name is SymbolId) {
  784. CheckWritable((SymbolId)name);
  785. return;
  786. }
  787. ScopeMemberAttributes scopeAttrs;
  788. if (_objectAttrs != null) {
  789. if (_objectAttrs.TryGetValue(name, out scopeAttrs) && (scopeAttrs & ScopeMemberAttributes.ReadOnly) != 0) {
  790. throw new MemberAccessException("can only write to member " + name.ToString());
  791. }
  792. } else {
  793. string stringName = name as string;
  794. if (stringName != null) {
  795. CheckWritable(SymbolTable.StringToId(stringName));
  796. }
  797. }
  798. }
  799. public bool CheckDeletable(object name) {
  800. if (name is SymbolId) return CheckDeletable((SymbolId)name);
  801. ScopeMemberAttributes scopeAttrs;
  802. if (_objectAttrs != null){
  803. return !_objectAttrs.TryGetValue(name, out scopeAttrs) ||
  804. (scopeAttrs & ScopeMemberAttributes.DontDelete) == 0;
  805. } else {
  806. string stringName = name as string;
  807. if (stringName != null) {
  808. return CheckDeletable(SymbolTable.StringToId(stringName));
  809. }
  810. }
  811. return true;
  812. }
  813. public bool CheckEnumerable(object name) {
  814. if (name is SymbolId) return CheckEnumerable((SymbolId)name);
  815. ScopeMemberAttributes scopeAttrs;
  816. if (_objectAttrs != null) {
  817. return !_objectAttrs.TryGetValue(name, out scopeAttrs) ||
  818. (scopeAttrs & ScopeMemberAttributes.DontEnumerate) == 0;
  819. } else {
  820. string stringName = name as string;
  821. if (stringName != null) {
  822. return CheckEnumerable(SymbolTable.StringToId(stringName));
  823. }
  824. }
  825. return true;
  826. }
  827. }
  828. #endregion
  829. }
  830. }