/src/moddable/xs/sources/xsJSON.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2016-2017 Moddable Tech, Inc. |
3 | | * |
4 | | * This file is part of the Moddable SDK Runtime. |
5 | | * |
6 | | * The Moddable SDK Runtime is free software: you can redistribute it and/or modify |
7 | | * it under the terms of the GNU Lesser General Public License as published by |
8 | | * the Free Software Foundation, either version 3 of the License, or |
9 | | * (at your option) any later version. |
10 | | * |
11 | | * The Moddable SDK Runtime is distributed in the hope that it will be useful, |
12 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | | * GNU Lesser General Public License for more details. |
15 | | * |
16 | | * You should have received a copy of the GNU Lesser General Public License |
17 | | * along with the Moddable SDK Runtime. If not, see <http://www.gnu.org/licenses/>. |
18 | | * |
19 | | * This file incorporates work covered by the following copyright and |
20 | | * permission notice: |
21 | | * |
22 | | * Copyright (C) 2010-2016 Marvell International Ltd. |
23 | | * Copyright (C) 2002-2010 Kinoma, Inc. |
24 | | * |
25 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
26 | | * you may not use this file except in compliance with the License. |
27 | | * You may obtain a copy of the License at |
28 | | * |
29 | | * http://www.apache.org/licenses/LICENSE-2.0 |
30 | | * |
31 | | * Unless required by applicable law or agreed to in writing, software |
32 | | * distributed under the License is distributed on an "AS IS" BASIS, |
33 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
34 | | * See the License for the specific language governing permissions and |
35 | | * limitations under the License. |
36 | | */ |
37 | | |
38 | | #include "xsAll.h" |
39 | | |
40 | | enum { |
41 | | XS_NO_JSON_TOKEN, |
42 | | XS_JSON_TOKEN_COLON, |
43 | | XS_JSON_TOKEN_COMMA, |
44 | | XS_JSON_TOKEN_EOF, |
45 | | XS_JSON_TOKEN_FALSE, |
46 | | XS_JSON_TOKEN_INTEGER, |
47 | | XS_JSON_TOKEN_LEFT_BRACE, |
48 | | XS_JSON_TOKEN_LEFT_BRACKET, |
49 | | XS_JSON_TOKEN_NULL, |
50 | | XS_JSON_TOKEN_NUMBER, |
51 | | XS_JSON_TOKEN_RIGHT_BRACE, |
52 | | XS_JSON_TOKEN_RIGHT_BRACKET, |
53 | | XS_JSON_TOKEN_STRING, |
54 | | XS_JSON_TOKEN_TRUE, |
55 | | }; |
56 | | |
57 | | typedef struct { |
58 | | txSlot* slot; |
59 | | txSize offset; |
60 | | txInteger integer; |
61 | | txNumber number; |
62 | | txSlot* string; |
63 | | txInteger token; |
64 | | txSlot* keys; |
65 | | txInteger line; |
66 | | txBoolean sourceFlag; |
67 | | txInteger sourceOffset; |
68 | | txInteger sourceSize; |
69 | | } txJSONParser; |
70 | | |
71 | | typedef struct { |
72 | | txString buffer; |
73 | | char indent[64]; |
74 | | txInteger indentLength; |
75 | | txInteger level; |
76 | | txSize offset; |
77 | | txSize size; |
78 | | txSlot* replacer; |
79 | | txSlot* keys; |
80 | | txSlot* stack; |
81 | | } txJSONStringifier; |
82 | | |
83 | | static void fxParseJSON(txMachine* the, txJSONParser* theParser); |
84 | | static void fxParseJSONArray(txMachine* the, txJSONParser* theParser); |
85 | | static void fxParseJSONObject(txMachine* the, txJSONParser* theParser); |
86 | | static void fxParseJSONToken(txMachine* the, txJSONParser* theParser); |
87 | | static void fxParseJSONValue(txMachine* the, txJSONParser* theParser); |
88 | | static void fxReviveJSON(txMachine* the, txJSONParser* theParser, txSlot* reviver); |
89 | | |
90 | | static void fxStringifyJSON(txMachine* the, txJSONStringifier* theStringifier); |
91 | | static void fxStringifyJSONCharacter(txMachine* the, txJSONStringifier* theStringifier, txInteger character); |
92 | | static void fxStringifyJSONChars(txMachine* the, txJSONStringifier* theStringifier, char* s, txSize theSize); |
93 | | static void fxStringifyJSONIndent(txMachine* the, txJSONStringifier* theStringifier); |
94 | | static void fxStringifyJSONInteger(txMachine* the, txJSONStringifier* theStringifier, txInteger theInteger); |
95 | | static void fxStringifyJSONName(txMachine* the, txJSONStringifier* theStringifier, txInteger* theFlag); |
96 | | static void fxStringifyJSONNumber(txMachine* the, txJSONStringifier* theStringifier, txNumber theNumber); |
97 | | static void fxStringifyJSONProperty(txMachine* the, txJSONStringifier* theStringifier, txInteger* theFlag); |
98 | | static void fxStringifyJSONString(txMachine* the, txJSONStringifier* theStringifier, txString theString); |
99 | | static void fxStringifyJSONUnicodeEscape(txMachine* the, txJSONStringifier* theStringifier, txInteger character); |
100 | | |
101 | | static txSlot* fxToJSONKeys(txMachine* the, txSlot* reference); |
102 | | |
103 | | void fxBuildJSON(txMachine* the) |
104 | 27.0k | { |
105 | 27.0k | txSlot* slot; |
106 | 27.0k | mxPush(mxObjectPrototype); |
107 | 27.0k | slot = fxLastProperty(the, fxNewObjectInstance(the)); |
108 | 27.0k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_JSON_parse), 2, mxID(_parse), XS_DONT_ENUM_FLAG); |
109 | 27.0k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_JSON_stringify), 3, mxID(_stringify), XS_DONT_ENUM_FLAG); |
110 | 27.0k | #if mxECMAScript2026 |
111 | 27.0k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_JSON_isRawJSON), 1, mxID(_isRawJSON), XS_DONT_ENUM_FLAG); |
112 | 27.0k | slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_JSON_rawJSON), 1, mxID(_rawJSON), XS_DONT_ENUM_FLAG); |
113 | 27.0k | #endif |
114 | 27.0k | slot = fxNextStringXProperty(the, slot, "JSON", mxID(_Symbol_toStringTag), XS_DONT_ENUM_FLAG | XS_DONT_SET_FLAG); |
115 | 27.0k | mxPull(mxJSONObject); |
116 | 27.0k | } |
117 | | |
118 | | #define mxIsRawJSON(THE_SLOT) \ |
119 | 18 | ((THE_SLOT) && ((THE_SLOT)->next) && ((THE_SLOT)->next->flag & XS_INTERNAL_FLAG) && ((THE_SLOT)->next->kind == XS_RAW_JSON_KIND)) |
120 | | |
121 | | void fx_JSON_isRawJSON(txMachine* the) |
122 | 29 | { |
123 | 29 | if (mxArgc < 1) |
124 | 0 | mxTypeError("no text"); |
125 | 29 | txSlot* slot = mxArgv(0); |
126 | 29 | mxResult->kind = XS_BOOLEAN_KIND; |
127 | 29 | mxResult->value.boolean = (mxIsReference(slot) && mxIsRawJSON(slot->value.reference)) ? 1 : 0; |
128 | 29 | } |
129 | | |
130 | | void fx_JSON_parse(txMachine* the) |
131 | 46.4k | { |
132 | 46.4k | volatile txJSONParser aParser = {0}; |
133 | 46.4k | if (mxArgc < 1) |
134 | 6.46k | mxSyntaxError("no buffer"); |
135 | 39.9k | fxToString(the, mxArgv(0)); |
136 | 39.9k | aParser.slot = mxArgv(0); |
137 | 39.9k | aParser.offset = 0; |
138 | 39.9k | mxPush(mxEmptyString); |
139 | 39.9k | aParser.string = the->stack; |
140 | 39.9k | aParser.line = 1; |
141 | 39.9k | if ((mxArgc > 1) && mxIsReference(mxArgv(1))) { |
142 | 711 | if (fxIsArray(the, mxArgv(1)->value.reference)) |
143 | 13 | aParser.keys = fxToJSONKeys(the, mxArgv(1)); |
144 | 698 | else if (mxIsCallable(mxArgv(1)->value.reference)) |
145 | 467 | aParser.sourceFlag = 1; |
146 | 711 | } |
147 | 39.9k | fxParseJSON(the, (txJSONParser*)&aParser); |
148 | 39.9k | if (aParser.sourceFlag) { |
149 | 460 | txSlot* valueReference = the->stack + 1; |
150 | 460 | txSlot* sourceReference = the->stack; |
151 | 460 | txSlot* instance; |
152 | 460 | txID id; |
153 | 460 | mxPush(mxObjectPrototype); |
154 | 460 | instance = fxNewObjectInstance(the); |
155 | 460 | id = fxID(the, ""); |
156 | 460 | mxBehaviorDefineOwnProperty(the, instance, id, 0, valueReference, XS_GET_ONLY); |
157 | 460 | mxPushSlot(mxArgv(1)); |
158 | 460 | mxCall(); |
159 | 460 | mxPushUndefined(); |
160 | 460 | fxKeyAt(the, id, 0, the->stack); |
161 | 460 | mxPushSlot(valueReference); |
162 | 460 | mxPushSlot(sourceReference); |
163 | 460 | fxReviveJSON(the, (txJSONParser*)&aParser, mxArgv(1)); |
164 | 460 | } |
165 | 39.9k | mxPullSlot(mxResult); |
166 | 39.9k | } |
167 | | |
168 | | void fxParseJSON(txMachine* the, txJSONParser* theParser) |
169 | 40.0k | { |
170 | 40.0k | fxParseJSONToken(the, theParser); |
171 | 40.0k | fxParseJSONValue(the, theParser); |
172 | 40.0k | if (theParser->token != XS_JSON_TOKEN_EOF) |
173 | 3.64k | mxSyntaxError("%ld: missing EOF", theParser->line); |
174 | 40.0k | } |
175 | | |
176 | | void fxParseJSONArray(txMachine* the, txJSONParser* theParser) |
177 | 8.80M | { |
178 | 8.80M | txSlot* sourceArray = NULL; |
179 | 8.80M | txSlot* sourceItem; |
180 | 8.80M | txSlot* valueArray; |
181 | 8.80M | txSlot* valueItem; |
182 | 8.80M | txIndex length; |
183 | | |
184 | 8.80M | mxCheckCStack(); |
185 | 8.80M | fxParseJSONToken(the, theParser); |
186 | 8.80M | mxPush(mxArrayPrototype); |
187 | 8.80M | valueArray = fxNewArrayInstance(the); |
188 | 8.80M | valueItem = fxLastProperty(the, valueArray); |
189 | 8.80M | if (theParser->sourceFlag) { |
190 | 108 | mxPush(mxArrayPrototype); |
191 | 108 | sourceArray = fxNewArrayInstance(the); |
192 | 108 | sourceItem = fxLastProperty(the, sourceArray); |
193 | 108 | } |
194 | 8.80M | length = 0; |
195 | 23.5M | for (;;) { |
196 | 23.5M | if (theParser->token == XS_JSON_TOKEN_RIGHT_BRACKET) |
197 | 7.12M | break; |
198 | 16.4M | if (length) { |
199 | 10.8M | if (theParser->token == XS_JSON_TOKEN_COMMA) |
200 | 10.8M | fxParseJSONToken(the, theParser); |
201 | 1.69k | else |
202 | 1.69k | mxSyntaxError("%ld: missing ,", theParser->line); |
203 | 10.8M | } |
204 | 16.4M | fxParseJSONValue(the, theParser); |
205 | 16.4M | length++; |
206 | 16.4M | if (theParser->sourceFlag) { |
207 | 210 | sourceItem->next = fxNewSlot(the); |
208 | 210 | sourceItem = sourceItem->next; |
209 | 210 | sourceItem->kind = the->stack->kind; |
210 | 210 | sourceItem->value = the->stack->value; |
211 | 210 | mxPop(); |
212 | 210 | } |
213 | 16.4M | valueItem->next = fxNewSlot(the); |
214 | 16.4M | valueItem = valueItem->next; |
215 | 16.4M | valueItem->kind = the->stack->kind; |
216 | 16.4M | valueItem->value = the->stack->value; |
217 | 16.4M | mxPop(); |
218 | 16.4M | } |
219 | 8.80M | valueArray->next->value.array.length = length; |
220 | 8.80M | fxCacheArray(the, valueArray); |
221 | 8.80M | if (theParser->sourceFlag) { |
222 | 104 | sourceArray->next->value.array.length = length; |
223 | 104 | fxCacheArray(the, sourceArray); |
224 | 104 | } |
225 | 8.80M | fxParseJSONToken(the, theParser); |
226 | 8.80M | } |
227 | | |
228 | | void fxParseJSONToken(txMachine* the, txJSONParser* theParser) |
229 | 44.2M | { |
230 | 44.2M | txInteger character; |
231 | 44.2M | txBoolean escaped; |
232 | 44.2M | txNumber number; |
233 | 44.2M | txSize offset; |
234 | 44.2M | txSize size; |
235 | 44.2M | txString p, s; |
236 | | |
237 | 44.2M | theParser->integer = 0; |
238 | 44.2M | theParser->number = 0; |
239 | 44.2M | theParser->string->value.string = mxEmptyString.value.string; |
240 | 44.2M | theParser->string->kind = mxEmptyString.kind; |
241 | 44.2M | theParser->token = XS_NO_JSON_TOKEN; |
242 | 44.2M | p = theParser->slot->value.string + theParser->offset; |
243 | 89.5M | while (theParser->token == XS_NO_JSON_TOKEN) { |
244 | 45.2M | switch (*p) { |
245 | 11.6k | case 0: |
246 | 11.6k | theParser->token = XS_JSON_TOKEN_EOF; |
247 | 11.6k | break; |
248 | 115k | case 10: |
249 | 115k | p++; |
250 | 115k | theParser->line++; |
251 | 115k | break; |
252 | 816 | case 13: |
253 | 816 | p++; |
254 | 816 | theParser->line++; |
255 | 816 | if (*p == 10) |
256 | 230 | p++; |
257 | 816 | break; |
258 | 7.42k | case '\t': |
259 | 876k | case ' ': |
260 | 876k | p++; |
261 | 876k | break; |
262 | 17.7k | case '-': |
263 | 91.4k | case '0': |
264 | 202k | case '1': |
265 | 283k | case '2': |
266 | 302k | case '3': |
267 | 440k | case '4': |
268 | 613k | case '5': |
269 | 749k | case '6': |
270 | 8.44M | case '7': |
271 | 9.35M | case '8': |
272 | 9.55M | case '9': |
273 | 9.55M | s = p; |
274 | 9.55M | if (*p == '-') |
275 | 17.7k | p++; |
276 | 9.55M | if (('0' <= *p) && (*p <= '9')) { |
277 | 9.55M | if (*p == '0') { |
278 | 74.7k | p++; |
279 | 74.7k | } |
280 | 9.48M | else { |
281 | 9.48M | p++; |
282 | 10.0M | while (('0' <= *p) && (*p <= '9')) |
283 | 565k | p++; |
284 | 9.48M | } |
285 | 9.55M | if (*p == '.') { |
286 | 19.2k | p++; |
287 | 19.2k | if (('0' <= *p) && (*p <= '9')) { |
288 | 18.5k | p++; |
289 | 149k | while (('0' <= *p) && (*p <= '9')) |
290 | 130k | p++; |
291 | 18.5k | } |
292 | 667 | else |
293 | 667 | goto error; |
294 | 19.2k | } |
295 | 9.55M | if ((*p == 'e') || (*p == 'E')) { |
296 | 23.8k | p++; |
297 | 23.8k | if ((*p == '+') || (*p == '-')) |
298 | 8.88k | p++; |
299 | 23.8k | if (('0' <= *p) && (*p <= '9')) { |
300 | 22.3k | p++; |
301 | 62.6k | while (('0' <= *p) && (*p <= '9')) |
302 | 40.3k | p++; |
303 | 22.3k | } |
304 | 1.51k | else |
305 | 1.51k | goto error; |
306 | 23.8k | } |
307 | 9.55M | } |
308 | 1.47k | else |
309 | 1.47k | goto error; |
310 | 9.55M | size = mxPtrDiff(p - s); |
311 | 9.55M | if (theParser->sourceFlag) { |
312 | 131 | theParser->sourceOffset = mxPtrDiff(s - theParser->slot->value.string); |
313 | 131 | theParser->sourceSize = size; |
314 | 131 | } |
315 | 9.55M | if ((size_t)(size + 1) > sizeof(the->nameBuffer)) |
316 | 6 | mxSyntaxError("%ld: number overflow", theParser->line); |
317 | 9.55M | c_memcpy(the->nameBuffer, s, size); |
318 | 9.55M | the->nameBuffer[size] = 0; |
319 | 9.55M | theParser->number = fxStringToNumber(the, the->nameBuffer, 0); |
320 | 9.55M | theParser->integer = (txInteger)theParser->number; |
321 | 9.55M | number = theParser->integer; |
322 | 9.55M | if ((theParser->number == number) && (theParser->number != -0)) |
323 | 9.43M | theParser->token = XS_JSON_TOKEN_INTEGER; |
324 | 120k | else |
325 | 120k | theParser->token = XS_JSON_TOKEN_NUMBER; |
326 | 9.55M | break; |
327 | 13.0M | case ',': |
328 | 13.0M | p++; |
329 | 13.0M | theParser->token = XS_JSON_TOKEN_COMMA; |
330 | 13.0M | break; |
331 | 2.50M | case ':': |
332 | 2.50M | p++; |
333 | 2.50M | theParser->token = XS_JSON_TOKEN_COLON; |
334 | 2.50M | break; |
335 | 8.80M | case '[': |
336 | 8.80M | p++; |
337 | 8.80M | theParser->token = XS_JSON_TOKEN_LEFT_BRACKET; |
338 | 8.80M | break; |
339 | 7.12M | case ']': |
340 | 7.12M | p++; |
341 | 7.12M | theParser->token = XS_JSON_TOKEN_RIGHT_BRACKET; |
342 | 7.12M | break; |
343 | 249k | case '{': |
344 | 249k | p++; |
345 | 249k | theParser->token = XS_JSON_TOKEN_LEFT_BRACE; |
346 | 249k | break; |
347 | 62.2k | case '}': |
348 | 62.2k | p++; |
349 | 62.2k | theParser->token = XS_JSON_TOKEN_RIGHT_BRACE; |
350 | 62.2k | break; |
351 | 2.82M | case '"': |
352 | 2.82M | s = p; |
353 | 2.82M | p++; |
354 | 2.82M | escaped = 0; |
355 | 2.82M | offset = mxPtrDiff(p - theParser->slot->value.string); |
356 | 2.82M | size = 0; |
357 | 67.1M | for (;;) { |
358 | 67.1M | p = mxStringByteDecode(p, &character); |
359 | 67.1M | if (character < 32) { |
360 | 1.24k | goto error; |
361 | 1.24k | } |
362 | 67.1M | else if (character == '"') { |
363 | 2.81M | break; |
364 | 2.81M | } |
365 | 64.3M | else if (character == '\\') { |
366 | 88.2k | escaped = 1; |
367 | 88.2k | switch (*p) { |
368 | 1.77k | case '"': |
369 | 2.35k | case '/': |
370 | 3.06k | case '\\': |
371 | 5.83k | case 'b': |
372 | 7.60k | case 'f': |
373 | 8.80k | case 'n': |
374 | 76.9k | case 'r': |
375 | 78.2k | case 't': |
376 | 78.2k | p++; |
377 | 78.2k | size++; |
378 | 78.2k | break; |
379 | 9.28k | case 'u': |
380 | 9.28k | p++; |
381 | 9.28k | if (fxParseUnicodeEscape(&p, &character, 0, '\\')) |
382 | 8.43k | size += mxStringByteLength(character); |
383 | 854 | else |
384 | 854 | goto error; |
385 | 8.43k | break; |
386 | 8.43k | default: |
387 | 704 | goto error; |
388 | 88.2k | } |
389 | 88.2k | } |
390 | 64.2M | else { |
391 | 64.2M | size += mxStringByteLength(character); |
392 | 64.2M | } |
393 | 67.1M | } |
394 | 2.81M | if (theParser->sourceFlag) { |
395 | 24 | theParser->sourceOffset = mxPtrDiff(s - theParser->slot->value.string); |
396 | 24 | theParser->sourceSize = mxPtrDiff(p - s); |
397 | 24 | } |
398 | 2.81M | s = theParser->string->value.string = fxNewChunk(the, size + 1); |
399 | 2.81M | theParser->string->kind = XS_STRING_KIND; |
400 | 2.81M | p = theParser->slot->value.string + offset; |
401 | 2.81M | if (escaped) { |
402 | 14.7M | for (;;) { |
403 | 14.7M | if (*p == '"') { |
404 | 17.4k | p++; |
405 | 17.4k | *s = 0; |
406 | 17.4k | break; |
407 | 17.4k | } |
408 | 14.7M | else if (*p == '\\') { |
409 | 85.0k | p++; |
410 | 85.0k | switch (*p) { |
411 | 1.56k | case '"': |
412 | 1.95k | case '/': |
413 | 2.46k | case '\\': |
414 | 2.46k | *s++ = *p++; |
415 | 2.46k | break; |
416 | 2.57k | case 'b': |
417 | 2.57k | p++; |
418 | 2.57k | *s++ = '\b'; |
419 | 2.57k | break; |
420 | 1.58k | case 'f': |
421 | 1.58k | p++; |
422 | 1.58k | *s++ = '\f'; |
423 | 1.58k | break; |
424 | 1.00k | case 'n': |
425 | 1.00k | p++; |
426 | 1.00k | *s++ = '\n'; |
427 | 1.00k | break; |
428 | 67.9k | case 'r': |
429 | 67.9k | p++; |
430 | 67.9k | *s++ = '\r'; |
431 | 67.9k | break; |
432 | 1.09k | case 't': |
433 | 1.09k | p++; |
434 | 1.09k | *s++ = '\t'; |
435 | 1.09k | break; |
436 | 8.20k | case 'u': |
437 | 8.20k | p++; |
438 | 8.20k | fxParseUnicodeEscape(&p, &character, 0, '\\'); |
439 | 8.20k | s = mxStringByteEncode(s, character); |
440 | 8.20k | break; |
441 | 85.0k | } |
442 | 85.0k | } |
443 | 14.6M | else { |
444 | 14.6M | *s++ = *p++; |
445 | 14.6M | } |
446 | 14.7M | } |
447 | 17.4k | } |
448 | 2.80M | else { |
449 | 2.80M | c_memcpy(s, p, size); |
450 | 2.80M | p += size + 1; |
451 | 2.80M | s[size] = 0; |
452 | 2.80M | } |
453 | 2.81M | theParser->token = XS_JSON_TOKEN_STRING; |
454 | 2.81M | break; |
455 | 4.30k | case 'f': |
456 | 4.30k | s = p; |
457 | 4.30k | p++; |
458 | 4.30k | if (*p != 'a') goto error; |
459 | 3.80k | p++; |
460 | 3.80k | if (*p != 'l') goto error; |
461 | 3.75k | p++; |
462 | 3.75k | if (*p != 's') goto error; |
463 | 3.15k | p++; |
464 | 3.15k | if (*p != 'e') goto error; |
465 | 2.31k | p++; |
466 | 2.31k | if (theParser->sourceFlag) { |
467 | 236 | theParser->sourceOffset = mxPtrDiff(s - theParser->slot->value.string); |
468 | 236 | theParser->sourceSize = mxPtrDiff(p - s); |
469 | 236 | } |
470 | 2.31k | theParser->token = XS_JSON_TOKEN_FALSE; |
471 | 2.31k | break; |
472 | 1.72k | case 'n': |
473 | 1.72k | s = p; |
474 | 1.72k | p++; |
475 | 1.72k | if (*p != 'u') goto error; |
476 | 1.40k | p++; |
477 | 1.40k | if (*p != 'l') goto error; |
478 | 662 | p++; |
479 | 662 | if (*p != 'l') goto error; |
480 | 570 | p++; |
481 | 570 | if (theParser->sourceFlag) { |
482 | 198 | theParser->sourceOffset = mxPtrDiff(s - theParser->slot->value.string); |
483 | 198 | theParser->sourceSize = mxPtrDiff(p - s); |
484 | 198 | } |
485 | 570 | theParser->token = XS_JSON_TOKEN_NULL; |
486 | 570 | break; |
487 | 40.0k | case 't': |
488 | 40.0k | s = p; |
489 | 40.0k | p++; |
490 | 40.0k | if (*p != 'r') goto error; |
491 | 38.7k | p++; |
492 | 38.7k | if (*p != 'u') goto error; |
493 | 38.0k | p++; |
494 | 38.0k | if (*p != 'e') goto error; |
495 | 37.5k | p++; |
496 | 37.5k | if (theParser->sourceFlag) { |
497 | 21 | theParser->sourceOffset = mxPtrDiff(s - theParser->slot->value.string); |
498 | 21 | theParser->sourceSize = mxPtrDiff(p - s); |
499 | 21 | } |
500 | 37.5k | theParser->token = XS_JSON_TOKEN_TRUE; |
501 | 37.5k | break; |
502 | 8.85k | default: |
503 | 20.9k | error: |
504 | 20.9k | mxSyntaxError("%ld: invalid character", theParser->line); |
505 | 0 | break; |
506 | 45.2M | } |
507 | 45.2M | } |
508 | 44.2M | theParser->offset = mxPtrDiff(p - theParser->slot->value.string); |
509 | 44.2M | } |
510 | | |
511 | | void fxParseJSONObject(txMachine* the, txJSONParser* theParser) |
512 | 249k | { |
513 | 249k | txSlot* sourceObject = NULL; |
514 | 249k | txSlot* valueObject; |
515 | 249k | txBoolean comma = 0; |
516 | 249k | txSlot* at; |
517 | 249k | txIndex index; |
518 | 249k | txID id; |
519 | 249k | txSlot* property; |
520 | | |
521 | 249k | mxCheckCStack(); |
522 | 249k | fxParseJSONToken(the, theParser); |
523 | 249k | mxPush(mxObjectPrototype); |
524 | 249k | valueObject = fxNewObjectInstance(the); |
525 | 249k | if (theParser->sourceFlag) { |
526 | 9 | mxPush(mxObjectPrototype); |
527 | 9 | sourceObject = fxNewObjectInstance(the); |
528 | 9 | } |
529 | 2.56M | for (;;) { |
530 | 2.56M | if (theParser->token == XS_JSON_TOKEN_RIGHT_BRACE) |
531 | 62.1k | break; |
532 | 2.50M | if (comma) { |
533 | 2.26M | if (theParser->token == XS_JSON_TOKEN_COMMA) |
534 | 2.26M | fxParseJSONToken(the, theParser); |
535 | 495 | else |
536 | 495 | mxSyntaxError("%ld: missing ,", theParser->line); |
537 | 2.26M | } |
538 | 2.50M | if (theParser->token != XS_JSON_TOKEN_STRING) |
539 | 630 | mxSyntaxError("%ld: missing name", theParser->line); |
540 | 2.50M | mxPushString(theParser->string->value.string); |
541 | 2.50M | at = the->stack; |
542 | 2.50M | index = 0; |
543 | 2.50M | if (theParser->keys) { |
544 | 26 | at->kind = XS_UNDEFINED_KIND; |
545 | 26 | if (fxStringToIndex(the, at->value.string, &index)) |
546 | 4 | id = 0; |
547 | 22 | else |
548 | 22 | id = fxFindName(the, at->value.string); |
549 | 26 | if (id != XS_NO_ID) { |
550 | 5 | txSlot* item = theParser->keys->value.reference->next; |
551 | 12 | while (item) { |
552 | 9 | if ((item->value.at.id == id) && (item->value.at.index == index)) { |
553 | 2 | at->value.at.id = id; |
554 | 2 | at->value.at.index = index; |
555 | 2 | at->kind = XS_AT_KIND; |
556 | 2 | break; |
557 | 2 | } |
558 | 7 | item = item->next; |
559 | 7 | } |
560 | 5 | } |
561 | 26 | } |
562 | 2.50M | else { |
563 | 2.50M | if (fxStringToIndex(the, at->value.string, &index)) |
564 | 2.02M | id = 0; |
565 | 477k | else |
566 | 477k | id = fxNewName(the, at); |
567 | 2.50M | at->value.at.id = id; |
568 | 2.50M | at->value.at.index = index; |
569 | 2.50M | at->kind = XS_AT_KIND; |
570 | 2.50M | } |
571 | 2.50M | fxParseJSONToken(the, theParser); |
572 | 2.50M | if (theParser->token != XS_JSON_TOKEN_COLON) |
573 | 2.76k | mxSyntaxError("%ld: missing :", theParser->line); |
574 | 2.50M | fxParseJSONToken(the, theParser); |
575 | 2.50M | fxParseJSONValue(the, theParser); |
576 | 2.50M | if (theParser->sourceFlag) { |
577 | 23 | property = mxBehaviorSetProperty(the, sourceObject, at->value.at.id, at->value.at.index, XS_OWN); |
578 | 23 | property->kind = the->stack->kind; |
579 | 23 | property->value = the->stack->value; |
580 | 23 | mxPop(); // source |
581 | 23 | } |
582 | 2.50M | if ((at->kind == XS_AT_KIND) && (the->stack->kind != XS_UNDEFINED_KIND)) { |
583 | 2.32M | property = mxBehaviorSetProperty(the, valueObject, at->value.at.id, at->value.at.index, XS_OWN); |
584 | 2.32M | property->kind = the->stack->kind; |
585 | 2.32M | property->value = the->stack->value; |
586 | 2.32M | } |
587 | 2.50M | mxPop(); // value |
588 | 2.50M | mxPop(); // at |
589 | 2.50M | comma = 1; |
590 | 2.50M | } |
591 | 245k | fxParseJSONToken(the, theParser); |
592 | 245k | } |
593 | | |
594 | | void fxParseJSONValue(txMachine* the, txJSONParser* theParser) |
595 | 18.9M | { |
596 | 18.9M | if (theParser->token == XS_JSON_TOKEN_LEFT_BRACE) |
597 | 249k | fxParseJSONObject(the, theParser); |
598 | 18.7M | else if (theParser->token == XS_JSON_TOKEN_LEFT_BRACKET) |
599 | 8.80M | fxParseJSONArray(the, theParser); |
600 | 9.90M | else { |
601 | 9.90M | switch (theParser->token) { |
602 | 2.10k | case XS_JSON_TOKEN_FALSE: |
603 | 2.10k | mxPushBoolean(0); |
604 | 2.10k | break; |
605 | 37.5k | case XS_JSON_TOKEN_TRUE: |
606 | 37.5k | mxPushBoolean(1); |
607 | 37.5k | break; |
608 | 564 | case XS_JSON_TOKEN_NULL: |
609 | 564 | mxPushNull(); |
610 | 564 | break; |
611 | 9.42M | case XS_JSON_TOKEN_INTEGER: |
612 | 9.42M | mxPushInteger(theParser->integer); |
613 | 9.42M | break; |
614 | 120k | case XS_JSON_TOKEN_NUMBER: |
615 | 120k | mxPushNumber(theParser->number); |
616 | 120k | break; |
617 | 311k | case XS_JSON_TOKEN_STRING: |
618 | 311k | mxPushString(theParser->string->value.string); |
619 | 311k | break; |
620 | 1.24k | default: |
621 | 1.24k | mxPushUndefined(); |
622 | 1.24k | mxSyntaxError("%ld: invalid value", theParser->line); |
623 | 0 | break; |
624 | 9.90M | } |
625 | 9.90M | if (theParser->sourceFlag) { |
626 | 585 | txSlot* value = the->stack; |
627 | 585 | txSlot* list; |
628 | 585 | txSlot* slot; |
629 | 585 | mxPushList(); |
630 | 585 | list = the->stack; |
631 | 585 | slot = list->value.list.first = fxNewSlot(the); |
632 | 585 | slot->kind = XS_DATA_VIEW_KIND; |
633 | 585 | slot->value.dataView.offset = theParser->sourceOffset; |
634 | 585 | slot->value.dataView.size = theParser->sourceSize; |
635 | 585 | slot = slot->next = list->value.list.last = fxNewSlot(the); |
636 | 585 | slot->kind = value->kind; |
637 | 585 | slot->value = value->value; |
638 | 585 | } |
639 | 9.90M | fxParseJSONToken(the, theParser); |
640 | 9.90M | } |
641 | 18.9M | } |
642 | | |
643 | | void fxReviveJSON(txMachine* the, txJSONParser* theParser, txSlot* reviver) |
644 | 14.2k | { |
645 | 14.2k | txSlot* valueReference = the->stack + 1; |
646 | 14.2k | txSlot* sourceReference = the->stack; |
647 | 14.2k | mxCheckCStack(); |
648 | 14.2k | if (mxIsReference(valueReference)) { |
649 | 13.6k | txSlot* instance = valueReference->value.reference; |
650 | 13.6k | if (fxIsArray(the, instance)) { |
651 | 3.62k | txIndex length, index; |
652 | 3.62k | mxPushSlot(valueReference); |
653 | 3.62k | mxGetID(mxID(_length)); |
654 | 3.62k | length = (txIndex)fxToLength(the, the->stack); |
655 | 3.62k | mxPop(); |
656 | 3.62k | index = 0; |
657 | 7.25k | while (index < length) { |
658 | 3.63k | mxPushSlot(valueReference); |
659 | 3.63k | mxPushSlot(reviver); |
660 | 3.63k | mxCall(); |
661 | 3.63k | mxPushUndefined(); |
662 | 3.63k | fxKeyAt(the, 0, index, the->stack); |
663 | 3.63k | mxPushSlot(valueReference); |
664 | 3.63k | mxGetIndex(index); |
665 | 3.63k | if (mxIsReference(sourceReference)) { |
666 | 203 | mxPushSlot(sourceReference); |
667 | 203 | mxGetIndex(index); |
668 | 203 | } |
669 | 3.43k | else |
670 | 3.43k | mxPushUndefined(); |
671 | 3.63k | fxReviveJSON(the, theParser, reviver); |
672 | 3.63k | if (mxIsUndefined(the->stack)) { |
673 | 4 | mxBehaviorDeleteProperty(the, valueReference->value.reference, 0, index); |
674 | 4 | } |
675 | 3.63k | else { |
676 | 3.63k | mxBehaviorDefineOwnProperty(the, valueReference->value.reference, 0, index, the->stack, XS_GET_ONLY); |
677 | 3.63k | } |
678 | 3.63k | mxPop(); |
679 | 3.63k | index++; |
680 | 3.63k | } |
681 | 3.62k | } |
682 | 10.0k | else { |
683 | 10.0k | txSlot* at = fxNewInstance(the); |
684 | 10.0k | mxBehaviorOwnKeys(the, instance, XS_EACH_NAME_FLAG, at); |
685 | 20.1k | while ((at = at->next)) { |
686 | 10.1k | mxPushSlot(valueReference); |
687 | 10.1k | mxPushSlot(reviver); |
688 | 10.1k | mxCall(); |
689 | 10.1k | mxPushUndefined(); |
690 | 10.1k | fxKeyAt(the, at->value.at.id, at->value.at.index, the->stack); |
691 | 10.1k | mxPushSlot(valueReference); |
692 | 10.1k | mxGetAll(at->value.at.id, at->value.at.index); |
693 | 10.1k | if (mxIsReference(sourceReference)) { |
694 | 16 | mxPushSlot(sourceReference); |
695 | 16 | mxGetAll(at->value.at.id, at->value.at.index); |
696 | 16 | } |
697 | 10.1k | else |
698 | 10.1k | mxPushUndefined(); |
699 | 10.1k | fxReviveJSON(the, theParser, reviver); |
700 | 10.1k | if (mxIsUndefined(the->stack)) { |
701 | 137 | mxBehaviorDeleteProperty(the, valueReference->value.reference, at->value.at.id, at->value.at.index); |
702 | 137 | } |
703 | 9.99k | else { |
704 | 9.99k | mxBehaviorDefineOwnProperty(the, valueReference->value.reference, at->value.at.id, at->value.at.index, the->stack, XS_GET_ONLY); |
705 | 9.99k | } |
706 | 10.1k | mxPop(); |
707 | 10.1k | } |
708 | 10.0k | mxPop(); |
709 | 10.0k | } |
710 | 13.6k | } |
711 | 14.2k | if ((sourceReference->kind == XS_LIST_KIND) && fxIsSameValue(the, valueReference, sourceReference->value.list.last, 0)) { |
712 | 474 | txSlot* view = sourceReference->value.list.first; |
713 | 474 | txInteger offset = view->value.dataView.offset; |
714 | 474 | txInteger size = view->value.dataView.size; |
715 | 474 | txSlot* instance; |
716 | 474 | txSlot* source; |
717 | 474 | mxPop(); |
718 | 474 | mxPush(mxObjectPrototype); |
719 | 474 | instance = fxNewObjectInstance(the); |
720 | 474 | source = instance->next = fxNewSlot(the); |
721 | 474 | source->value.string = fxNewChunk(the, size + 1); |
722 | 474 | c_memcpy(source->value.string, theParser->slot->value.string + offset, size); |
723 | 474 | source->value.string[size] = 0; |
724 | 474 | source->kind = XS_STRING_KIND; |
725 | 474 | source->ID = mxID(_source); |
726 | 474 | } |
727 | 13.7k | else { |
728 | 13.7k | mxPop(); |
729 | 13.7k | mxPush(mxObjectPrototype); |
730 | 13.7k | fxNewObjectInstance(the); |
731 | 13.7k | } |
732 | 14.2k | mxRunCount(3); |
733 | 14.2k | } |
734 | | |
735 | | void fx_JSON_rawJSON(txMachine* the) |
736 | 82 | { |
737 | 82 | txSlot* slot; |
738 | 82 | txString string; |
739 | 82 | txSize length; |
740 | 82 | txSlot* instance; |
741 | 82 | txSlot* property; |
742 | 82 | volatile txJSONParser aParser = {0}; |
743 | 82 | if (mxArgc > 0) |
744 | 82 | mxPushSlot(mxArgv(0)); |
745 | 0 | else |
746 | 0 | mxPushUndefined(); |
747 | 82 | slot = the->stack; |
748 | 82 | string = fxToString(the, slot); |
749 | 82 | length = (txSize)c_strlen(string); |
750 | 82 | if (length == 0) |
751 | 3 | mxSyntaxError("empty string"); |
752 | 79 | else { |
753 | 79 | char first = string[0]; |
754 | 79 | char last = string[length - 1]; |
755 | 79 | if ((first == 0x09) || (first == 0x0A) || (first == 0x0D) || (first == 0x20) || (last == 0x09) || (last == 0x0A) || (last == 0x0D) || (last == 0x20)) |
756 | 31 | mxSyntaxError("invalid string"); |
757 | 79 | } |
758 | 48 | aParser.slot = slot; |
759 | 48 | aParser.offset = 0; |
760 | 48 | mxPush(mxEmptyString); |
761 | 48 | aParser.string = the->stack; |
762 | 48 | aParser.line = 1; |
763 | 48 | fxParseJSON(the, (txJSONParser*)&aParser); |
764 | 48 | if (mxIsReference(the->stack)) |
765 | 0 | mxSyntaxError("invalid string"); |
766 | 48 | mxPop(); |
767 | 48 | instance = fxNewInstance(the); |
768 | 48 | instance->flag |= XS_EXOTIC_FLAG | XS_DONT_PATCH_FLAG; |
769 | 48 | property = instance->next = fxNewSlot(the); |
770 | 48 | property->flag = XS_INTERNAL_FLAG | XS_DONT_DELETE_FLAG | XS_DONT_SET_FLAG; |
771 | 48 | property->kind = XS_RAW_JSON_KIND; |
772 | 48 | property = property->next = fxNewSlot(the); |
773 | 48 | property->ID = mxID(_rawJSON); |
774 | 48 | property->flag = XS_DONT_DELETE_FLAG | XS_DONT_SET_FLAG; |
775 | 48 | property->kind = slot->kind; |
776 | 48 | property->value = slot->value; |
777 | 48 | mxPullSlot(mxResult); |
778 | 48 | } |
779 | | |
780 | | void fx_JSON_stringify(txMachine* the) |
781 | 160k | { |
782 | 160k | volatile txJSONStringifier aStringifier = {0}; |
783 | 160k | mxTry(the) { |
784 | 160k | fxStringifyJSON(the, (txJSONStringifier*)&aStringifier); |
785 | 160k | if (aStringifier.offset) { |
786 | 157k | fxStringifyJSONChars(the, (txJSONStringifier*)&aStringifier, "\0", 1); |
787 | 157k | mxResult->value.string = (txString)fxNewChunk(the, aStringifier.offset); |
788 | 157k | c_memcpy(mxResult->value.string, aStringifier.buffer, aStringifier.offset); |
789 | 157k | mxResult->kind = XS_STRING_KIND; |
790 | 157k | } |
791 | 160k | c_free(aStringifier.buffer); |
792 | 160k | } |
793 | 160k | mxCatch(the) { |
794 | 18 | if (aStringifier.buffer) |
795 | 18 | c_free(aStringifier.buffer); |
796 | 18 | fxJump(the); |
797 | 18 | } |
798 | 160k | } |
799 | | |
800 | | void fxStringifyJSON(txMachine* the, txJSONStringifier* theStringifier) |
801 | 160k | { |
802 | 160k | txSlot* aSlot; |
803 | 160k | txInteger aFlag; |
804 | 160k | txSlot* instance; |
805 | | |
806 | 160k | aSlot = fxGetInstance(the, mxThis); |
807 | 160k | theStringifier->offset = 0; |
808 | 160k | theStringifier->size = 1024; |
809 | 160k | theStringifier->buffer = c_malloc(1024); |
810 | 160k | if (!theStringifier->buffer) |
811 | 0 | fxAbort(the, XS_NOT_ENOUGH_MEMORY_EXIT); |
812 | | |
813 | 160k | if (mxArgc > 1) { |
814 | 2.84k | aSlot = mxArgv(1); |
815 | 2.84k | if (mxIsReference(aSlot)) { |
816 | 2.74k | if (fxIsCallable(the, aSlot)) |
817 | 2.67k | theStringifier->replacer = mxArgv(1); |
818 | 74 | else if (fxIsArray(the, fxGetInstance(the, aSlot))) |
819 | 68 | theStringifier->keys = fxToJSONKeys(the, aSlot); |
820 | 2.74k | } |
821 | 2.84k | } |
822 | 160k | if (mxArgc > 2) { |
823 | 89 | aSlot = mxArgv(2); |
824 | 89 | if (mxIsReference(aSlot)) { |
825 | 22 | txSlot* instance = fxGetInstance(the, aSlot); |
826 | 22 | if (mxIsNumber(instance)) { |
827 | 6 | fxToNumber(the, aSlot); |
828 | 6 | } |
829 | 16 | else if (mxIsString(instance)) { |
830 | 8 | fxToString(the, aSlot); |
831 | 8 | } |
832 | 22 | } |
833 | 89 | if ((aSlot->kind == XS_INTEGER_KIND) || (aSlot->kind == XS_NUMBER_KIND)) { |
834 | 29 | txInteger aCount = fxToInteger(the, aSlot), anIndex; |
835 | 29 | if (aCount < 0) |
836 | 5 | aCount = 0; |
837 | 24 | else if (aCount > 10) |
838 | 2 | aCount = 10; |
839 | 127 | for (anIndex = 0; anIndex < aCount; anIndex++) |
840 | 98 | theStringifier->indent[anIndex] = ' '; |
841 | 29 | theStringifier->indentLength = aCount; |
842 | 29 | } |
843 | 60 | else if (mxIsStringPrimitive(aSlot)) { |
844 | 34 | txInteger aCount = fxUnicodeLength(aSlot->value.string, C_NULL); |
845 | 34 | if (aCount > 10) { |
846 | 8 | aCount = fxUnicodeToUTF8Offset(aSlot->value.string, 10); |
847 | 8 | } |
848 | 26 | else { |
849 | 26 | aCount = (txInteger)c_strlen(aSlot->value.string); |
850 | 26 | } |
851 | 34 | c_memcpy(theStringifier->indent, aSlot->value.string, aCount); |
852 | 34 | theStringifier->indent[aCount] = 0; |
853 | 34 | theStringifier->indentLength = aCount; |
854 | 34 | } |
855 | 89 | } |
856 | | |
857 | 160k | theStringifier->stack = the->stack; |
858 | 160k | mxPush(mxObjectPrototype); |
859 | 160k | instance = fxNewObjectInstance(the); |
860 | 160k | aFlag = 0; |
861 | 160k | if (mxArgc > 0) |
862 | 160k | mxPushSlot(mxArgv(0)); |
863 | 14 | else |
864 | 14 | mxPushUndefined(); |
865 | 160k | fxNextSlotProperty(the, instance, the->stack, mxID(__empty_string_), XS_NO_FLAG); |
866 | 160k | mxPush(mxEmptyString); |
867 | 160k | fxStringifyJSONProperty(the, theStringifier, &aFlag); |
868 | 160k | mxPop(); |
869 | 160k | } |
870 | | |
871 | | void fxStringifyJSONCharacter(txMachine* the, txJSONStringifier* theStringifier, txInteger character) |
872 | 22.5M | { |
873 | 22.5M | txSize size = mxStringByteLength(character); |
874 | 22.5M | if ((theStringifier->offset + size) >= theStringifier->size) { |
875 | 51.3k | char* aBuffer; |
876 | 51.3k | theStringifier->size += ((size / 1024) + 1) * 1024; |
877 | 51.3k | aBuffer = c_realloc(theStringifier->buffer, theStringifier->size); |
878 | 51.3k | if (!aBuffer) |
879 | 0 | fxAbort(the, XS_NOT_ENOUGH_MEMORY_EXIT); |
880 | 51.3k | theStringifier->buffer = aBuffer; |
881 | 51.3k | } |
882 | 22.5M | mxStringByteEncode(theStringifier->buffer + theStringifier->offset, character); |
883 | 22.5M | theStringifier->offset += size; |
884 | 22.5M | } |
885 | | |
886 | | void fxStringifyJSONChars(txMachine* the, txJSONStringifier* theStringifier, char* s, txSize theSize) |
887 | 184M | { |
888 | | //fprintf(stderr, "%s", s); |
889 | 184M | if ((theStringifier->offset + theSize) >= theStringifier->size) { |
890 | 198k | char* aBuffer; |
891 | 198k | theStringifier->size += ((theSize / 1024) + 1) * 1024; |
892 | 198k | aBuffer = c_realloc(theStringifier->buffer, theStringifier->size); |
893 | 198k | if (!aBuffer) |
894 | 0 | fxAbort(the, XS_NOT_ENOUGH_MEMORY_EXIT); |
895 | 198k | theStringifier->buffer = aBuffer; |
896 | 198k | } |
897 | 184M | c_memcpy(theStringifier->buffer + theStringifier->offset, s, theSize); |
898 | 184M | theStringifier->offset += theSize; |
899 | 184M | } |
900 | | |
901 | | void fxStringifyJSONIndent(txMachine* the, txJSONStringifier* theStringifier) |
902 | 25.8M | { |
903 | 25.8M | txInteger aLevel; |
904 | 25.8M | if (theStringifier->indent[0]) { |
905 | 661 | fxStringifyJSONChars(the, theStringifier, "\n", 1); |
906 | 2.09k | for (aLevel = 0; aLevel < theStringifier->level; aLevel++) |
907 | 1.43k | fxStringifyJSONChars(the, theStringifier, theStringifier->indent, theStringifier->indentLength); |
908 | 661 | } |
909 | 25.8M | } |
910 | | |
911 | | void fxStringifyJSONInteger(txMachine* the, txJSONStringifier* theStringifier, txInteger theInteger) |
912 | 25.4M | { |
913 | 25.4M | char aBuffer[256]; |
914 | 25.4M | fxIntegerToString(the, theInteger, aBuffer, sizeof(aBuffer)); |
915 | 25.4M | fxStringifyJSONChars(the, theStringifier, aBuffer, (txSize)c_strlen(aBuffer)); |
916 | 25.4M | } |
917 | | |
918 | | void fxStringifyJSONName(txMachine* the, txJSONStringifier* theStringifier, txInteger* theFlag) |
919 | 25.8M | { |
920 | 25.8M | txSlot* aSlot = the->stack; |
921 | 25.8M | if (*theFlag & 1) { |
922 | 25.4M | fxStringifyJSONChars(the, theStringifier, ",", 1); |
923 | 25.4M | fxStringifyJSONIndent(the, theStringifier); |
924 | 25.4M | } |
925 | 362k | else |
926 | 362k | *theFlag |= 1; |
927 | 25.8M | if (*theFlag & 2) { |
928 | 25.6M | if (aSlot->kind == XS_INTEGER_KIND) { |
929 | 25.4M | fxStringifyJSONChars(the, theStringifier, "\"", 1); |
930 | 25.4M | fxStringifyJSONInteger(the, theStringifier, aSlot->value.integer); |
931 | 25.4M | fxStringifyJSONChars(the, theStringifier, "\"", 1); |
932 | 25.4M | } |
933 | 206k | else |
934 | 206k | fxStringifyJSONString(the, theStringifier, aSlot->value.string); |
935 | 25.6M | fxStringifyJSONChars(the, theStringifier, ":", 1); |
936 | 25.6M | if (theStringifier->indent[0]) |
937 | 245 | fxStringifyJSONChars(the, theStringifier, " ", 1); |
938 | 25.6M | } |
939 | 25.8M | mxPop(); // POP KEY |
940 | 25.8M | } |
941 | | |
942 | | void fxStringifyJSONNumber(txMachine* the, txJSONStringifier* theStringifier, txNumber theNumber) |
943 | 67.2k | { |
944 | 67.2k | int fpclass = c_fpclassify(theNumber); |
945 | 67.2k | if ((fpclass != C_FP_NAN) && (fpclass != C_FP_INFINITE)) { |
946 | 19.0k | char aBuffer[256]; |
947 | 19.0k | fxNumberToString(the, theNumber, aBuffer, sizeof(aBuffer), 0, 0); |
948 | 19.0k | fxStringifyJSONChars(the, theStringifier, aBuffer, (txSize)c_strlen(aBuffer)); |
949 | 19.0k | } |
950 | 48.2k | else |
951 | 48.2k | fxStringifyJSONChars(the, theStringifier, "null", 4); |
952 | 67.2k | } |
953 | | |
954 | | void fxStringifyJSONProperty(txMachine* the, txJSONStringifier* theStringifier, txInteger* theFlag) |
955 | 25.9M | { |
956 | 25.9M | txSlot* aWrapper = the->stack + 2; |
957 | 25.9M | txSlot* aValue = the->stack + 1; |
958 | 25.9M | txSlot* aKey = the->stack; |
959 | 25.9M | txSlot* anInstance; |
960 | 25.9M | txSlot* aSlot; |
961 | 25.9M | txInteger aFlag; |
962 | 25.9M | txIndex aLength, anIndex; |
963 | | |
964 | 25.9M | mxCheckCStack(); |
965 | 25.9M | if (mxIsReference(aValue) || mxIsBigInt(aValue)) { |
966 | | /* THIS */ |
967 | 171k | mxPushSlot(aValue); |
968 | | /* FUNCTION */ |
969 | 171k | mxDub(); |
970 | 171k | mxGetID(mxID(_toJSON)); |
971 | 171k | if (mxIsReference(the->stack) && mxIsFunction(the->stack->value.reference)) { |
972 | 2.43k | mxCall(); |
973 | 2.43k | mxPushSlot(aKey); |
974 | 2.43k | fxToString(the, the->stack); |
975 | 2.43k | mxRunCount(1); |
976 | 2.43k | mxPullSlot(aValue); |
977 | 2.43k | } |
978 | 171k | the->stack = aKey; |
979 | 171k | } |
980 | 25.9M | if (theStringifier->replacer) { |
981 | | /* THIS */ |
982 | 48.7k | mxPushSlot(aWrapper); |
983 | | /* FUNCTION */ |
984 | 48.7k | mxPushSlot(theStringifier->replacer); |
985 | 48.7k | mxCall(); |
986 | | /* ARGUMENTS */ |
987 | 48.7k | mxPushSlot(aKey); |
988 | 48.7k | fxToString(the, the->stack); |
989 | 48.7k | mxPushSlot(aValue); |
990 | | /* COUNT */ |
991 | 48.7k | mxRunCount(2); |
992 | 48.7k | mxPullSlot(aValue); |
993 | 48.7k | the->stack = aKey; |
994 | 48.7k | } |
995 | 25.9M | if (mxIsReference(aValue)) { |
996 | 215k | mxPushSlot(aValue); |
997 | 215k | anInstance = fxToInstance(the, the->stack); |
998 | 215k | if (anInstance->flag & XS_LEVEL_FLAG) |
999 | 8 | mxTypeError("cyclic value"); |
1000 | 214k | the->stack = aKey; |
1001 | 214k | aSlot = anInstance->next; |
1002 | 214k | if (aSlot && (aSlot->flag & XS_INTERNAL_FLAG)) { |
1003 | 133k | if ((aSlot->kind == XS_INTEGER_KIND) || (aSlot->kind == XS_NUMBER_KIND)) { |
1004 | 3 | fxToNumber(the, aValue); |
1005 | 3 | } |
1006 | 133k | else if (mxIsStringPrimitive(aSlot)) { |
1007 | 3 | fxToString(the, aValue); |
1008 | 3 | } |
1009 | 133k | else if ((aSlot->kind == XS_BOOLEAN_KIND) || (aSlot->kind == XS_BIGINT_KIND) || (aSlot->kind == XS_BIGINT_X_KIND)) { |
1010 | 8 | aValue->kind = aSlot->kind; |
1011 | 8 | aValue->value = aSlot->value; |
1012 | 8 | } |
1013 | 133k | else if (aSlot->kind == XS_RAW_JSON_KIND) { |
1014 | 24 | mxPushSlot(aValue); |
1015 | 24 | mxGetID(mxID(_rawJSON)); |
1016 | 24 | aValue->kind = the->stack->kind; |
1017 | 24 | aValue->value = the->stack->value; |
1018 | 24 | the->stack = aKey; |
1019 | 24 | fxStringifyJSONName(the, theStringifier, theFlag); |
1020 | 24 | fxStringifyJSONChars(the, theStringifier, aValue->value.string, (txSize)c_strlen(aValue->value.string)); |
1021 | 24 | mxPop(); // POP VALUE |
1022 | 24 | return; |
1023 | 24 | } |
1024 | 133k | } |
1025 | 214k | } |
1026 | 25.9M | if (aValue->kind == XS_NULL_KIND) { |
1027 | 1.17k | fxStringifyJSONName(the, theStringifier, theFlag); |
1028 | 1.17k | fxStringifyJSONChars(the, theStringifier, "null", 4); |
1029 | 1.17k | } |
1030 | 25.9M | else if (aValue->kind == XS_BOOLEAN_KIND) { |
1031 | 88.0k | fxStringifyJSONName(the, theStringifier, theFlag); |
1032 | 88.0k | if (aValue->value.boolean) |
1033 | 175 | fxStringifyJSONChars(the, theStringifier, "true", 4); |
1034 | 87.8k | else |
1035 | 87.8k | fxStringifyJSONChars(the, theStringifier, "false", 5); |
1036 | 88.0k | } |
1037 | 25.8M | else if (aValue->kind == XS_INTEGER_KIND) { |
1038 | 745 | fxStringifyJSONName(the, theStringifier, theFlag); |
1039 | 745 | fxStringifyJSONInteger(the, theStringifier, aValue->value.integer); |
1040 | 745 | } |
1041 | 25.8M | else if (aValue->kind == XS_NUMBER_KIND) { |
1042 | 67.2k | fxStringifyJSONName(the, theStringifier, theFlag); |
1043 | 67.2k | fxStringifyJSONNumber(the, theStringifier, aValue->value.number); |
1044 | 67.2k | } |
1045 | 25.7M | else if ((aValue->kind == XS_STRING_KIND) || (aValue->kind == XS_STRING_X_KIND)) { |
1046 | 25.4M | fxStringifyJSONName(the, theStringifier, theFlag); |
1047 | 25.4M | fxStringifyJSONString(the, theStringifier, aValue->value.string); |
1048 | 25.4M | } |
1049 | 297k | else if ((aValue->kind == XS_BIGINT_KIND) || (aValue->kind == XS_BIGINT_X_KIND)) { |
1050 | 8 | mxTypeError("stringify bigint"); |
1051 | 8 | } |
1052 | 297k | else if ((aValue->kind == XS_REFERENCE_KIND) && !fxIsCallable(the, aValue)) { |
1053 | 205k | mxTry(the) { |
1054 | 205k | fxStringifyJSONName(the, theStringifier, theFlag); |
1055 | 205k | if (anInstance->flag & XS_MARK_FLAG) |
1056 | 0 | mxTypeError("read only value"); |
1057 | 205k | anInstance->flag |= XS_LEVEL_FLAG; |
1058 | 205k | if (fxIsArray(the, anInstance)) { |
1059 | 47.3k | fxStringifyJSONChars(the, theStringifier, "[", 1); |
1060 | 47.3k | mxPushReference(anInstance); |
1061 | 47.3k | mxGetID(mxID(_length)); |
1062 | 47.3k | aLength = fxToInteger(the, the->stack); |
1063 | 47.3k | if (aLength > 0) { |
1064 | 46.5k | theStringifier->level++; |
1065 | 46.5k | fxStringifyJSONIndent(the, theStringifier); |
1066 | 46.5k | aFlag = 4; |
1067 | 46.5k | mxPop(); |
1068 | 94.0k | for (anIndex = 0; anIndex < aLength; anIndex++) { |
1069 | 47.4k | mxPushReference(anInstance); |
1070 | 47.4k | mxGetIndex(anIndex); |
1071 | 47.4k | mxPushInteger(anIndex); |
1072 | 47.4k | fxStringifyJSONProperty(the, theStringifier, &aFlag); |
1073 | 47.4k | } |
1074 | 46.5k | theStringifier->level--; |
1075 | 46.5k | fxStringifyJSONIndent(the, theStringifier); |
1076 | 46.5k | } |
1077 | 47.3k | fxStringifyJSONChars(the, theStringifier, "]", 1); |
1078 | 47.3k | } |
1079 | 157k | else { |
1080 | 157k | fxStringifyJSONChars(the, theStringifier, "{", 1); |
1081 | 157k | { |
1082 | 157k | txSlot* at; |
1083 | 157k | txSlot* property; |
1084 | 157k | if (theStringifier->keys) { |
1085 | 70 | mxPushUndefined(); |
1086 | 70 | at = theStringifier->keys->value.reference; |
1087 | 70 | } |
1088 | 157k | else { |
1089 | 157k | at = fxNewInstance(the); |
1090 | 157k | mxBehaviorOwnKeys(the, anInstance, XS_EACH_NAME_FLAG, at); |
1091 | 157k | } |
1092 | 157k | if (at->next) { |
1093 | 157k | theStringifier->level++; |
1094 | 157k | fxStringifyJSONIndent(the, theStringifier); |
1095 | 157k | aFlag = 2; |
1096 | 157k | mxPushUndefined(); |
1097 | 157k | property = the->stack; |
1098 | 157k | mxPushReference(anInstance); |
1099 | 25.8M | while ((at = at->next)) { |
1100 | 25.7M | if (mxBehaviorGetOwnProperty(the, anInstance, at->value.at.id, at->value.at.index, property) && !(property->flag & XS_DONT_ENUM_FLAG)) { |
1101 | 25.7M | mxPushReference(anInstance); |
1102 | 25.7M | mxGetAll(at->value.at.id, at->value.at.index); |
1103 | 25.7M | if (at->value.at.id) |
1104 | 294k | fxPushKeyString(the, at->value.at.id, C_NULL); |
1105 | 25.4M | else |
1106 | 25.4M | mxPushInteger((txInteger)at->value.at.index); |
1107 | 25.7M | fxStringifyJSONProperty(the, theStringifier, &aFlag); |
1108 | 25.7M | } |
1109 | 25.7M | } |
1110 | 157k | mxPop(); |
1111 | 157k | mxPop(); |
1112 | 157k | theStringifier->level--; |
1113 | 157k | fxStringifyJSONIndent(the, theStringifier); |
1114 | 157k | } |
1115 | 157k | mxPop(); |
1116 | 157k | } |
1117 | 157k | fxStringifyJSONChars(the, theStringifier, "}", 1); |
1118 | 157k | } |
1119 | 205k | anInstance->flag &= ~XS_LEVEL_FLAG; |
1120 | 205k | } |
1121 | 205k | mxCatch(the) { |
1122 | 20 | if (anInstance->flag & XS_LEVEL_FLAG) |
1123 | 20 | anInstance->flag &= ~XS_LEVEL_FLAG; |
1124 | 20 | fxJump(the); |
1125 | 20 | } |
1126 | 205k | } |
1127 | 91.8k | else { |
1128 | 91.8k | if (*theFlag & 4) { |
1129 | 484 | if (*theFlag & 1) { |
1130 | 244 | fxStringifyJSONChars(the, theStringifier, ",", 1); |
1131 | 244 | fxStringifyJSONIndent(the, theStringifier); |
1132 | 244 | } |
1133 | 240 | else |
1134 | 240 | *theFlag |= 1; |
1135 | 484 | fxStringifyJSONChars(the, theStringifier, "null", 4); |
1136 | 484 | } |
1137 | 91.8k | } |
1138 | 25.9M | mxPop(); // POP VALUE |
1139 | 25.9M | } |
1140 | | |
1141 | | void fxStringifyJSONString(txMachine* the, txJSONStringifier* theStringifier, txString theString) |
1142 | 25.6M | { |
1143 | 25.6M | fxStringifyJSONChars(the, theStringifier, "\"", 1); |
1144 | 53.0M | for (;;) { |
1145 | 53.0M | txInteger character; |
1146 | 53.0M | theString = mxStringByteDecode(theString, &character); |
1147 | 53.0M | if (character == C_EOF) |
1148 | 25.6M | break; |
1149 | 27.3M | if (character < 8) |
1150 | 2.33M | fxStringifyJSONUnicodeEscape(the, theStringifier, character); |
1151 | 24.9M | else if (character == 8) |
1152 | 22.4k | fxStringifyJSONChars(the, theStringifier, "\\b", 2); |
1153 | 24.9M | else if (character == 9) |
1154 | 3.58k | fxStringifyJSONChars(the, theStringifier, "\\t", 2); |
1155 | 24.9M | else if (character == 10) |
1156 | 22.6k | fxStringifyJSONChars(the, theStringifier, "\\n", 2); |
1157 | 24.9M | else if (character == 11) |
1158 | 41.3k | fxStringifyJSONUnicodeEscape(the, theStringifier, character); |
1159 | 24.8M | else if (character == 12) |
1160 | 115k | fxStringifyJSONChars(the, theStringifier, "\\f", 2); |
1161 | 24.7M | else if (character == 13) |
1162 | 818 | fxStringifyJSONChars(the, theStringifier, "\\r", 2); |
1163 | 24.7M | else if (character < 32) |
1164 | 488k | fxStringifyJSONUnicodeEscape(the, theStringifier, character); |
1165 | 24.2M | else if (character < 34) |
1166 | 233k | fxStringifyJSONCharacter(the, theStringifier, character); |
1167 | 24.0M | else if (character == 34) |
1168 | 495k | fxStringifyJSONChars(the, theStringifier, "\\\"", 2); |
1169 | 23.5M | else if (character < 92) |
1170 | 3.29M | fxStringifyJSONCharacter(the, theStringifier, character); |
1171 | 20.2M | else if (character == 92) |
1172 | 781k | fxStringifyJSONChars(the, theStringifier, "\\\\", 2); |
1173 | 19.4M | else if (character < 127) |
1174 | 4.28M | fxStringifyJSONCharacter(the, theStringifier, character); |
1175 | 15.1M | else if ((0xD800 <= character) && (character <= 0xDFFF)) |
1176 | 457k | fxStringifyJSONUnicodeEscape(the, theStringifier, character); |
1177 | 14.7M | else |
1178 | 14.7M | fxStringifyJSONCharacter(the, theStringifier, character); |
1179 | 27.3M | } |
1180 | 25.6M | fxStringifyJSONChars(the, theStringifier, "\"", 1); |
1181 | 25.6M | } |
1182 | | |
1183 | | void fxStringifyJSONUnicodeEscape(txMachine* the, txJSONStringifier* theStringifier, txInteger character) |
1184 | 3.32M | { |
1185 | 3.32M | char buffer[16]; |
1186 | 3.32M | txString p = buffer; |
1187 | 3.32M | *p++ = '\\'; |
1188 | 3.32M | *p++ = 'u'; |
1189 | 3.32M | p = fxStringifyUnicodeEscape(p, character, '\\'); |
1190 | 3.32M | fxStringifyJSONChars(the, theStringifier, buffer, mxPtrDiff(p - buffer)); |
1191 | 3.32M | } |
1192 | | |
1193 | | txSlot* fxToJSONKeys(txMachine* the, txSlot* reference) |
1194 | 81 | { |
1195 | 81 | txSlot* list = fxNewInstance(the); |
1196 | 81 | txSlot* item = list; |
1197 | 81 | txSlot* slot; |
1198 | 81 | txIndex length, i; |
1199 | 81 | mxPushSlot(reference); |
1200 | 81 | mxGetID(mxID(_length)); |
1201 | 81 | length = (txIndex)fxToLength(the, the->stack); |
1202 | 81 | mxPop(); |
1203 | 81 | i = 0; |
1204 | 308 | while (i < length) { |
1205 | 227 | txBoolean flag = 0; |
1206 | 227 | txID id = XS_NO_ID; |
1207 | 227 | txIndex index = 0; |
1208 | 227 | mxPushSlot(reference); |
1209 | 227 | mxGetIndex(i); |
1210 | 227 | slot = the->stack; |
1211 | 278 | again: |
1212 | 278 | if ((slot->kind == XS_STRING_KIND) || (slot->kind == XS_STRING_X_KIND)) { |
1213 | 113 | if (fxStringToIndex(the, slot->value.string, &index)) |
1214 | 7 | flag = 1; |
1215 | 106 | else { |
1216 | 106 | if (slot->kind == XS_STRING_X_KIND) |
1217 | 0 | id = fxNewNameX(the, slot->value.string); |
1218 | 106 | else |
1219 | 106 | id = fxNewName(the, slot); |
1220 | 106 | flag = 1; |
1221 | 106 | } |
1222 | 113 | } |
1223 | 165 | else if (slot->kind == XS_INTEGER_KIND) { |
1224 | 72 | if (fxIntegerToIndex(the, slot->value.integer, &index)) |
1225 | 53 | flag = 1; |
1226 | 19 | else { |
1227 | 19 | fxToString(the, slot); |
1228 | 19 | goto again; |
1229 | 19 | } |
1230 | 72 | } |
1231 | 93 | else if (slot->kind == XS_NUMBER_KIND){ |
1232 | 34 | if (fxNumberToIndex(the, slot->value.number, &index)) |
1233 | 4 | flag = 1; |
1234 | 30 | else { |
1235 | 30 | fxToString(the, slot); |
1236 | 30 | goto again; |
1237 | 30 | } |
1238 | 34 | } |
1239 | 59 | else if (slot->kind == XS_REFERENCE_KIND) { |
1240 | 4 | txSlot* instance = slot->value.reference; |
1241 | 4 | if (mxIsNumber(instance) || mxIsString(instance)) { |
1242 | 2 | fxToString(the, slot); |
1243 | 2 | goto again; |
1244 | 2 | } |
1245 | 4 | } |
1246 | 227 | if (flag) { |
1247 | 170 | txSlot* already = list->next; |
1248 | 479 | while (already) { |
1249 | 329 | if ((already->value.at.id == id) && (already->value.at.index == index)) |
1250 | 20 | break; |
1251 | 309 | already = already->next; |
1252 | 309 | } |
1253 | 170 | if (!already) { |
1254 | 150 | item = item->next = fxNewSlot(the); |
1255 | 150 | item->value.at.id = id; |
1256 | 150 | item->value.at.index = index; |
1257 | 150 | item->kind = XS_AT_KIND; |
1258 | 150 | } |
1259 | 170 | } |
1260 | 227 | mxPop(); |
1261 | 227 | i++; |
1262 | 227 | } |
1263 | 81 | return the->stack; |
1264 | 81 | } |