jseption 0.0.5 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/README.md +30 -6
  2. package/dist/atoms.d.ts +1 -0
  3. package/dist/atoms.js +2 -1
  4. package/dist/atoms.js.map +1 -1
  5. package/dist/builtins/base64.d.ts +14 -0
  6. package/dist/builtins/base64.js +219 -0
  7. package/dist/builtins/base64.js.map +1 -0
  8. package/dist/builtins/bigint.js +3 -3
  9. package/dist/builtins/bigint.js.map +1 -1
  10. package/dist/builtins/date.d.ts +1 -0
  11. package/dist/builtins/date.js +26 -12
  12. package/dist/builtins/date.js.map +1 -1
  13. package/dist/builtins/error.js +9 -0
  14. package/dist/builtins/error.js.map +1 -1
  15. package/dist/builtins/float16.d.ts +3 -0
  16. package/dist/builtins/float16.js +69 -0
  17. package/dist/builtins/float16.js.map +1 -0
  18. package/dist/builtins/function.js +2 -2
  19. package/dist/builtins/index.js +9 -6
  20. package/dist/builtins/index.js.map +1 -1
  21. package/dist/builtins/iterator.d.ts +2 -0
  22. package/dist/builtins/iterator.js +624 -0
  23. package/dist/builtins/iterator.js.map +1 -0
  24. package/dist/builtins/json.d.ts +13 -1
  25. package/dist/builtins/json.js +283 -211
  26. package/dist/builtins/json.js.map +1 -1
  27. package/dist/builtins/mapset.js +365 -6
  28. package/dist/builtins/mapset.js.map +1 -1
  29. package/dist/builtins/math.js +203 -3
  30. package/dist/builtins/math.js.map +1 -1
  31. package/dist/builtins/native.js +6 -6
  32. package/dist/builtins/object.js +9 -2
  33. package/dist/builtins/object.js.map +1 -1
  34. package/dist/builtins/promise.js +31 -11
  35. package/dist/builtins/promise.js.map +1 -1
  36. package/dist/builtins/proxy.js +2 -2
  37. package/dist/builtins/reflect.js +6 -2
  38. package/dist/builtins/reflect.js.map +1 -1
  39. package/dist/builtins/regexp.js +74 -3
  40. package/dist/builtins/regexp.js.map +1 -1
  41. package/dist/builtins/string.js +1 -1
  42. package/dist/builtins/symbol.js +1 -1
  43. package/dist/builtins/symbol.js.map +1 -1
  44. package/dist/builtins/typedarray.d.ts +4 -1
  45. package/dist/builtins/typedarray.js +325 -37
  46. package/dist/builtins/typedarray.js.map +1 -1
  47. package/dist/builtins/weakref.js +7 -7
  48. package/dist/embed.d.ts +5 -0
  49. package/dist/embed.js +36 -11
  50. package/dist/embed.js.map +1 -1
  51. package/dist/interpreter.js +15 -14
  52. package/dist/interpreter.js.map +1 -1
  53. package/dist/jit.d.ts +1 -0
  54. package/dist/jit.js +51 -13
  55. package/dist/jit.js.map +1 -1
  56. package/dist/jit_hook.d.ts +1 -0
  57. package/dist/jit_hook.js.map +1 -1
  58. package/dist/jit_runtime.d.ts +1 -1
  59. package/dist/jit_runtime.js +6 -6
  60. package/dist/jit_runtime.js.map +1 -1
  61. package/dist/jit_stack.d.ts +1 -0
  62. package/dist/jit_stack.js +2 -1
  63. package/dist/jit_stack.js.map +1 -1
  64. package/dist/jseption.d.ts +2 -0
  65. package/dist/jseption.js +1 -0
  66. package/dist/jseption.js.map +1 -1
  67. package/dist/membrane.d.ts +6 -0
  68. package/dist/membrane.js +7 -3
  69. package/dist/membrane.js.map +1 -1
  70. package/dist/module.js +5 -3
  71. package/dist/module.js.map +1 -1
  72. package/dist/parser.js +26 -9
  73. package/dist/parser.js.map +1 -1
  74. package/dist/print.d.ts +12 -0
  75. package/dist/print.js +494 -0
  76. package/dist/print.js.map +1 -0
  77. package/dist/qjs.js +13 -10
  78. package/dist/qjs.js.map +1 -1
  79. package/dist/quickjs.d.ts +8 -0
  80. package/dist/quickjs.js +132 -53
  81. package/dist/quickjs.js.map +1 -1
  82. package/dist/std.d.ts +4 -2
  83. package/dist/std.js +42 -8
  84. package/dist/std.js.map +1 -1
  85. package/dist/values.d.ts +31 -27
  86. package/dist/values.js.map +1 -1
  87. package/dist/version.d.ts +1 -1
  88. package/dist/version.js +1 -1
  89. package/dist/version.js.map +1 -1
  90. package/package.json +1 -1
