deserializer.js 29 KB

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