| 1 | 'use strict';
|
|---|
| 2 | var $ = require('../internals/export');
|
|---|
| 3 | var DESCRIPTORS = require('../internals/descriptors');
|
|---|
| 4 | var globalThis = require('../internals/global-this');
|
|---|
| 5 | var getBuiltIn = require('../internals/get-built-in');
|
|---|
| 6 | var uncurryThis = require('../internals/function-uncurry-this');
|
|---|
| 7 | var call = require('../internals/function-call');
|
|---|
| 8 | var isCallable = require('../internals/is-callable');
|
|---|
| 9 | var isObject = require('../internals/is-object');
|
|---|
| 10 | var isArray = require('../internals/is-array');
|
|---|
| 11 | var hasOwn = require('../internals/has-own-property');
|
|---|
| 12 | var toString = require('../internals/to-string');
|
|---|
| 13 | var lengthOfArrayLike = require('../internals/length-of-array-like');
|
|---|
| 14 | var createProperty = require('../internals/create-property');
|
|---|
| 15 | var fails = require('../internals/fails');
|
|---|
| 16 | var parseJSONString = require('../internals/parse-json-string');
|
|---|
| 17 | var NATIVE_SYMBOL = require('../internals/symbol-constructor-detection');
|
|---|
| 18 |
|
|---|
| 19 | var JSON = globalThis.JSON;
|
|---|
| 20 | var Number = globalThis.Number;
|
|---|
| 21 | var SyntaxError = globalThis.SyntaxError;
|
|---|
| 22 | var nativeParse = JSON && JSON.parse;
|
|---|
| 23 | var enumerableOwnProperties = getBuiltIn('Object', 'keys');
|
|---|
| 24 | // eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe
|
|---|
| 25 | var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
|
|---|
| 26 | var at = uncurryThis(''.charAt);
|
|---|
| 27 | var slice = uncurryThis(''.slice);
|
|---|
| 28 | var exec = uncurryThis(/./.exec);
|
|---|
| 29 | var push = uncurryThis([].push);
|
|---|
| 30 |
|
|---|
| 31 | var IS_DIGIT = /^\d$/;
|
|---|
| 32 | var IS_NON_ZERO_DIGIT = /^[1-9]$/;
|
|---|
| 33 | var IS_NUMBER_START = /^[\d-]$/;
|
|---|
| 34 | var IS_WHITESPACE = /^[\t\n\r ]$/;
|
|---|
| 35 |
|
|---|
| 36 | var PRIMITIVE = 0;
|
|---|
| 37 | var OBJECT = 1;
|
|---|
| 38 |
|
|---|
| 39 | var $parse = function (source, reviver) {
|
|---|
| 40 | source = toString(source);
|
|---|
| 41 | var context = new Context(source, 0, '');
|
|---|
| 42 | var root = context.parse();
|
|---|
| 43 | var value = root.value;
|
|---|
| 44 | var endIndex = context.skip(IS_WHITESPACE, root.end);
|
|---|
| 45 | if (endIndex < source.length) {
|
|---|
| 46 | throw new SyntaxError('Unexpected extra character: "' + at(source, endIndex) + '" after the parsed data at: ' + endIndex);
|
|---|
| 47 | }
|
|---|
| 48 | return isCallable(reviver) ? internalize({ '': value }, '', reviver, root) : value;
|
|---|
| 49 | };
|
|---|
| 50 |
|
|---|
| 51 | var internalize = function (holder, name, reviver, node) {
|
|---|
| 52 | var val = holder[name];
|
|---|
| 53 | var unmodified = node && val === node.value;
|
|---|
| 54 | var context = unmodified && typeof node.source == 'string' ? { source: node.source } : {};
|
|---|
| 55 | var elementRecordsLen, keys, len, i, P;
|
|---|
| 56 | if (isObject(val)) {
|
|---|
| 57 | var nodeIsArray = isArray(val);
|
|---|
| 58 | var nodes = unmodified ? node.nodes : nodeIsArray ? [] : {};
|
|---|
| 59 | if (nodeIsArray) {
|
|---|
| 60 | elementRecordsLen = nodes.length;
|
|---|
| 61 | len = lengthOfArrayLike(val);
|
|---|
| 62 | for (i = 0; i < len; i++) {
|
|---|
| 63 | internalizeProperty(val, i, internalize(val, '' + i, reviver, i < elementRecordsLen ? nodes[i] : undefined));
|
|---|
| 64 | }
|
|---|
| 65 | } else {
|
|---|
| 66 | keys = enumerableOwnProperties(val);
|
|---|
| 67 | len = lengthOfArrayLike(keys);
|
|---|
| 68 | for (i = 0; i < len; i++) {
|
|---|
| 69 | P = keys[i];
|
|---|
| 70 | internalizeProperty(val, P, internalize(val, P, reviver, hasOwn(nodes, P) ? nodes[P] : undefined));
|
|---|
| 71 | }
|
|---|
| 72 | }
|
|---|
| 73 | }
|
|---|
| 74 | return call(reviver, holder, name, val, context);
|
|---|
| 75 | };
|
|---|
| 76 |
|
|---|
| 77 | var internalizeProperty = function (object, key, value) {
|
|---|
| 78 | if (DESCRIPTORS) {
|
|---|
| 79 | var descriptor = getOwnPropertyDescriptor(object, key);
|
|---|
| 80 | if (descriptor && !descriptor.configurable) return;
|
|---|
| 81 | }
|
|---|
| 82 | if (value === undefined) delete object[key];
|
|---|
| 83 | else createProperty(object, key, value);
|
|---|
| 84 | };
|
|---|
| 85 |
|
|---|
| 86 | var Node = function (value, end, source, nodes) {
|
|---|
| 87 | this.value = value;
|
|---|
| 88 | this.end = end;
|
|---|
| 89 | this.source = source;
|
|---|
| 90 | this.nodes = nodes;
|
|---|
| 91 | };
|
|---|
| 92 |
|
|---|
| 93 | var Context = function (source, index) {
|
|---|
| 94 | this.source = source;
|
|---|
| 95 | this.index = index;
|
|---|
| 96 | };
|
|---|
| 97 |
|
|---|
| 98 | // https://www.json.org/json-en.html
|
|---|
| 99 | Context.prototype = {
|
|---|
| 100 | fork: function (nextIndex) {
|
|---|
| 101 | return new Context(this.source, nextIndex);
|
|---|
| 102 | },
|
|---|
| 103 | parse: function () {
|
|---|
| 104 | var source = this.source;
|
|---|
| 105 | var i = this.skip(IS_WHITESPACE, this.index);
|
|---|
| 106 | var fork = this.fork(i);
|
|---|
| 107 | var chr = at(source, i);
|
|---|
| 108 | if (exec(IS_NUMBER_START, chr)) return fork.number();
|
|---|
| 109 | switch (chr) {
|
|---|
| 110 | case '{':
|
|---|
| 111 | return fork.object();
|
|---|
| 112 | case '[':
|
|---|
| 113 | return fork.array();
|
|---|
| 114 | case '"':
|
|---|
| 115 | return fork.string();
|
|---|
| 116 | case 't':
|
|---|
| 117 | return fork.keyword(true);
|
|---|
| 118 | case 'f':
|
|---|
| 119 | return fork.keyword(false);
|
|---|
| 120 | case 'n':
|
|---|
| 121 | return fork.keyword(null);
|
|---|
| 122 | } throw new SyntaxError('Unexpected character: "' + chr + '" at: ' + i);
|
|---|
| 123 | },
|
|---|
| 124 | node: function (type, value, start, end, nodes) {
|
|---|
| 125 | return new Node(value, end, type ? null : slice(this.source, start, end), nodes);
|
|---|
| 126 | },
|
|---|
| 127 | object: function () {
|
|---|
| 128 | var source = this.source;
|
|---|
| 129 | var i = this.index + 1;
|
|---|
| 130 | var expectKeypair = false;
|
|---|
| 131 | var object = {};
|
|---|
| 132 | var nodes = {};
|
|---|
| 133 | var closed = false;
|
|---|
| 134 | while (i < source.length) {
|
|---|
| 135 | i = this.until(['"', '}'], i);
|
|---|
| 136 | if (at(source, i) === '}' && !expectKeypair) {
|
|---|
| 137 | i++;
|
|---|
| 138 | closed = true;
|
|---|
| 139 | break;
|
|---|
| 140 | }
|
|---|
| 141 | // Parsing the key
|
|---|
| 142 | var result = this.fork(i).string();
|
|---|
| 143 | var key = result.value;
|
|---|
| 144 | i = result.end;
|
|---|
| 145 | i = this.until([':'], i) + 1;
|
|---|
| 146 | // Parsing value
|
|---|
| 147 | i = this.skip(IS_WHITESPACE, i);
|
|---|
| 148 | result = this.fork(i).parse();
|
|---|
| 149 | createProperty(nodes, key, result);
|
|---|
| 150 | createProperty(object, key, result.value);
|
|---|
| 151 | i = this.until([',', '}'], result.end);
|
|---|
| 152 | var chr = at(source, i);
|
|---|
| 153 | if (chr === ',') {
|
|---|
| 154 | expectKeypair = true;
|
|---|
| 155 | i++;
|
|---|
| 156 | } else if (chr === '}') {
|
|---|
| 157 | i++;
|
|---|
| 158 | closed = true;
|
|---|
| 159 | break;
|
|---|
| 160 | }
|
|---|
| 161 | }
|
|---|
| 162 | if (!closed) throw new SyntaxError('Unterminated object at: ' + i);
|
|---|
| 163 | return this.node(OBJECT, object, this.index, i, nodes);
|
|---|
| 164 | },
|
|---|
| 165 | array: function () {
|
|---|
| 166 | var source = this.source;
|
|---|
| 167 | var i = this.index + 1;
|
|---|
| 168 | var expectElement = false;
|
|---|
| 169 | var array = [];
|
|---|
| 170 | var nodes = [];
|
|---|
| 171 | var closed = false;
|
|---|
| 172 | while (i < source.length) {
|
|---|
| 173 | i = this.skip(IS_WHITESPACE, i);
|
|---|
| 174 | if (at(source, i) === ']' && !expectElement) {
|
|---|
| 175 | i++;
|
|---|
| 176 | closed = true;
|
|---|
| 177 | break;
|
|---|
| 178 | }
|
|---|
| 179 | var result = this.fork(i).parse();
|
|---|
| 180 | push(nodes, result);
|
|---|
| 181 | push(array, result.value);
|
|---|
| 182 | i = this.until([',', ']'], result.end);
|
|---|
| 183 | if (at(source, i) === ',') {
|
|---|
| 184 | expectElement = true;
|
|---|
| 185 | i++;
|
|---|
| 186 | } else if (at(source, i) === ']') {
|
|---|
| 187 | i++;
|
|---|
| 188 | closed = true;
|
|---|
| 189 | break;
|
|---|
| 190 | }
|
|---|
| 191 | }
|
|---|
| 192 | if (!closed) throw new SyntaxError('Unterminated array at: ' + i);
|
|---|
| 193 | return this.node(OBJECT, array, this.index, i, nodes);
|
|---|
| 194 | },
|
|---|
| 195 | string: function () {
|
|---|
| 196 | var index = this.index;
|
|---|
| 197 | var parsed = parseJSONString(this.source, this.index + 1);
|
|---|
| 198 | return this.node(PRIMITIVE, parsed.value, index, parsed.end);
|
|---|
| 199 | },
|
|---|
| 200 | number: function () {
|
|---|
| 201 | var source = this.source;
|
|---|
| 202 | var startIndex = this.index;
|
|---|
| 203 | var i = startIndex;
|
|---|
| 204 | if (at(source, i) === '-') i++;
|
|---|
| 205 | if (at(source, i) === '0') i++;
|
|---|
| 206 | else if (exec(IS_NON_ZERO_DIGIT, at(source, i))) i = this.skip(IS_DIGIT, i + 1);
|
|---|
| 207 | else throw new SyntaxError('Failed to parse number at: ' + i);
|
|---|
| 208 | if (at(source, i) === '.') {
|
|---|
| 209 | var fractionStartIndex = i + 1;
|
|---|
| 210 | i = this.skip(IS_DIGIT, fractionStartIndex);
|
|---|
| 211 | if (fractionStartIndex === i) throw new SyntaxError("Failed to parse number's fraction at: " + i);
|
|---|
| 212 | }
|
|---|
| 213 | if (at(source, i) === 'e' || at(source, i) === 'E') {
|
|---|
| 214 | i++;
|
|---|
| 215 | if (at(source, i) === '+' || at(source, i) === '-') i++;
|
|---|
| 216 | var exponentStartIndex = i;
|
|---|
| 217 | i = this.skip(IS_DIGIT, i);
|
|---|
| 218 | if (exponentStartIndex === i) throw new SyntaxError("Failed to parse number's exponent value at: " + i);
|
|---|
| 219 | }
|
|---|
| 220 | return this.node(PRIMITIVE, Number(slice(source, startIndex, i)), startIndex, i);
|
|---|
| 221 | },
|
|---|
| 222 | keyword: function (value) {
|
|---|
| 223 | var keyword = '' + value;
|
|---|
| 224 | var index = this.index;
|
|---|
| 225 | var endIndex = index + keyword.length;
|
|---|
| 226 | if (slice(this.source, index, endIndex) !== keyword) throw new SyntaxError('Failed to parse value at: ' + index);
|
|---|
| 227 | return this.node(PRIMITIVE, value, index, endIndex);
|
|---|
| 228 | },
|
|---|
| 229 | skip: function (regex, i) {
|
|---|
| 230 | var source = this.source;
|
|---|
| 231 | for (; i < source.length; i++) if (!exec(regex, at(source, i))) break;
|
|---|
| 232 | return i;
|
|---|
| 233 | },
|
|---|
| 234 | until: function (array, i) {
|
|---|
| 235 | i = this.skip(IS_WHITESPACE, i);
|
|---|
| 236 | var chr = at(this.source, i);
|
|---|
| 237 | for (var j = 0; j < array.length; j++) if (array[j] === chr) return i;
|
|---|
| 238 | throw new SyntaxError('Unexpected character: "' + chr + '" at: ' + i);
|
|---|
| 239 | }
|
|---|
| 240 | };
|
|---|
| 241 |
|
|---|
| 242 | var NO_SOURCE_SUPPORT = fails(function () {
|
|---|
| 243 | var unsafeInt = '9007199254740993';
|
|---|
| 244 | var source;
|
|---|
| 245 | nativeParse(unsafeInt, function (key, value, context) {
|
|---|
| 246 | source = context.source;
|
|---|
| 247 | });
|
|---|
| 248 | return source !== unsafeInt;
|
|---|
| 249 | });
|
|---|
| 250 |
|
|---|
| 251 | var PROPER_BASE_PARSE = NATIVE_SYMBOL && !fails(function () {
|
|---|
| 252 | // Safari 9 bug
|
|---|
| 253 | return 1 / nativeParse('-0 \t') !== -Infinity;
|
|---|
| 254 | });
|
|---|
| 255 |
|
|---|
| 256 | // `JSON.parse` method
|
|---|
| 257 | // https://tc39.es/ecma262/#sec-json.parse
|
|---|
| 258 | // https://github.com/tc39/proposal-json-parse-with-source
|
|---|
| 259 | $({ target: 'JSON', stat: true, forced: NO_SOURCE_SUPPORT }, {
|
|---|
| 260 | parse: function parse(text, reviver) {
|
|---|
| 261 | return PROPER_BASE_PARSE && !isCallable(reviver) ? nativeParse(text) : $parse(text, reviver);
|
|---|
| 262 | }
|
|---|
| 263 | });
|
|---|