@@ -4,7 +4,7 @@
4
4
  * js_json_check, js_json_to_str, JS_JSONStringify)
5
5
  */
6
6
  import * as Q from "../quickjs.js";
7
- import { JS_EXCEPTION, JS_IsException, JS_IsUndefined, JS_IsObject, JS_IsFunction, JS_IsArray, JS_NewObject, JS_NewArray, JS_NewErrorNative, JS_Throw, JS_ThrowTypeError, JS_ThrowStackOverflow, JS_DefinePropertyValue, JS_DefinePropertyValueUint32, JS_GetProperty, JS_GetPropertyInt64, JS_GetPropertyValue, JS_DeleteProperty, JS_GetOwnPropertyNamesInternal, JS_PROP_C_W_E, JS_PROP_WRITABLE, JS_PROP_CONFIGURABLE, JS_GPN_STRING_MASK, JS_GPN_ENUM_ONLY, JS_ATOM_NULL, JS_ATOM_empty_string, JS_ATOM_toJSON, JS_ATOM_fileName, JS_ATOM_lineNumber, JS_ATOM_columnNumber, JS_ATOM_Symbol_toStringTag, JS_ATOM_true, JS_ATOM_false, JS_ATOM_null, JS_ValueToAtom, JS_NewAtomUInt32, JS_AtomToValue, JS_Call, JS_ToString, JS_ToNumber, JS_ToInt32Clamp, JS_ToQuotedString, js_get_length64, build_backtrace, JS_SetPropertyFunctionList, JS_CFUNC_DEF, JS_PROP_STRING_DEF, JS_OBJECT_DEF, JS_NewAtom, } from "../quickjs.js";
7
+ import { JS_EXCEPTION, JS_IsException, JS_IsUndefined, JS_IsObject, JS_IsFunction, JS_IsArray, JS_NewObject, JS_NewArray, JS_NewErrorNative, JS_Throw, JS_ThrowTypeError, JS_ThrowStackOverflow, JS_DefinePropertyValue, JS_DefinePropertyValueUint32, JS_DefinePropertyValueStr, JS_GetProperty, JS_GetPropertyInt64, JS_GetPropertyValue, JS_DeleteProperty, JS_GetOwnPropertyNamesInternal, JS_PROP_C_W_E, JS_PROP_WRITABLE, JS_PROP_CONFIGURABLE, JS_GPN_STRING_MASK, JS_GPN_ENUM_ONLY, JS_ATOM_NULL, JS_ATOM_empty_string, JS_ATOM_toJSON, JS_ATOM_fileName, JS_ATOM_lineNumber, JS_ATOM_columnNumber, JS_ATOM_Symbol_toStringTag, JS_ATOM_true, JS_ATOM_false, JS_ATOM_null, JS_ValueToAtom, JS_NewAtomUInt32, JS_AtomToValue, JS_Call, JS_ToString, JS_ToNumber, JS_ToInt32Clamp, JS_ToQuotedString, js_get_length64, build_backtrace, JS_SetPropertyFunctionList, JS_CFUNC_DEF, JS_PROP_STRING_DEF, JS_OBJECT_DEF, JS_NewAtom, JS_ATOM_NaN, JS_ATOM_Infinity, JS_ThrowSyntaxError, JS_PROP_ENUMERABLE, js_same_value, __JS_AtomIsTaggedInt, __JS_AtomToUInt32, } from "../quickjs.js";
8
8
  import { fix_deep_json } from "./native.js";
