deserializer.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639
  1. 'use strict';
  2. const Buffer = require('buffer').Buffer;
  3. const Long = require('../long');
  4. const Double = require('../double');
  5. const Timestamp = require('../timestamp');
  6. const ObjectId = require('../objectid');
  7. const Code = require('../code');
  8. const MinKey = require('../min_key');
  9. const MaxKey = require('../max_key');
  10. const Decimal128 = require('../decimal128');
  11. const Int32 = require('../int_32');
  12. const DBRef = require('../db_ref');
  13. const BSONRegExp = require('../regexp');
  14. const Binary = require('../binary');
  15. const constants = require('../constants');
  16. const validateUtf8 = require('../validate_utf8').validateUtf8;
  17. // Internal long versions
  18. const JS_INT_MAX_LONG = Long.fromNumber(constants.JS_INT_MAX);
  19. const JS_INT_MIN_LONG = Long.fromNumber(constants.JS_INT_MIN);
  20. const functionCache = {};
  21. function deserialize(buffer, options, isArray) {
  22. options = options == null ? {} : options;
  23. const index = options && options.index ? options.index : 0;
  24. // Read the document size
  25. const size =
  26. buffer[index] |
  27. (buffer[index + 1] << 8) |
  28. (buffer[index + 2] << 16) |
  29. (buffer[index + 3] << 24);
  30. if (size < 5) {
  31. throw new Error(`bson size must be >= 5, is ${size}`);
  32. }
  33. if (options.allowObjectSmallerThanBufferSize && buffer.length < size) {
  34. throw new Error(`buffer length ${buffer.length} must be >= bson size ${size}`);
  35. }
  36. if (!options.allowObjectSmallerThanBufferSize && buffer.length !== size) {
  37. throw new Error(`buffer length ${buffer.length} must === bson size ${size}`);
  38. }
  39. if (size + index > buffer.length) {
  40. throw new Error(
  41. `(bson size ${size} + options.index ${index} must be <= buffer length ${Buffer.byteLength(
  42. buffer
  43. )})`
  44. );
  45. }
  46. // Illegal end value
  47. if (buffer[index + size - 1] !== 0) {
  48. throw new Error("One object, sized correctly, with a spot for an EOO, but the EOO isn't 0x00");
  49. }
  50. // Start deserializtion
  51. return deserializeObject(buffer, index, options, isArray);
  52. }
  53. function deserializeObject(buffer, index, options, isArray) {
  54. const evalFunctions = options['evalFunctions'] == null ? false : options['evalFunctions'];
  55. const cacheFunctions = options['cacheFunctions'] == null ? false : options['cacheFunctions'];
  56. const cacheFunctionsCrc32 =
  57. options['cacheFunctionsCrc32'] == null ? false : options['cacheFunctionsCrc32'];
  58. if (!cacheFunctionsCrc32) var crc32 = null;
  59. const fieldsAsRaw = options['fieldsAsRaw'] == null ? null : options['fieldsAsRaw'];
  60. // Return raw bson buffer instead of parsing it
  61. const raw = options['raw'] == null ? false : options['raw'];
  62. // Return BSONRegExp objects instead of native regular expressions
  63. const bsonRegExp = typeof options['bsonRegExp'] === 'boolean' ? options['bsonRegExp'] : false;
  64. // Controls the promotion of values vs wrapper classes
  65. const promoteBuffers = options['promoteBuffers'] == null ? false : options['promoteBuffers'];
  66. const promoteLongs = options['promoteLongs'] == null ? true : options['promoteLongs'];
  67. const promoteValues = options['promoteValues'] == null ? true : options['promoteValues'];
  68. // Set the start index
  69. let startIndex = index;
  70. // Validate that we have at least 4 bytes of buffer
  71. if (buffer.length < 5) throw new Error('corrupt bson message < 5 bytes long');
  72. // Read the document size
  73. const size =
  74. buffer[index++] | (buffer[index++] << 8) | (buffer[index++] << 16) | (buffer[index++] << 24);
  75. // Ensure buffer is valid size
  76. if (size < 5 || size > buffer.length) throw new Error('corrupt bson message');
  77. // Create holding object
  78. const object = isArray ? [] : {};
  79. // Used for arrays to skip having to perform utf8 decoding
  80. let arrayIndex = 0;
  81. let done = false;
  82. // While we have more left data left keep parsing
  83. while (!done) {
  84. // Read the type
  85. const elementType = buffer[index++];
  86. // If we get a zero it's the last byte, exit
  87. if (elementType === 0) break;
  88. // Get the start search index
  89. let i = index;
  90. // Locate the end of the c string
  91. while (buffer[i] !== 0x00 && i < buffer.length) {
  92. i++;
  93. }
  94. // If are at the end of the buffer there is a problem with the document
  95. if (i >= Buffer.byteLength(buffer)) throw new Error('Bad BSON Document: illegal CString');
  96. const name = isArray ? arrayIndex++ : buffer.toString('utf8', index, i);
  97. index = i + 1;
  98. if (elementType === constants.BSON_DATA_STRING) {
  99. const stringSize =
  100. buffer[index++] |
  101. (buffer[index++] << 8) |
  102. (buffer[index++] << 16) |
  103. (buffer[index++] << 24);
  104. if (
  105. stringSize <= 0 ||
  106. stringSize > buffer.length - index ||
  107. buffer[index + stringSize - 1] !== 0
  108. )
  109. throw new Error('bad string length in bson');
  110. if (!validateUtf8(buffer, index, index + stringSize - 1)) {
  111. throw new Error('Invalid UTF-8 string in BSON document');
  112. }
  113. const s = buffer.toString('utf8', index, index + stringSize - 1);
  114. object[name] = s;
  115. index = index + stringSize;
  116. } else if (elementType === constants.BSON_DATA_OID) {
  117. const oid = Buffer.alloc(12);
  118. buffer.copy(oid, 0, index, index + 12);
  119. object[name] = new ObjectId(oid);
  120. index = index + 12;
  121. } else if (elementType === constants.BSON_DATA_INT && promoteValues === false) {
  122. object[name] = new Int32(
  123. buffer[index++] | (buffer[index++] << 8) | (buffer[index++] << 16) | (buffer[index++] << 24)
  124. );
  125. } else if (elementType === constants.BSON_DATA_INT) {
  126. object[name] =
  127. buffer[index++] |
  128. (buffer[index++] << 8) |
  129. (buffer[index++] << 16) |
  130. (buffer[index++] << 24);
  131. } else if (elementType === constants.BSON_DATA_NUMBER && promoteValues === false) {
  132. object[name] = new Double(buffer.readDoubleLE(index));
  133. index = index + 8;
  134. } else if (elementType === constants.BSON_DATA_NUMBER) {
  135. object[name] = buffer.readDoubleLE(index);
  136. index = index + 8;
  137. } else if (elementType === constants.BSON_DATA_DATE) {
  138. const lowBits =
  139. buffer[index++] |
  140. (buffer[index++] << 8) |
  141. (buffer[index++] << 16) |
  142. (buffer[index++] << 24);
  143. const highBits =
  144. buffer[index++] |
  145. (buffer[index++] << 8) |
  146. (buffer[index++] << 16) |
  147. (buffer[index++] << 24);
  148. object[name] = new Date(new Long(lowBits, highBits).toNumber());
  149. } else if (elementType === constants.BSON_DATA_BOOLEAN) {
  150. if (buffer[index] !== 0 && buffer[index] !== 1) throw new Error('illegal boolean type value');
  151. object[name] = buffer[index++] === 1;
  152. } else if (elementType === constants.BSON_DATA_OBJECT) {
  153. const _index = index;
  154. const objectSize =
  155. buffer[index] |
  156. (buffer[index + 1] << 8) |
  157. (buffer[index + 2] << 16) |
  158. (buffer[index + 3] << 24);
  159. if (objectSize <= 0 || objectSize > buffer.length - index)
  160. throw new Error('bad embedded document length in bson');
  161. // We have a raw value
  162. if (raw) {
  163. object[name] = buffer.slice(index, index + objectSize);
  164. } else {
  165. object[name] = deserializeObject(buffer, _index, options, false);
  166. }
  167. index = index + objectSize;
  168. } else if (elementType === constants.BSON_DATA_ARRAY) {
  169. const _index = index;
  170. const objectSize =
  171. buffer[index] |
  172. (buffer[index + 1] << 8) |
  173. (buffer[index + 2] << 16) |
  174. (buffer[index + 3] << 24);
  175. let arrayOptions = options;
  176. // Stop index
  177. const stopIndex = index + objectSize;
  178. // All elements of array to be returned as raw bson
  179. if (fieldsAsRaw && fieldsAsRaw[name]) {
  180. arrayOptions = {};
  181. for (let n in options) arrayOptions[n] = options[n];
  182. arrayOptions['raw'] = true;
  183. }
  184. object[name] = deserializeObject(buffer, _index, arrayOptions, true);
  185. index = index + objectSize;
  186. if (buffer[index - 1] !== 0) throw new Error('invalid array terminator byte');
  187. if (index !== stopIndex) throw new Error('corrupted array bson');
  188. } else if (elementType === constants.BSON_DATA_UNDEFINED) {
  189. object[name] = undefined;
  190. } else if (elementType === constants.BSON_DATA_NULL) {
  191. object[name] = null;
  192. } else if (elementType === constants.BSON_DATA_LONG) {
  193. // Unpack the low and high bits
  194. const lowBits =
  195. buffer[index++] |
  196. (buffer[index++] << 8) |
  197. (buffer[index++] << 16) |
  198. (buffer[index++] << 24);
  199. const highBits =
  200. buffer[index++] |
  201. (buffer[index++] << 8) |
  202. (buffer[index++] << 16) |
  203. (buffer[index++] << 24);
  204. const long = new Long(lowBits, highBits);
  205. // Promote the long if possible
  206. if (promoteLongs && promoteValues === true) {
  207. object[name] =
  208. long.lessThanOrEqual(JS_INT_MAX_LONG) && long.greaterThanOrEqual(JS_INT_MIN_LONG)
  209. ? long.toNumber()
  210. : long;
  211. } else {
  212. object[name] = long;
  213. }
  214. } else if (elementType === constants.BSON_DATA_DECIMAL128) {
  215. // Buffer to contain the decimal bytes
  216. const bytes = Buffer.alloc(16);
  217. // Copy the next 16 bytes into the bytes buffer
  218. buffer.copy(bytes, 0, index, index + 16);
  219. // Update index
  220. index = index + 16;
  221. // Assign the new Decimal128 value
  222. const decimal128 = new Decimal128(bytes);
  223. // If we have an alternative mapper use that
  224. object[name] = decimal128.toObject ? decimal128.toObject() : decimal128;
  225. } else if (elementType === constants.BSON_DATA_BINARY) {
  226. let binarySize =
  227. buffer[index++] |
  228. (buffer[index++] << 8) |
  229. (buffer[index++] << 16) |
  230. (buffer[index++] << 24);
  231. const totalBinarySize = binarySize;
  232. const subType = buffer[index++];
  233. // Did we have a negative binary size, throw
  234. if (binarySize < 0) throw new Error('Negative binary type element size found');
  235. // Is the length longer than the document
  236. if (binarySize > Buffer.byteLength(buffer))
  237. throw new Error('Binary type size larger than document size');
  238. // Decode as raw Buffer object if options specifies it
  239. if (buffer['slice'] != null) {
  240. // If we have subtype 2 skip the 4 bytes for the size
  241. if (subType === Binary.SUBTYPE_BYTE_ARRAY) {
  242. binarySize =
  243. buffer[index++] |
  244. (buffer[index++] << 8) |
  245. (buffer[index++] << 16) |
  246. (buffer[index++] << 24);
  247. if (binarySize < 0)
  248. throw new Error('Negative binary type element size found for subtype 0x02');
  249. if (binarySize > totalBinarySize - 4)
  250. throw new Error('Binary type with subtype 0x02 contains to long binary size');
  251. if (binarySize < totalBinarySize - 4)
  252. throw new Error('Binary type with subtype 0x02 contains to short binary size');
  253. }
  254. if (promoteBuffers && promoteValues) {
  255. object[name] = buffer.slice(index, index + binarySize);
  256. } else {
  257. object[name] = new Binary(buffer.slice(index, index + binarySize), subType);
  258. }
  259. } else {
  260. const _buffer =
  261. typeof Uint8Array !== 'undefined'
  262. ? new Uint8Array(new ArrayBuffer(binarySize))
  263. : new Array(binarySize);
  264. // If we have subtype 2 skip the 4 bytes for the size
  265. if (subType === Binary.SUBTYPE_BYTE_ARRAY) {
  266. binarySize =
  267. buffer[index++] |
  268. (buffer[index++] << 8) |
  269. (buffer[index++] << 16) |
  270. (buffer[index++] << 24);
  271. if (binarySize < 0)
  272. throw new Error('Negative binary type element size found for subtype 0x02');
  273. if (binarySize > totalBinarySize - 4)
  274. throw new Error('Binary type with subtype 0x02 contains to long binary size');
  275. if (binarySize < totalBinarySize - 4)
  276. throw new Error('Binary type with subtype 0x02 contains to short binary size');
  277. }
  278. // Copy the data
  279. for (i = 0; i < binarySize; i++) {
  280. _buffer[i] = buffer[index + i];
  281. }
  282. if (promoteBuffers && promoteValues) {
  283. object[name] = _buffer;
  284. } else {
  285. object[name] = new Binary(_buffer, subType);
  286. }
  287. }
  288. // Update the index
  289. index = index + binarySize;
  290. } else if (elementType === constants.BSON_DATA_REGEXP && bsonRegExp === false) {
  291. // Get the start search index
  292. i = index;
  293. // Locate the end of the c string
  294. while (buffer[i] !== 0x00 && i < buffer.length) {
  295. i++;
  296. }
  297. // If are at the end of the buffer there is a problem with the document
  298. if (i >= buffer.length) throw new Error('Bad BSON Document: illegal CString');
  299. // Return the C string
  300. const source = buffer.toString('utf8', index, i);
  301. // Create the regexp
  302. index = i + 1;
  303. // Get the start search index
  304. i = index;
  305. // Locate the end of the c string
  306. while (buffer[i] !== 0x00 && i < buffer.length) {
  307. i++;
  308. }
  309. // If are at the end of the buffer there is a problem with the document
  310. if (i >= buffer.length) throw new Error('Bad BSON Document: illegal CString');
  311. // Return the C string
  312. const regExpOptions = buffer.toString('utf8', index, i);
  313. index = i + 1;
  314. // For each option add the corresponding one for javascript
  315. const optionsArray = new Array(regExpOptions.length);
  316. // Parse options
  317. for (i = 0; i < regExpOptions.length; i++) {
  318. switch (regExpOptions[i]) {
  319. case 'm':
  320. optionsArray[i] = 'm';
  321. break;
  322. case 's':
  323. optionsArray[i] = 'g';
  324. break;
  325. case 'i':
  326. optionsArray[i] = 'i';
  327. break;
  328. }
  329. }
  330. object[name] = new RegExp(source, optionsArray.join(''));
  331. } else if (elementType === constants.BSON_DATA_REGEXP && bsonRegExp === true) {
  332. // Get the start search index
  333. i = index;
  334. // Locate the end of the c string
  335. while (buffer[i] !== 0x00 && i < buffer.length) {
  336. i++;
  337. }
  338. // If are at the end of the buffer there is a problem with the document
  339. if (i >= buffer.length) throw new Error('Bad BSON Document: illegal CString');
  340. // Return the C string
  341. const source = buffer.toString('utf8', index, i);
  342. index = i + 1;
  343. // Get the start search index
  344. i = index;
  345. // Locate the end of the c string
  346. while (buffer[i] !== 0x00 && i < buffer.length) {
  347. i++;
  348. }
  349. // If are at the end of the buffer there is a problem with the document
  350. if (i >= buffer.length) throw new Error('Bad BSON Document: illegal CString');
  351. // Return the C string
  352. const regExpOptions = buffer.toString('utf8', index, i);
  353. index = i + 1;
  354. // Set the object
  355. object[name] = new BSONRegExp(source, regExpOptions);
  356. } else if (elementType === constants.BSON_DATA_SYMBOL) {
  357. const stringSize =
  358. buffer[index++] |
  359. (buffer[index++] << 8) |
  360. (buffer[index++] << 16) |
  361. (buffer[index++] << 24);
  362. if (
  363. stringSize <= 0 ||
  364. stringSize > buffer.length - index ||
  365. buffer[index + stringSize - 1] !== 0
  366. )
  367. throw new Error('bad string length in bson');
  368. // symbol is deprecated - upgrade to string.
  369. object[name] = buffer.toString('utf8', index, index + stringSize - 1);
  370. index = index + stringSize;
  371. } else if (elementType === constants.BSON_DATA_TIMESTAMP) {
  372. const lowBits =
  373. buffer[index++] |
  374. (buffer[index++] << 8) |
  375. (buffer[index++] << 16) |
  376. (buffer[index++] << 24);
  377. const highBits =
  378. buffer[index++] |
  379. (buffer[index++] << 8) |
  380. (buffer[index++] << 16) |
  381. (buffer[index++] << 24);
  382. object[name] = new Timestamp(lowBits, highBits);
  383. } else if (elementType === constants.BSON_DATA_MIN_KEY) {
  384. object[name] = new MinKey();
  385. } else if (elementType === constants.BSON_DATA_MAX_KEY) {
  386. object[name] = new MaxKey();
  387. } else if (elementType === constants.BSON_DATA_CODE) {
  388. const stringSize =
  389. buffer[index++] |
  390. (buffer[index++] << 8) |
  391. (buffer[index++] << 16) |
  392. (buffer[index++] << 24);
  393. if (
  394. stringSize <= 0 ||
  395. stringSize > buffer.length - index ||
  396. buffer[index + stringSize - 1] !== 0
  397. )
  398. throw new Error('bad string length in bson');
  399. const functionString = buffer.toString('utf8', index, index + stringSize - 1);
  400. // If we are evaluating the functions
  401. if (evalFunctions) {
  402. // If we have cache enabled let's look for the md5 of the function in the cache
  403. if (cacheFunctions) {
  404. const hash = cacheFunctionsCrc32 ? crc32(functionString) : functionString;
  405. // Got to do this to avoid V8 deoptimizing the call due to finding eval
  406. object[name] = isolateEvalWithHash(functionCache, hash, functionString, object);
  407. } else {
  408. object[name] = isolateEval(functionString);
  409. }
  410. } else {
  411. object[name] = new Code(functionString);
  412. }
  413. // Update parse index position
  414. index = index + stringSize;
  415. } else if (elementType === constants.BSON_DATA_CODE_W_SCOPE) {
  416. const totalSize =
  417. buffer[index++] |
  418. (buffer[index++] << 8) |
  419. (buffer[index++] << 16) |
  420. (buffer[index++] << 24);
  421. // Element cannot be shorter than totalSize + stringSize + documentSize + terminator
  422. if (totalSize < 4 + 4 + 4 + 1) {
  423. throw new Error('code_w_scope total size shorter minimum expected length');
  424. }
  425. // Get the code string size
  426. const stringSize =
  427. buffer[index++] |
  428. (buffer[index++] << 8) |
  429. (buffer[index++] << 16) |
  430. (buffer[index++] << 24);
  431. // Check if we have a valid string
  432. if (
  433. stringSize <= 0 ||
  434. stringSize > buffer.length - index ||
  435. buffer[index + stringSize - 1] !== 0
  436. )
  437. throw new Error('bad string length in bson');
  438. // Javascript function
  439. const functionString = buffer.toString('utf8', index, index + stringSize - 1);
  440. // Update parse index position
  441. index = index + stringSize;
  442. // Parse the element
  443. const _index = index;
  444. // Decode the size of the object document
  445. const objectSize =
  446. buffer[index] |
  447. (buffer[index + 1] << 8) |
  448. (buffer[index + 2] << 16) |
  449. (buffer[index + 3] << 24);
  450. // Decode the scope object
  451. const scopeObject = deserializeObject(buffer, _index, options, false);
  452. // Adjust the index
  453. index = index + objectSize;
  454. // Check if field length is to short
  455. if (totalSize < 4 + 4 + objectSize + stringSize) {
  456. throw new Error('code_w_scope total size is to short, truncating scope');
  457. }
  458. // Check if totalSize field is to long
  459. if (totalSize > 4 + 4 + objectSize + stringSize) {
  460. throw new Error('code_w_scope total size is to long, clips outer document');
  461. }
  462. // If we are evaluating the functions
  463. if (evalFunctions) {
  464. // If we have cache enabled let's look for the md5 of the function in the cache
  465. if (cacheFunctions) {
  466. const hash = cacheFunctionsCrc32 ? crc32(functionString) : functionString;
  467. // Got to do this to avoid V8 deoptimizing the call due to finding eval
  468. object[name] = isolateEvalWithHash(functionCache, hash, functionString, object);
  469. } else {
  470. object[name] = isolateEval(functionString);
  471. }
  472. object[name].scope = scopeObject;
  473. } else {
  474. object[name] = new Code(functionString, scopeObject);
  475. }
  476. } else if (elementType === constants.BSON_DATA_DBPOINTER) {
  477. // Get the code string size
  478. const stringSize =
  479. buffer[index++] |
  480. (buffer[index++] << 8) |
  481. (buffer[index++] << 16) |
  482. (buffer[index++] << 24);
  483. // Check if we have a valid string
  484. if (
  485. stringSize <= 0 ||
  486. stringSize > buffer.length - index ||
  487. buffer[index + stringSize - 1] !== 0
  488. )
  489. throw new Error('bad string length in bson');
  490. // Namespace
  491. if (!validateUtf8(buffer, index, index + stringSize - 1)) {
  492. throw new Error('Invalid UTF-8 string in BSON document');
  493. }
  494. const namespace = buffer.toString('utf8', index, index + stringSize - 1);
  495. // Update parse index position
  496. index = index + stringSize;
  497. // Read the oid
  498. const oidBuffer = Buffer.alloc(12);
  499. buffer.copy(oidBuffer, 0, index, index + 12);
  500. const oid = new ObjectId(oidBuffer);
  501. // Update the index
  502. index = index + 12;
  503. // Upgrade to DBRef type
  504. object[name] = new DBRef(namespace, oid);
  505. } else {
  506. throw new Error(
  507. 'Detected unknown BSON type ' +
  508. elementType.toString(16) +
  509. ' for fieldname "' +
  510. name +
  511. '", are you using the latest BSON parser?'
  512. );
  513. }
  514. }
  515. // Check if the deserialization was against a valid array/object
  516. if (size !== index - startIndex) {
  517. if (isArray) throw new Error('corrupt array bson');
  518. throw new Error('corrupt object bson');
  519. }
  520. // check if object's $ keys are those of a DBRef
  521. const dollarKeys = Object.keys(object).filter(k => k.startsWith('$'));
  522. let valid = true;
  523. dollarKeys.forEach(k => {
  524. if (['$ref', '$id', '$db'].indexOf(k) === -1) valid = false;
  525. });
  526. // if a $key not in "$ref", "$id", "$db", don't make a DBRef
  527. if (!valid) return object;
  528. if (object['$id'] != null && object['$ref'] != null) {
  529. let copy = Object.assign({}, object);
  530. delete copy.$ref;
  531. delete copy.$id;
  532. delete copy.$db;
  533. return new DBRef(object.$ref, object.$id, object.$db || null, copy);
  534. }
  535. return object;
  536. }
  537. /**
  538. * Ensure eval is isolated.
  539. *
  540. * @ignore
  541. * @api private
  542. */
  543. function isolateEvalWithHash(functionCache, hash, functionString, object) {
  544. // Contains the value we are going to set
  545. let value = null;
  546. // Check for cache hit, eval if missing and return cached function
  547. if (functionCache[hash] == null) {
  548. eval('value = ' + functionString);
  549. functionCache[hash] = value;
  550. }
  551. // Set the object
  552. return functionCache[hash].bind(object);
  553. }
  554. /**
  555. * Ensure eval is isolated.
  556. *
  557. * @ignore
  558. * @api private
  559. */
  560. function isolateEval(functionString) {
  561. // Contains the value we are going to set
  562. let value = null;
  563. // Eval the function
  564. eval('value = ' + functionString);
  565. return value;
  566. }
  567. module.exports = deserialize;