PageRenderTime 49ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

/Languages/Ruby/Libraries/Extensions/IDictionaryOps.cs

http://github.com/IronLanguages/main
C# | 626 lines | 479 code | 102 blank | 45 comment | 67 complexity | b4a11c868b7fbf4b710e79b603165226 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. * ironruby@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. using System;
  16. using System.Collections;
  17. using System.Collections.Generic;
  18. using System.Reflection;
  19. using System.Runtime.InteropServices;
  20. using Microsoft.Scripting.Runtime;
  21. using Microsoft.Scripting.Actions;
  22. using Microsoft.Scripting.Generation;
  23. using Microsoft.Scripting.Utils;
  24. using System.Runtime.CompilerServices;
  25. using IronRuby.Runtime;
  26. using Microsoft.Scripting;
  27. namespace IronRuby.Builtins {
  28. // TODO: IDictionary<TKey, TValue> instead of IDictionary<object, object>?
  29. // (need support for extension methods on generic interfaces first)
  30. // (IDictionary isn't a good solution because it doesn't have TryGetValue)
  31. [RubyModule(Extends = typeof(IDictionary<object, object>), Restrictions = ModuleRestrictions.None)]
  32. [Includes(typeof(Enumerable))]
  33. public static class IDictionaryOps {
  34. #region Helper methods
  35. // Make a 2 element array
  36. internal static RubyArray/*!*/ MakeArray(KeyValuePair<object, object> pair) {
  37. RubyArray list = new RubyArray(2);
  38. list.Add(CustomStringDictionary.ObjToNull(pair.Key));
  39. list.Add(pair.Value);
  40. return list;
  41. }
  42. internal static RubyArray/*!*/ MakeArray(object key, object value) {
  43. RubyArray list = new RubyArray(2);
  44. list.Add(CustomStringDictionary.ObjToNull(key));
  45. list.Add(value);
  46. return list;
  47. }
  48. // Replaces the data in dest with the data from src
  49. internal static T ReplaceData<T>(T dest, IEnumerable<KeyValuePair<object, object>> src) where T : IDictionary<object, object> {
  50. dest.Clear();
  51. foreach (KeyValuePair<object, object> pair in src) {
  52. dest[pair.Key] = pair.Value;
  53. }
  54. return dest;
  55. }
  56. // Copies key,value pairs into an array. Needed for iteration over the hash while mutating it.
  57. private static IEnumerable<KeyValuePair<object, object>> CopyKeyValuePairs(IDictionary<object, object>/*!*/ dict) {
  58. KeyValuePair<object, object>[] pairs = new KeyValuePair<object, object>[dict.Count];
  59. dict.CopyTo(pairs, 0);
  60. return pairs;
  61. }
  62. #endregion
  63. [RubyMethod("==")]
  64. public static bool Equals(RespondToStorage/*!*/ respondTo, BinaryOpStorage/*!*/ equals, IDictionary<object, object>/*!*/ self, object other) {
  65. return Protocols.RespondTo(respondTo, other, "to_hash") && Protocols.IsEqual(equals, other, self);
  66. }
  67. [MultiRuntimeAware]
  68. private static RubyUtils.RecursionTracker _EqualsTracker = new RubyUtils.RecursionTracker();
  69. [RubyMethod("==")]
  70. public static bool Equals(BinaryOpStorage/*!*/ equals, IDictionary<object, object>/*!*/ self, [NotNull]IDictionary<object, object>/*!*/ other) {
  71. Assert.NotNull(self, other);
  72. if (ReferenceEquals(self, other)) {
  73. return true;
  74. }
  75. if (self.Count != other.Count) {
  76. return false;
  77. }
  78. using (IDisposable handleSelf = _EqualsTracker.TrackObject(self), handleOther = _EqualsTracker.TrackObject(other)) {
  79. if (handleSelf == null && handleOther == null) {
  80. // both dictionaries went recursive:
  81. return true;
  82. }
  83. // Each key value pair must be the same
  84. var site = equals.GetCallSite("==");
  85. foreach (KeyValuePair<object, object> pair in self) {
  86. object value;
  87. if (!other.TryGetValue(pair.Key, out value) || !Protocols.IsEqual(site, pair.Value, value)) {
  88. return false;
  89. }
  90. }
  91. }
  92. return true;
  93. }
  94. [RubyMethod("[]")]
  95. public static object GetElement(RubyContext/*!*/ context, IDictionary<object, object>/*!*/ self, object key) {
  96. object result;
  97. if (!self.TryGetValue(CustomStringDictionary.NullToObj(key), out result)) {
  98. return null;
  99. }
  100. return result;
  101. }
  102. // TODO: remove, frozen check should be implemented in Hash indexer
  103. [RubyMethod("[]=")]
  104. [RubyMethod("store")]
  105. public static object SetElement(RubyContext/*!*/ context, Hash/*!*/ self, object key, object value) {
  106. self.RequireNotFrozen();
  107. return RubyUtils.SetHashElement(context, self, key, value);
  108. }
  109. [RubyMethod("[]=")]
  110. [RubyMethod("store")]
  111. public static object SetElement(RubyContext/*!*/ context, IDictionary<object, object>/*!*/ self, object key, object value) {
  112. return RubyUtils.SetHashElement(context, self, key, value);
  113. }
  114. // TODO: remove, frozen check should be implemented in Hash.Clear
  115. [RubyMethod("clear")]
  116. public static IDictionary<object, object>/*!*/ Clear(Hash/*!*/ self) {
  117. self.RequireNotFrozen();
  118. self.Clear();
  119. return self;
  120. }
  121. [RubyMethod("clear")]
  122. public static IDictionary<object, object>/*!*/ Clear(IDictionary<object, object>/*!*/ self) {
  123. self.Clear();
  124. return self;
  125. }
  126. // We don't define "dup" here because "dup" shouldn't show up on builtin types like Hash
  127. // (Kernel#dup just works for these types)
  128. private static IDictionary<object, object>/*!*/ Duplicate(
  129. CallSiteStorage<Func<CallSite, object, object, object>>/*!*/ initializeCopyStorage,
  130. CallSiteStorage<Func<CallSite, RubyClass, object>>/*!*/ allocateStorage,
  131. IDictionary<object, object>/*!*/ self) {
  132. // Call Kernel#dup, then copy items
  133. var copy = (IDictionary<object, object>)KernelOps.Duplicate(initializeCopyStorage, allocateStorage, self);
  134. return ReplaceData(copy, self);
  135. }
  136. [RubyMethod("default")]
  137. public static object GetDefaultValue(RubyContext/*!*/ context, IDictionary<object, object>/*!*/ self, [Optional]object key) {
  138. return null;
  139. }
  140. [RubyMethod("default_proc")]
  141. public static Proc GetDefaultProc(IDictionary<object, object>/*!*/ self) {
  142. return null;
  143. }
  144. #region delete, delete_if
  145. [RubyMethod("delete")]
  146. public static object Delete(BlockParam block, Hash/*!*/ self, object key) {
  147. self.RequireNotFrozen();
  148. return Delete(block, (IDictionary<object, object>)self, key);
  149. }
  150. [RubyMethod("delete")]
  151. public static object Delete(BlockParam block, IDictionary<object, object>/*!*/ self, object key) {
  152. object value;
  153. if (!self.TryGetValue(CustomStringDictionary.NullToObj(key), out value)) {
  154. // key not found, call the block if it was passed in
  155. if (block != null) {
  156. object result;
  157. block.Yield(key, out result);
  158. return result;
  159. }
  160. return null;
  161. }
  162. self.Remove(CustomStringDictionary.NullToObj(key));
  163. return value;
  164. }
  165. [RubyMethod("delete_if")]
  166. public static object DeleteIf(BlockParam block, Hash/*!*/ self) {
  167. self.RequireNotFrozen();
  168. return DeleteIf(block, (IDictionary<object, object>)self);
  169. }
  170. [RubyMethod("delete_if")]
  171. public static object DeleteIf(BlockParam block, IDictionary<object, object>/*!*/ self) {
  172. if (self.Count > 0 && block == null) {
  173. throw RubyExceptions.NoBlockGiven();
  174. }
  175. // Make a copy of the keys to delete, so we don't modify the collection
  176. // while iterating over it
  177. RubyArray keysToDelete = new RubyArray();
  178. foreach (var pair in self) {
  179. object result;
  180. if (block.Yield(CustomStringDictionary.ObjToNull(pair.Key), pair.Value, out result)) {
  181. return result;
  182. }
  183. // Delete the key, unless 'false' or 'nil' is returned
  184. if (RubyOps.IsTrue(result)) {
  185. keysToDelete.Add(pair.Key);
  186. }
  187. }
  188. foreach (object key in keysToDelete) {
  189. self.Remove(key);
  190. }
  191. return self;
  192. }
  193. #endregion
  194. #region each, each_pair, each_key, each_value
  195. [RubyMethod("each")]
  196. [RubyMethod("each_pair")]
  197. public static Enumerator/*!*/ Each(RubyContext/*!*/ context, IDictionary<object, object>/*!*/ self) {
  198. return new Enumerator((_, block) => Each(context, block, self));
  199. }
  200. [RubyMethod("each")]
  201. [RubyMethod("each_pair")]
  202. public static object Each(RubyContext/*!*/ context, [NotNull]BlockParam/*!*/ block, IDictionary<object, object>/*!*/ self) {
  203. if (self.Count > 0) {
  204. // Must make a copy of the Keys array so that we can iterate over a static set of keys. Remember
  205. // that the block can modify the hash, hence the need for a copy of the keys
  206. object[] keys = new object[self.Count];
  207. self.Keys.CopyTo(keys, 0);
  208. // TODO: what are all the scenarios where the block can mutate the hash? can it remove keys? if so, what happens?
  209. for (int i = 0; i < keys.Length; i++) {
  210. object result;
  211. if (block.Yield(MakeArray(keys[i], self[keys[i]]), out result)) {
  212. return result;
  213. }
  214. }
  215. }
  216. return self;
  217. }
  218. [RubyMethod("each_key")]
  219. public static Enumerator/*!*/ EachKey(RubyContext/*!*/ context, IDictionary<object, object>/*!*/ self) {
  220. return new Enumerator((_, block) => EachKey(context, block, self));
  221. }
  222. [RubyMethod("each_key")]
  223. public static object EachKey(RubyContext/*!*/ context, [NotNull]BlockParam/*!*/ block, IDictionary<object, object>/*!*/ self) {
  224. if (self.Count > 0) {
  225. // Must make a copy of the Keys array so that we can iterate over a static set of keys. Remember
  226. // that the block can modify the hash, hence the need for a copy of the keys
  227. object[] keys = new object[self.Count];
  228. self.Keys.CopyTo(keys, 0);
  229. // TODO: what are all the scenarios where the block can mutate the hash? can it remove keys? if so, what happens?
  230. for (int i = 0; i < keys.Length; i++) {
  231. object result;
  232. if (block.Yield(CustomStringDictionary.ObjToNull(keys[i]), out result)) {
  233. return result;
  234. }
  235. }
  236. }
  237. return self;
  238. }
  239. [RubyMethod("each_value")]
  240. public static Enumerator/*!*/ EachValue(RubyContext/*!*/ context, IDictionary<object, object>/*!*/ self) {
  241. return new Enumerator((_, block) => EachValue(context, block, self));
  242. }
  243. [RubyMethod("each_value")]
  244. public static object EachValue(RubyContext/*!*/ context, [NotNull]BlockParam/*!*/ block, IDictionary<object, object>/*!*/ self) {
  245. if (self.Count > 0) {
  246. // Ruby allows modifications while iterating thru the dictionary:
  247. object[] values = new object[self.Count];
  248. self.Values.CopyTo(values, 0);
  249. for (int i = 0; i < values.Length; i++) {
  250. object result;
  251. if (block.Yield(values[i], out result)) {
  252. return result;
  253. }
  254. }
  255. }
  256. return self;
  257. }
  258. #endregion
  259. [RubyMethod("empty?")]
  260. public static bool Empty(IDictionary<object, object>/*!*/ self) {
  261. return self.Count == 0;
  262. }
  263. [RubyMethod("fetch")]
  264. public static object Fetch(RubyContext/*!*/ context, BlockParam block, IDictionary<object, object>/*!*/ self, object key, [Optional]object defaultValue) {
  265. object result;
  266. if (self.TryGetValue(CustomStringDictionary.NullToObj(key), out result)) {
  267. return result;
  268. }
  269. if (block != null) {
  270. if (defaultValue != Missing.Value) {
  271. context.ReportWarning("block supersedes default value argument");
  272. }
  273. block.Yield(key, out result);
  274. return result;
  275. }
  276. if (defaultValue == Missing.Value) {
  277. throw RubyExceptions.CreateIndexError("key not found");
  278. }
  279. return defaultValue;
  280. }
  281. [RubyMethod("has_key?")]
  282. [RubyMethod("include?")]
  283. [RubyMethod("key?")]
  284. [RubyMethod("member?")]
  285. public static bool HasKey(IDictionary<object, object>/*!*/ self, object key) {
  286. return self.ContainsKey(CustomStringDictionary.NullToObj(key));
  287. }
  288. [RubyMethod("has_value?")]
  289. [RubyMethod("value?")]
  290. public static bool HasValue(BinaryOpStorage/*!*/ equals, IDictionary<object, object>/*!*/ self, object value) {
  291. foreach (KeyValuePair<object, object> pair in self) {
  292. if (Protocols.IsEqual(equals, pair.Value, value)) {
  293. return true;
  294. }
  295. }
  296. return false;
  297. }
  298. [RubyMethod("index")]
  299. public static object Index(BinaryOpStorage/*!*/ equals, IDictionary<object, object>/*!*/ self, object value) {
  300. foreach (KeyValuePair<object, object> pair in self) {
  301. if (Protocols.IsEqual(equals, pair.Value, value)) {
  302. return CustomStringDictionary.ObjToNull(pair.Key);
  303. }
  304. }
  305. return null;
  306. }
  307. [RubyMethod("invert")]
  308. public static Hash/*!*/ Invert(RubyContext/*!*/ context, IDictionary<object, object>/*!*/ self) {
  309. // invert returns a Hash, even from subclasses
  310. Hash hash = new Hash(context.EqualityComparer, self.Count);
  311. foreach (KeyValuePair<object, object> pair in self) {
  312. hash[CustomStringDictionary.NullToObj(pair.Value)] = CustomStringDictionary.ObjToNull(pair.Key);
  313. }
  314. return hash;
  315. }
  316. [RubyMethod("keys")]
  317. public static RubyArray/*!*/ GetKeys(IDictionary<object, object>/*!*/ self) {
  318. RubyArray keys = new RubyArray(self.Count);
  319. foreach (object key in self.Keys) {
  320. keys.Add(CustomStringDictionary.ObjToNull(key));
  321. }
  322. return keys;
  323. }
  324. [RubyMethod("length")]
  325. [RubyMethod("size")]
  326. public static int Length(IDictionary<object, object>/*!*/ self) {
  327. return self.Count;
  328. }
  329. [RubyMethod("merge")]
  330. public static object Merge(
  331. CallSiteStorage<Func<CallSite, object, object, object>>/*!*/ initializeCopyStorage,
  332. CallSiteStorage<Func<CallSite, RubyClass, object>>/*!*/ allocateStorage,
  333. BlockParam block, IDictionary<object, object>/*!*/ self,
  334. [DefaultProtocol, NotNull]IDictionary<object, object>/*!*/ hash) {
  335. return Update(block, Duplicate(initializeCopyStorage, allocateStorage, self), hash);
  336. }
  337. [RubyMethod("merge!")]
  338. [RubyMethod("update")]
  339. public static object Update(BlockParam block, Hash/*!*/ self, [DefaultProtocol, NotNull]IDictionary<object, object>/*!*/ hash) {
  340. self.RequireNotFrozen();
  341. return Update(block, (IDictionary<object, object>)self, hash);
  342. }
  343. [RubyMethod("merge!")]
  344. [RubyMethod("update")]
  345. public static object Update(BlockParam block, IDictionary<object, object>/*!*/ self, [DefaultProtocol, NotNull]IDictionary<object, object>/*!*/ hash) {
  346. if (block == null) {
  347. foreach (var pair in CopyKeyValuePairs(hash)) {
  348. self[CustomStringDictionary.NullToObj(pair.Key)] = pair.Value;
  349. }
  350. } else {
  351. foreach (var pair in CopyKeyValuePairs(hash)) {
  352. object key = pair.Key, newValue = pair.Value, oldValue;
  353. if (self.TryGetValue(key, out oldValue)) {
  354. if (block.Yield(CustomStringDictionary.ObjToNull(key), oldValue, pair.Value, out newValue)) {
  355. return newValue;
  356. }
  357. }
  358. self[key] = newValue;
  359. }
  360. }
  361. return self;
  362. }
  363. [RubyMethod("rehash")]
  364. public static IDictionary<object, object> Rehash(Hash/*!*/ self) {
  365. self.RequireNotFrozen();
  366. return Rehash((IDictionary<object, object>)self);
  367. }
  368. [RubyMethod("rehash")]
  369. public static IDictionary<object, object> Rehash(IDictionary<object, object>/*!*/ self) {
  370. return ReplaceData(self, CopyKeyValuePairs(self));
  371. }
  372. // This works like delete_if, not reject!
  373. // (because it needs to return the new collection)
  374. [RubyMethod("reject")]
  375. public static object Reject(
  376. CallSiteStorage<Func<CallSite, object, object, object>>/*!*/ initializeCopyStorage,
  377. CallSiteStorage<Func<CallSite, RubyClass, object>>/*!*/ allocateStorage,
  378. BlockParam block, IDictionary<object, object>/*!*/ self) {
  379. return DeleteIf(block, Duplicate(initializeCopyStorage, allocateStorage, self));
  380. }
  381. // This works like delete_if, but returns nil if no elements were removed
  382. [RubyMethod("reject!")]
  383. public static object RejectMutate(BlockParam block, Hash/*!*/ self) {
  384. self.RequireNotFrozen();
  385. return RejectMutate(block, (IDictionary<object, object>)self);
  386. }
  387. [RubyMethod("reject!")]
  388. public static object RejectMutate(BlockParam block, IDictionary<object, object>/*!*/ self) {
  389. // Make a copy of the keys to delete, so we don't modify the collection
  390. // while iterating over it
  391. RubyArray keysToDelete = new RubyArray();
  392. foreach (KeyValuePair<object,object> pair in self) {
  393. object result;
  394. if (block.Yield(CustomStringDictionary.ObjToNull(pair.Key), pair.Value, out result)) {
  395. return result;
  396. }
  397. // Delete the key, unless 'false' or 'nil' is returned
  398. if (RubyOps.IsTrue(result)) {
  399. keysToDelete.Add(pair.Key);
  400. }
  401. }
  402. foreach (object key in keysToDelete) {
  403. self.Remove(key);
  404. }
  405. return keysToDelete.Count == 0 ? null : self;
  406. }
  407. [RubyMethod("replace")]
  408. public static Hash/*!*/ Replace(RubyContext/*!*/ context, Hash/*!*/ self, [DefaultProtocol, NotNull]IDictionary<object, object>/*!*/ other) {
  409. self.RequireNotFrozen();
  410. return IDictionaryOps.ReplaceData(self, other);
  411. }
  412. [RubyMethod("select")]
  413. public static Enumerator/*!*/ Select(RubyContext/*!*/ context, IDictionary<object, object>/*!*/ self) {
  414. return new Enumerator((_, block) => Select(context, block, self));
  415. }
  416. [RubyMethod("select")]
  417. public static object Select(RubyContext/*!*/ context, [NotNull]BlockParam/*!*/ block, IDictionary<object, object>/*!*/ self) {
  418. Hash result = new Hash(context);
  419. foreach (var pair in CopyKeyValuePairs(self)) {
  420. object blockResult;
  421. if (block.Yield(CustomStringDictionary.ObjToNull(pair.Key), pair.Value, out blockResult)) {
  422. return blockResult;
  423. }
  424. if (RubyOps.IsTrue(blockResult)) {
  425. result[pair.Key] = pair.Value;
  426. }
  427. }
  428. return result;
  429. }
  430. [RubyMethod("shift")]
  431. public static object Shift(Hash/*!*/ self) {
  432. self.RequireNotFrozen();
  433. return Shift((IDictionary<object, object>)self);
  434. }
  435. [RubyMethod("shift")]
  436. public static object Shift(IDictionary<object, object>/*!*/ self) {
  437. if (self.Count == 0) {
  438. return null;
  439. }
  440. IEnumerator<KeyValuePair<object, object>> e = self.GetEnumerator();
  441. e.MoveNext();
  442. KeyValuePair<object, object> pair = e.Current;
  443. self.Remove(pair.Key);
  444. return MakeArray(pair);
  445. }
  446. [RubyMethod("sort")]
  447. public static object Sort(ComparisonStorage/*!*/ comparisonStorage, BlockParam block, IDictionary<object, object>/*!*/ self) {
  448. return ArrayOps.SortInPlace(comparisonStorage, block, ToArray(self));
  449. }
  450. [RubyMethod("to_a")]
  451. public static RubyArray/*!*/ ToArray(IDictionary<object, object>/*!*/ self) {
  452. RubyArray result = new RubyArray(self.Count);
  453. foreach (KeyValuePair<object, object> pair in self) {
  454. result.Add(MakeArray(pair));
  455. }
  456. return result;
  457. }
  458. [RubyMethod("flatten")]
  459. public static IList/*!*/ Flatten(ConversionStorage<IList>/*!*/ tryToAry, IDictionary<object, object>/*!*/ self,
  460. [DefaultProtocol, DefaultParameterValue(1)] int maxDepth) {
  461. if (maxDepth == 0) {
  462. return ToArray(self);
  463. }
  464. if (maxDepth > 0) {
  465. maxDepth--;
  466. }
  467. RubyArray result = new RubyArray();
  468. IList list;
  469. foreach (KeyValuePair<object, object> pair in self) {
  470. if (maxDepth != 0 && (list = Protocols.TryCastToArray(tryToAry, pair.Key)) != null) {
  471. IListOps.Flatten(tryToAry, list, maxDepth - 1, result);
  472. } else {
  473. result.Add(pair.Key);
  474. }
  475. if (maxDepth != 0 && (list = Protocols.TryCastToArray(tryToAry, pair.Value)) != null) {
  476. IListOps.Flatten(tryToAry, list, maxDepth - 1, result);
  477. } else {
  478. result.Add(pair.Value);
  479. }
  480. }
  481. return result;
  482. }
  483. [RubyMethod("to_hash")]
  484. public static IDictionary<object, object> ToHash(IDictionary<object, object>/*!*/ self) {
  485. return self;
  486. }
  487. [RubyMethod("to_s")]
  488. [RubyMethod("inspect")]
  489. public static MutableString/*!*/ ToMutableString(RubyContext/*!*/ context, IDictionary<object, object>/*!*/ self) {
  490. using (IDisposable handle = RubyUtils.InfiniteInspectTracker.TrackObject(self)) {
  491. if (handle == null) {
  492. return MutableString.CreateAscii("{...}");
  493. }
  494. MutableString str = MutableString.CreateMutable(RubyEncoding.Binary);
  495. str.Append('{');
  496. bool first = true;
  497. foreach (var entry in self) {
  498. if (first) {
  499. first = false;
  500. } else {
  501. str.Append(", ");
  502. }
  503. str.Append(context.Inspect(CustomStringDictionary.ObjToNull(entry.Key)));
  504. str.Append("=>");
  505. str.Append(context.Inspect(entry.Value));
  506. }
  507. str.Append('}');
  508. return str;
  509. }
  510. }
  511. [RubyMethod("values")]
  512. public static RubyArray/*!*/ GetValues(IDictionary<object, object>/*!*/ self) {
  513. return new RubyArray(self.Values);
  514. }
  515. [RubyMethod("values_at")]
  516. public static RubyArray/*!*/ ValuesAt(RubyContext/*!*/ context, IDictionary<object, object>/*!*/ self, params object[]/*!*/ keys) {
  517. RubyArray values = new RubyArray();
  518. foreach (object key in keys) {
  519. values.Add(GetElement(context, self, key));
  520. }
  521. return values;
  522. }
  523. }
  524. }