9
9
  /* ---------------------------------------------------------------------
10
10
  JSON parser (port of json_next_token / json_parse_value)
@@ -94,131 +94,67 @@ function is_ident_start_ascii(c) {
94
94
  function is_ident_continue_ascii(c) {
95
95
  return is_ident_start_ascii(c) || is_digit(c);
96
96
  }
97
- /* port of lre_parse_escape(&p, TRUE): returns the code point, -1 if
98
- malformed, -2 if unknown escape; pp.p is updated */
99
- function lre_parse_escape(s, pp) {
100
- let p = pp.p;
101
- let c = s.at(p++);
102
- switch (c) {
103
- case 98:
104
- c = 8;
105
- break; /* \b */
106
- case 102:
107
- c = 12;
108
- break; /* \f */
109
- case 110:
110
- c = 10;
111
- break; /* \n */
112
- case 114:
113
- c = 13;
114
- break; /* \r */
115
- case 116:
116
- c = 9;
117
- break; /* \t */
118
- case 118:
119
- c = 11;
120
- break; /* \v */
121
- case 120: /* x */
122
- case 117: /* u */
123
- {
124
- if (s.at(p) === 123 /* { */) {
125
- p++;
126
- c = 0;
127
- for (;;) {
128
- const h = from_hex(s.at(p++));
129
- if (h < 0)
130
- return -1;
131
- c = (c << 4) | h;
132
- if (c > 0x10ffff)
133
- return -1;
134
- if (s.at(p) === 125 /* } */)
135
- break;
136
- }
137
- p++;
138
- }
139
- else {
140
- const n = (c === 120) ? 2 : 4;
141
- c = 0;
142
- for (let i = 0; i < n; i++) {
143
- const h = from_hex(s.at(p++));
144
- if (h < 0)
145
- return -1;
146
- c = (c << 4) | h;
147
- }
148
- }
149
- }
150
- break;
151
- default:
152
- return -2;
153
- }
154
- pp.p = p;
155
- return c;
156
- }
157
- /* port of js_parse_string() in JSON mode (s->cur_func == NULL) */
97
+ /* port of json_parse_string: 'p' is the position after the opening quote */
158
98
  function json_parse_string(s, sep, p) {
159
99
  let out = '';
160
100
  for (;;) {
161
101
  if (p >= s.buf_end)
162
- return json_parse_error(s, 'unexpected end of string');
163
- let c = s.at(p);
164
- if (c < 0x20) {
165
- return json_parse_error_pos(s, p, 'invalid character in a JSON string');
166
- }
167
- p++;
102
+ return json_parse_error(s, 'Unexpected end of JSON input');
103
+ let c = s.at(p++);
168
104
  if (c === sep)
169
105
  break;
106
+ if (c < 0x20)
107
+ return json_parse_error_pos(s, p - 1, 'Bad control character in string literal');
170
108
  if (c === 92 /* \ */) {
171
- const p_escape = p - 1;
172
- c = s.at(p);
109
+ c = s.at(p++);
173
110
  switch (c) {
174
- case 0:
175
- if (p >= s.buf_end)
176
- return json_parse_error(s, 'unexpected end of string');
177
- p++;
178
- break;
179
- case 39: /* ' */
180
- case 34: /* " */
111
+ case 98:
112
+ c = 8;
113
+ break; /* \b */
114
+ case 102:
115
+ c = 12;
116
+ break; /* \f */
117
+ case 110:
118
+ c = 10;
119
+ break; /* \n */
120
+ case 114:
121
+ c = 13;
122
+ break; /* \r */
123
+ case 116:
124
+ c = 9;
125
+ break; /* \t */
181
126
  case 92: /* \ */
182
- p++;
127
+ case 47: /* / */
183
128
  break;
184
- case 13: /* \r: accept DOS and MAC newline sequences */
185
- if (s.at(p + 1) === 10)
186
- p++;
187
- /* fall through */
188
- case 10:
189
- /* ignore escaped newline sequence */
190
- p++;
191
- continue;
192
- default:
193
- if (c >= 48 && c <= 57) {
194
- /* JSON case: no octal escapes */
195
- return json_parse_error_pos(s, p_escape, 'malformed escape sequence in string literal');
196
- }
197
- else if (c >= 0x80) {
198
- p++;
199
- /* LS or PS are skipped */
200
- if (c === CP_LS || c === CP_PS)
201
- continue;
202
- }
203
- else {
204
- const pp = { p };
205
- const ret = lre_parse_escape(s, pp);
206
- if (ret === -1) {
207
- return json_parse_error_pos(s, p_escape, 'malformed escape sequence in string literal');
208
- }
209
- else if (ret < 0) {
210
- /* ignore the '\' (could output a warning) */
211
- p++;
212
- }
213
- else {
214
- p = pp.p;
215
- c = ret;
216
- }
129
+ case 117: /* u */
130
+ c = 0;
131
+ for (let i = 0; i < 4; i++) {
132
+ const h = from_hex(s.at(p++));
133
+ if (h < 0)
134
+ return json_parse_error_pos(s, p - 1, 'Bad Unicode escape');
135
+ c = (c << 4) | h;
217
136
  }
218
137
  break;
138
+ case 10: /* escaped newline: multi-line string */
139
+ if (s.ext_json)
140
+ continue;
141
+ return json_parse_error_pos(s, p - 1, 'Bad escaped character');
142
+ case 118: /* v */
143
+ if (s.ext_json) {
144
+ c = 11;
145
+ break;
146
+ }
147
+ return json_parse_error_pos(s, p - 1, 'Bad escaped character');
148
+ default:
149
+ if (c === sep)
150
+ break;
151
+ if (p > s.buf_end)
152
+ return json_parse_error(s, 'Unexpected end of JSON input');
153
+ return json_parse_error_pos(s, p - 1, 'Bad escaped character');
219
154
  }
220
155
  }
221
- out += (c > 0xffff) ? String.fromCodePoint(c) : String.fromCharCode(c);
156
+ /* the input is UTF-16: other characters (surrogates included) are copied as is */
157
+ out += String.fromCharCode(c);
222
158
  }
223
159
  s.tok = -2 /* JSONTok.STRING */;
224
160
  s.tok_str = out;
@@ -239,80 +175,94 @@ function json_parse_ident(s, pp, c) {
239
175
  pp.p = p;
240
176
  return JS_NewAtom(s.ctx, ident);
241
177
  }
242
- /* port of js_atof() with radix 10 and no flags (JSON) or
243
- ATOD_ACCEPT_BIN_OCT with radix 0 (extended JSON). Returns -1 on error. */
178
+ /* the message of js_parse_error_pos(s, p, "Unexpected token '%c'", *p): the
179
+ NUL past the end of the C buffer ends the formatted message */
180
+ function json_unexpected_char(s, p) {
181
+ const c = s.at(p);
182
+ return json_parse_error_pos(s, p, c === 0 ? "Unexpected token '" : `Unexpected token '${String.fromCharCode(c)}'`);
183
+ }
184
+ /* the input starts with 'str' at position p */
185
+ function json_strstart(s, p, str) {
186
+ return s.buf.startsWith(str, p);
187
+ }
188
+ /* port of json_parse_number: 'p' is the position of the first character */
244
189
  function json_parse_number(s, p) {
245
- const ext = s.ext_json;
246
- let is_neg = false;
247
- let radix = 10;
248
- let p_start;
249
- if (s.at(p) === 43 /* + */) {
250
- p++;
251
- }
252
- else if (s.at(p) === 45 /* - */) {
190
+ const p_start = p;
191
+ let d;
192
+ if (s.at(p) === 43 /* + */ || s.at(p) === 45 /* - */)
253
193
  p++;
254
- is_neg = true;
255
- }
256
- if (ext && s.at(p) === 48 /* 0 */) {
257
- const c1 = s.at(p + 1);
258
- if (c1 === 120 || c1 === 88) {
259
- p += 2;
260
- radix = 16;
261
- }
262
- else if (c1 === 111 || c1 === 79) {
263
- p += 2;
264
- radix = 8;
194
+ done: {
195
+ if (!is_digit(s.at(p))) {
196
+ if (!s.ext_json)
197
+ return json_unexpected_char(s, p);
198
+ if (json_strstart(s, p, 'Infinity')) {
199
+ p += 8;
200
+ d = s.at(p_start) === 45 ? -Infinity : Infinity;
201
+ break done;
202
+ }
203
+ else if (json_strstart(s, p, 'NaN')) {
204
+ p += 3;
205
+ d = NaN;
206
+ break done;
207
+ }
208
+ else if (s.at(p) !== 46 /* . */) {
209
+ return json_unexpected_char(s, p);
210
+ }
265
211
  }
266
- else if (c1 === 98 || c1 === 66) {
267
- p += 2;
268
- radix = 2;
212
+ if (s.at(p) === 48 /* 0 */) {
213
+ if (s.ext_json) {
214
+ /* also accepts base 16, 8 and 2 prefix for integers */
215
+ let radix = 10;
216
+ const c1 = s.at(p + 1);
217
+ if (c1 === 120 || c1 === 88) {
218
+ p += 2;
219
+ radix = 16;
220
+ }
221
+ else if (c1 === 111 || c1 === 79) {
222
+ p += 2;
223
+ radix = 8;
224
+ }
225
+ else if (c1 === 98 || c1 === 66) {
226
+ p += 2;
227
+ radix = 2;
228
+ }
229
+ if (radix !== 10) {
230
+ /* prefix is present */
231
+ if (to_digit(s.at(p)) >= radix)
232
+ return json_unexpected_char(s, p);
233
+ const p_digits = p;
234
+ while (to_digit(s.at(p)) < radix)
235
+ p++;
236
+ /* parseInt is exact for the radixes 2, 8 and 16 */
237
+ d = parseInt(s.buf.substring(p_digits, p), radix);
238
+ if (s.at(p_start) === 45)
239
+ d = -d;
240
+ break done;
241
+ }
242
+ }
243
+ if (is_digit(s.at(p + 1)))
244
+ return json_parse_error_pos(s, p, 'Unexpected number');
269
245
  }
270
- if (radix !== 10) {
271
- /* there must be a digit after the prefix */
272
- if (to_digit(s.at(p)) >= radix)
273
- return json_parse_error_pos(s, p, 'invalid number literal');
246
+ while (is_digit(s.at(p)))
247
+ p++;
248
+ if (s.at(p) === 46 /* . */) {
249
+ p++;
250
+ if (!is_digit(s.at(p)))
251
+ return json_parse_error_pos(s, p, 'Unterminated fractional number');
252
+ while (is_digit(s.at(p)))
253
+ p++;
274
254
  }
275
- }
276
- let is_float = false;
277
- p_start = p;
278
- while (to_digit(s.at(p)) < radix)
279
- p++;
280
- if (s.at(p) === 46 /* . */ && (p > p_start || to_digit(s.at(p + 1)) < radix)) {
281
- is_float = true;
282
- p++;
283
- while (to_digit(s.at(p)) < radix)
255
+ if (s.at(p) === 101 /* e */ || s.at(p) === 69 /* E */) {
284
256
  p++;
285
- }
286
- if (p > p_start) {
287
- const c = s.at(p);
288
- if (((c === 101 || c === 69) && radix === 10) ||
289
- ((c === 112 || c === 80) && (radix === 2 || radix === 8 || radix === 16))) {
290
- let p1 = p + 1;
291
- is_float = true;
292
- if (s.at(p1) === 43 || s.at(p1) === 45)
293
- p1++;
294
- if (is_digit(s.at(p1))) {
295
- p = p1 + 1;
296
- while (is_digit(s.at(p)))
297
- p++;
298
- }
257
+ if (s.at(p) === 43 || s.at(p) === 45)
258
+ p++;
259
+ if (!is_digit(s.at(p)))
260
+ return json_parse_error_pos(s, p, 'Exponent part is missing a number');
261
+ while (is_digit(s.at(p)))
262
+ p++;
299
263
  }
264
+ d = Number(s.buf.substring(p_start, p));
300
265
  }
301
- if (p === p_start)
302
- return json_parse_error_pos(s, p, 'invalid number literal');
303
- const text = s.buf.substring(p_start, p);
304
- let d;
305
- if (radix === 10) {
306
- d = Number(text);
307
- }
308
- else if (is_float) {
309
- return json_parse_error_pos(s, p_start, 'invalid number literal');
310
- }
311
- else {
312
- d = parseInt(text, radix);
313
- }
314
- if (is_neg)
315
- d = -d;
316
266
  s.tok = -3 /* JSONTok.NUMBER */;
317
267
  s.tok_num = d;
318
268
  s.buf_ptr = p;
@@ -399,16 +349,14 @@ function json_next_token(s) {
399
349
  }
400
350
  break; /* def_token */
401
351
  case 43: /* + */
402
- if (!s.ext_json || !is_digit(s.at(p + 1)))
352
+ if (!s.ext_json)
403
353
  break;
404
354
  return json_parse_number(s, p);
405
- case 48: /* 0 */
406
- if (is_digit(s.at(p + 1)))
355
+ case 46: /* . */
356
+ if (!s.ext_json || !is_digit(s.at(p + 1)))
407
357
  break;
408
358
  return json_parse_number(s, p);
409
359
  case 45: /* - */
410
- if (!is_digit(s.at(p + 1)))
411
- break;
412
360
  return json_parse_number(s, p);
413
361
  default:
414
362
  if (is_digit(c))
@@ -449,7 +397,13 @@ function json_parse_expect(s, tok) {
449
397
  }
450
398
  /* current token (through a function so that TypeScript does not narrow s.tok) */
451
399
  function cur_tok(s) { return s.tok; }
452
- function json_parse_value(s, depth) {
400
+ function json_parse_record_init_primitive(s, pr, val) {
401
+ pr.value = val;
402
+ pr.source_pos = s.tok_ptr;
403
+ pr.source_len = s.buf_ptr - s.tok_ptr;
404
+ }
405
+ /* 'pr' can be null */
406
+ function json_parse_value(s, depth, pr) {
453
407
  const ctx = s.ctx;
454
408
  let val = null;
455
409
  if (depth > ctx.rt.max_stack_depth)
@@ -463,6 +417,10 @@ function json_parse_value(s, depth) {
463
417
  val = JS_NewObject(ctx);
464
418
  if (JS_IsException(val))
465
419
  return val;
420
+ if (pr) {
421
+ pr.value = val;
422
+ pr.entries = new Map();
423
+ }
466
424
  if (cur_tok(s) !== 125 /* } */) {
467
425
  for (;;) {
468
426
  let prop_name;
@@ -482,7 +440,12 @@ function json_parse_value(s, depth) {
482
440
  return JS_EXCEPTION;
483
441
  if (json_parse_expect(s, 58 /* : */))
484
442
  return JS_EXCEPTION;
485
- const prop_val = json_parse_value(s, depth + 1);
443
+ let pr1 = null;
444
+ if (pr) {
445
+ pr1 = { value: undefined };
446
+ pr.entries.set(prop_name, pr1);
447
+ }
448
+ const prop_val = json_parse_value(s, depth + 1, pr1);
486
449
  if (JS_IsException(prop_val))
487
450
  return JS_EXCEPTION;
488
451
  const ret = JS_DefinePropertyValue(ctx, val, prop_name, prop_val, JS_PROP_C_W_E);
@@ -507,10 +470,19 @@ function json_parse_value(s, depth) {
507
470
  val = JS_NewArray(ctx);
508
471
  if (JS_IsException(val))
509
472
  return val;
473
+ if (pr) {
474
+ pr.value = val;
475
+ pr.elements = [];
476
+ }
510
477
  if (cur_tok(s) !== 93 /* ] */) {
511
478
  let idx = 0;
512
479
  for (;;) {
513
- const el = json_parse_value(s, depth + 1);
480
+ let pr1 = null;
481
+ if (pr) {
482
+ pr1 = { value: undefined };
483
+ pr.elements.push(pr1);
484
+ }
485
+ const el = json_parse_value(s, depth + 1, pr1);
514
486
  if (JS_IsException(el))
515
487
  return JS_EXCEPTION;
516
488
  const ret = JS_DefinePropertyValueUint32(ctx, val, idx, el, JS_PROP_C_W_E);
@@ -531,20 +503,36 @@ function json_parse_value(s, depth) {
531
503
  break;
532
504
  case -2 /* JSONTok.STRING */:
533
505
  val = s.tok_str;
506
+ if (pr)
507
+ json_parse_record_init_primitive(s, pr, val);
534
508
  if (json_next_token(s))
535
509
  return JS_EXCEPTION;
536
510
  break;
537
511
  case -3 /* JSONTok.NUMBER */:
538
512
  val = s.tok_num;
513
+ if (pr)
514
+ json_parse_record_init_primitive(s, pr, val);
539
515
  if (json_next_token(s))
540
516
  return JS_EXCEPTION;
541
517
  break;
542
518
  case -4 /* JSONTok.IDENT */:
543
519
  if (s.tok_atom === JS_ATOM_false || s.tok_atom === JS_ATOM_true) {
544
520
  val = (s.tok_atom === JS_ATOM_true);
521
+ if (pr)
522
+ json_parse_record_init_primitive(s, pr, val);
545
523
  }
546
524
  else if (s.tok_atom === JS_ATOM_null) {
547
525
  val = null;
526
+ if (pr)
527
+ json_parse_record_init_primitive(s, pr, val);
528
+ }
529
+ else if (s.tok_atom === JS_ATOM_NaN && s.ext_json) {
530
+ /* Note: json5 identifier handling is ambiguous e.g. is
531
+ '{ NaN: 1 }' a valid JSON5 production ? */
532
+ val = NaN;
533
+ }
534
+ else if (s.tok_atom === JS_ATOM_Infinity && s.ext_json) {
535
+ val = Infinity;
548
536
  }
549
537
  else {
550
538
  return json_unexpected_token(s);
@@ -566,12 +554,13 @@ function json_unexpected_token(s) {
566
554
  }
567
555
  return JS_EXCEPTION;
568
556
  }
569
- export function JS_ParseJSON2(ctx, buf, filename, flags) {
557
+ /* 'pr' (can be null) receives the parse record of the value */
558
+ export function JS_ParseJSON3(ctx, buf, filename, flags, pr) {
570
559
  const s = new JSONParseState(ctx, buf, filename);
571
560
  s.ext_json = (flags & JS_PARSE_JSON_EXT) !== 0;
572
561
  if (json_next_token(s))
573
562
  return JS_EXCEPTION;
574
- const val = json_parse_value(s, 0);
563
+ const val = json_parse_value(s, 0, pr);
575
564
  if (JS_IsException(val))
576
565
  return val;
577
566
  if (s.tok !== -1 /* JSONTok.EOF */) {
@@ -580,18 +569,36 @@ export function JS_ParseJSON2(ctx, buf, filename, flags) {
580
569
  }
581
570
  return val;
582
571
  }
572
+ export function JS_ParseJSON2(ctx, buf, filename, flags) {
573
+ return JS_ParseJSON3(ctx, buf, filename, flags, null);
574
+ }
583
575
  export function JS_ParseJSON(ctx, buf, filename) {
584
576
  return JS_ParseJSON2(ctx, buf, filename, 0);
585
577
  }
586
578
  /* ---------------------------------------------------------------------
587
579
  JSON.parse
588
580
  --------------------------------------------------------------------- */
589
- function internalize_json_property(ctx, holder, name, reviver, depth) {
581
+ /* if pr !== null, then pr.value === holder by construction */
582
+ function internalize_json_property(ctx, holder, name, reviver, text_str, pr, depth) {
590
583
  if (depth > ctx.rt.max_stack_depth)
591
584
  return JS_ThrowStackOverflow(ctx);
592
585
  const val = JS_GetProperty(ctx, holder, name);
593
586
  if (JS_IsException(val))
594
587
  return val;
588
+ if (pr) {
589
+ if (pr.elements) {
590
+ pr = __JS_AtomIsTaggedInt(name) ? (pr.elements[__JS_AtomToUInt32(name)] ?? null) : null;
591
+ }
592
+ else {
593
+ pr = pr.entries?.get(name) ?? null;
594
+ }
595
+ /* the source text is only given for an unmodified value */
596
+ if (pr && !js_same_value(ctx, pr.value, val))
597
+ pr = null;
598
+ }
599
+ const context = JS_NewObject(ctx);
600
+ if (JS_IsException(context))
601
+ return JS_EXCEPTION;
595
602
  if (JS_IsObject(val)) {
596
603
  const is_array = JS_IsArray(ctx, val);
597
604
  if (is_array < 0)
@@ -617,7 +624,7 @@ function internalize_json_property(ctx, holder, name, reviver, depth) {
617
624
  else {
618
625
  prop = atoms[i].atom;
619
626
  }
620
- const new_el = internalize_json_property(ctx, val, prop, reviver, depth + 1);
627
+ const new_el = internalize_json_property(ctx, val, prop, reviver, text_str, pr, depth + 1);
621
628
  if (JS_IsException(new_el))
622
629
  return JS_EXCEPTION;
623
630
  let ret;
@@ -631,36 +638,89 @@ function internalize_json_property(ctx, holder, name, reviver, depth) {
631
638
  return JS_EXCEPTION;
632
639
  }
633
640
  }
641
+ else if (pr) {
642
+ const source = text_str.substring(pr.source_pos, pr.source_pos + pr.source_len);
643
+ if (JS_DefinePropertyValueStr(ctx, context, 'source', source, JS_PROP_C_W_E) < 0)
644
+ return JS_EXCEPTION;
645
+ }
634
646
  const name_val = JS_AtomToValue(ctx, name);
635
647
  if (JS_IsException(name_val))
636
648
  return JS_EXCEPTION;
637
- return JS_Call(ctx, reviver, holder, [name_val, val]);
649
+ return JS_Call(ctx, reviver, holder, [name_val, val, context]);
638
650
  }
639
651
  function js_json_parse(ctx, this_val, argv) {
640
652
  const str = JS_ToString(ctx, argv[0]);
641
653
  if (JS_IsException(str))
642
654
  return JS_EXCEPTION;
655
+ if (argv.length > 1 && JS_IsFunction(ctx, argv[1])) {
656
+ /* the ported parser, which keeps the parse records giving the reviver
657
+ the source text of the primitives (its third argument) */
658
+ const reviver = argv[1];
659
+ const root = JS_NewObject(ctx);
660
+ if (JS_IsException(root))
661
+ return JS_EXCEPTION;
662
+ const pr1 = { value: undefined };
663
+ const pr = { value: root, entries: new Map([[JS_ATOM_empty_string, pr1]]) };
664
+ const obj = JS_ParseJSON3(ctx, str, '<input>', 0, pr1);
665
+ if (JS_IsException(obj))
666
+ return obj;
667
+ if (JS_DefinePropertyValue(ctx, root, JS_ATOM_empty_string, obj, JS_PROP_C_W_E) < 0)
668
+ return JS_EXCEPTION;
669
+ return internalize_json_property(ctx, root, JS_ATOM_empty_string, reviver, str, pr, 0);
670
+ }
643
671
  /* fast path: the host parser, with the tree re-prototyped to the guest
644
672
  intrinsics. On a syntax error the ported parser runs to produce the
645
673
  QuickJS error (message, line and column). */
646
- let obj;
647
674
  try {
648
- obj = fix_deep_json(ctx, JSON.parse(str));
675
+ return fix_deep_json(ctx, JSON.parse(str));
649
676
  }
650
677
  catch {
651
- obj = JS_ParseJSON(ctx, str, '<input>');
678
+ return JS_ParseJSON(ctx, str, '<input>');
652
679
  }
680
+ }
681
+ /* JSON.rawJSON objects. The host's are used when it has them, so that the
682
+ host serializer of the JSON.stringify fast path emits their text;
683
+ otherwise they are null-prototype objects recorded here, and
684
+ JSON.stringify uses the ported serializer once one exists. */
685
+ /* (exported so that a test can run without the host functions) */
686
+ export const json_host_raw = {
687
+ rawJSON: JSON.rawJSON,
688
+ isRawJSON: JSON.isRawJSON,
689
+ };
690
+ const guest_raw_json = new WeakSet();
691
+ let guest_raw_json_used = false;
692
+ function js_is_raw_json(v) {
693
+ if (!Q.JS_IsObject(v))
694
+ return false;
695
+ if (json_host_raw.isRawJSON && json_host_raw.isRawJSON(v))
696
+ return true;
697
+ return guest_raw_json_used && guest_raw_json.has(v);
698
+ }
699
+ function js_json_isRawJSON(ctx, this_val, argv) {
700
+ return js_is_raw_json(argv[0]);
701
+ }
702
+ function is_valid_raw_json_char(c) {
703
+ return (c >= 97 && c <= 122) || (c >= 48 && c <= 57) || c === 45 /* - */ || c === 34 /* " */;
704
+ }
705
+ function js_json_rawJSON(ctx, this_val, argv) {
706
+ const str = JS_ToString(ctx, argv[0]);
707
+ if (JS_IsException(str))
708
+ return str;
709
+ const s = str;
710
+ if (s.length === 0 || !is_valid_raw_json_char(s.charCodeAt(0)) || !is_valid_raw_json_char(s.charCodeAt(s.length - 1)) ||
711
+ JS_IsException(js_json_parse(ctx, undefined, [s]))) {
712
+ return JS_ThrowSyntaxError(ctx, 'invalid rawJSON string');
713
+ }
714
+ if (json_host_raw.rawJSON)
715
+ return json_host_raw.rawJSON(s);
716
+ const obj = Q.JS_NewObjectProtoClass(ctx, null, 1 /* JSClassID.OBJECT */);
653
717
  if (JS_IsException(obj))
654
718
  return obj;
655
- if (argv.length > 1 && JS_IsFunction(ctx, argv[1])) {
656
- const reviver = argv[1];
657
- const root = JS_NewObject(ctx);
658
- if (JS_IsException(root))
659
- return JS_EXCEPTION;
660
- if (JS_DefinePropertyValue(ctx, root, JS_ATOM_empty_string, obj, JS_PROP_C_W_E) < 0)
661
- return JS_EXCEPTION;
662
- obj = internalize_json_property(ctx, root, JS_ATOM_empty_string, reviver, 0);
663
- }
719
+ if (JS_DefinePropertyValueStr(ctx, obj, 'rawJSON', s, JS_PROP_ENUMERABLE) < 0)
720
+ return JS_EXCEPTION;
721
+ Q.JS_PreventExtensions(ctx, obj);
722
+ guest_raw_json.add(obj);
723
+ guest_raw_json_used = true;
664
724
  return obj;
665
725
  }
666
726
  function js_json_check(ctx, jsc, holder, val, key) {
@@ -721,10 +781,17 @@ function js_json_to_str(ctx, jsc, holder, val, indent, depth) {
721
781
  if (JS_IsException(val))
722
782
  return -1;
723
783
  }
724
- else if (cl === 6 /* JSClassID.BOOLEAN */ || cl === 33 /* JSClassID.BIG_INT */) {
784
+ else if (cl === 6 /* JSClassID.BOOLEAN */ || cl === 34 /* JSClassID.BIG_INT */) {
725
785
  /* This will throw the same error as for the primitive object */
726
786
  val = Q.JS_GetAnyOpaque(p);
727
787
  }
788
+ else if (js_is_raw_json(p)) {
789
+ const raw = Q.JS_GetPropertyStr(ctx, p, 'rawJSON');
790
+ if (JS_IsException(raw))
791
+ return -1;
792
+ jsc.b.push(String(raw));
793
+ return 0;
794
+ }
728
795
  else {
729
796
  if (jsc.stack.includes(p)) {
730
797
  JS_ThrowTypeError(ctx, 'circular reference');
@@ -959,18 +1026,23 @@ function js_json_stringify(ctx, this_val, argv) {
959
1026
  /* fast path: the host serializer (toJSON/replacer are guest functions,
960
1027
  called natively). A host TypeError (BigInt, cycle) is reported by the
961
1028
  ported serializer with the QuickJS message; guest exceptions thrown by
962
- toJSON/replacer/getters propagate. */
963
- try {
964
- return JSON.stringify(argv[0], argv[1], argv[2]);
965
- }
966
- catch (e) {
967
- if (!(e instanceof TypeError))
968
- return Q.js_host_exception_to_guest(ctx, e);
1029
+ toJSON/replacer/getters propagate. The host serializer does not know
1030
+ the guest's own JSON.rawJSON objects (made when the host has none). */
1031
+ if (!guest_raw_json_used) {
1032
+ try {
1033
+ return JSON.stringify(argv[0], argv[1], argv[2]);
1034
+ }
1035
+ catch (e) {
1036
+ if (!(e instanceof TypeError))
1037
+ return Q.js_host_exception_to_guest(ctx, e);
1038
+ }
969
1039
  }
970
1040
  return JS_JSONStringify(ctx, argv[0], argv[1], argv[2]);
971
1041
  }
972
1042
  const js_json_funcs = [
1043
+ JS_CFUNC_DEF('isRawJSON', 1, js_json_isRawJSON),
973
1044
  JS_CFUNC_DEF('parse', 2, js_json_parse),
1045
+ JS_CFUNC_DEF('rawJSON', 1, js_json_rawJSON),
974
1046
  JS_CFUNC_DEF('stringify', 3, js_json_stringify),
975
1047
  JS_PROP_STRING_DEF(JS_ATOM_Symbol_toStringTag, 'JSON', JS_PROP_CONFIGURABLE),
976
1048
  ];