@mikrojs/quickjs 0.8.0-pr-115.g87a99f9 → 0.8.0

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.
@@ -176,6 +176,7 @@ enum {
176
176
  JS_CLASS_STRING_ITERATOR, /* u.array_iterator_data */
177
177
  JS_CLASS_REGEXP_STRING_ITERATOR, /* u.regexp_string_iterator_data */
178
178
  JS_CLASS_GENERATOR, /* u.generator_data */
179
+ JS_CLASS_DISPOSABLE_STACK,
179
180
  JS_CLASS_PROXY, /* u.proxy_data */
180
181
  JS_CLASS_PROMISE, /* u.promise_data */
181
182
  JS_CLASS_PROMISE_RESOLVE_FUNCTION, /* u.promise_function_data */
@@ -186,6 +187,7 @@ enum {
186
187
  JS_CLASS_ASYNC_FROM_SYNC_ITERATOR, /* u.async_from_sync_iterator_data */
187
188
  JS_CLASS_ASYNC_GENERATOR_FUNCTION, /* u.func */
188
189
  JS_CLASS_ASYNC_GENERATOR, /* u.async_generator_data */
190
+ JS_CLASS_ASYNC_DISPOSABLE_STACK,
189
191
  JS_CLASS_WEAK_REF,
190
192
  JS_CLASS_FINALIZATION_REGISTRY,
191
193
  JS_CLASS_DOM_EXCEPTION,
@@ -209,6 +211,7 @@ typedef enum JSErrorEnum {
209
211
  JS_URI_ERROR,
210
212
  JS_INTERNAL_ERROR,
211
213
  JS_AGGREGATE_ERROR,
214
+ JS_SUPPRESSED_ERROR,
212
215
 
213
216
  JS_NATIVE_ERROR_COUNT, /* number of different NativeError objects */
214
217
  JS_PLAIN_ERROR = JS_NATIVE_ERROR_COUNT
@@ -702,6 +705,10 @@ typedef struct JSClosureVar {
702
705
  typedef struct JSVarScope {
703
706
  int parent; /* index into fd->scopes of the enclosing scope */
704
707
  int first; /* index into fd->vars of the last variable in this scope */
708
+ uint8_t has_using : 1; /* scope has using declarations */
709
+ uint8_t is_await_using : 1; /* scope has await using declarations */
710
+ int using_label_catch; /* label for catch handler (-1 if none) */
711
+ int using_label_end; /* label for end of disposal block (-1 if none) */
705
712
  } JSVarScope;
706
713
 
707
714
  typedef enum {
@@ -717,6 +724,10 @@ typedef enum {
717
724
  JS_VAR_PRIVATE_GETTER,
718
725
  JS_VAR_PRIVATE_SETTER, /* must come after JS_VAR_PRIVATE_GETTER */
719
726
  JS_VAR_PRIVATE_GETTER_SETTER, /* must come after JS_VAR_PRIVATE_SETTER */
727
+ JS_VAR_USING, /* using declaration variable */
728
+ JS_VAR_USING_METHOD, /* hidden local holding the cached dispose method
729
+ for the preceding JS_VAR_USING var (always
730
+ allocated immediately after it). */
720
731
  } JSVarKindEnum;
721
732
 
722
733
  /* XXX: could use a different structure in bytecode functions to save
@@ -1135,7 +1146,7 @@ enum {
1135
1146
  #undef DEF
1136
1147
  JS_ATOM_END,
1137
1148
  };
1138
- #define JS_ATOM_LAST_KEYWORD JS_ATOM_super
1149
+ #define JS_ATOM_LAST_KEYWORD JS_ATOM_using
1139
1150
  #define JS_ATOM_LAST_STRICT_KEYWORD JS_ATOM_yield
1140
1151
 
1141
1152
  static const char js_atom_init[] =
@@ -1200,6 +1211,8 @@ static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen,
1200
1211
  JSValue val, bool is_array_ctor);
1201
1212
  static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj,
1202
1213
  JSValueConst val, int flags, int scope_idx);
1214
+ static JSValue js_new_suppressed_error(JSContext *ctx, JSValueConst error,
1215
+ JSValueConst suppressed);
1203
1216
  static __maybe_unused void JS_DumpString(JSRuntime *rt, JSString *p);
1204
1217
  static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt);
1205
1218
  static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p);
@@ -1270,6 +1283,9 @@ static void js_promise_mark(JSRuntime *rt, JSValueConst val,
1270
1283
  static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValueConst val);
1271
1284
  static void js_promise_resolve_function_mark(JSRuntime *rt, JSValueConst val,
1272
1285
  JS_MarkFunc *mark_func);
1286
+ static void js_disposable_stack_finalizer(JSRuntime *rt, JSValueConst val);
1287
+ static void js_disposable_stack_mark(JSRuntime *rt, JSValueConst val,
1288
+ JS_MarkFunc *mark_func);
1273
1289
 
1274
1290
  #define HINT_STRING 0
1275
1291
  #define HINT_NUMBER 1
@@ -1529,6 +1545,12 @@ static JSValue js_number(double d)
1529
1545
  return js_float64(d);
1530
1546
  }
1531
1547
 
1548
+ static JSValue __JS_NewShortBigInt(JSContext *ctx, int32_t d)
1549
+ {
1550
+ (void)&ctx;
1551
+ return JS_MKVAL(JS_TAG_SHORT_BIG_INT, d);
1552
+ }
1553
+
1532
1554
  JSValue JS_NewNumber(JSContext *ctx, double d)
1533
1555
  {
1534
1556
  return js_number(d);
@@ -1888,6 +1910,7 @@ static JSClassShortDef const js_std_class_def[] = {
1888
1910
  { JS_ATOM_String_Iterator, js_array_iterator_finalizer, js_array_iterator_mark }, /* JS_CLASS_STRING_ITERATOR */
1889
1911
  { JS_ATOM_RegExp_String_Iterator, js_regexp_string_iterator_finalizer, js_regexp_string_iterator_mark }, /* JS_CLASS_REGEXP_STRING_ITERATOR */
1890
1912
  { JS_ATOM_Generator, js_generator_finalizer, js_generator_mark }, /* JS_CLASS_GENERATOR */
1913
+ { JS_ATOM_DisposableStack, js_disposable_stack_finalizer, js_disposable_stack_mark }, /* JS_CLASS_DISPOSABLE_STACK */
1891
1914
  };
1892
1915
 
1893
1916
  static int init_class_range(JSRuntime *rt, JSClassShortDef const *tab,
@@ -2265,6 +2288,7 @@ void JS_SetRuntimeInfo(JSRuntime *rt, const char *s)
2265
2288
  void JS_FreeRuntime(JSRuntime *rt)
2266
2289
  {
2267
2290
  struct list_head *el, *el1;
2291
+ bool leak = false;
2268
2292
  int i;
2269
2293
 
2270
2294
  rt->in_free = true;
@@ -2305,6 +2329,7 @@ void JS_FreeRuntime(JSRuntime *rt)
2305
2329
  header_done = true;
2306
2330
  }
2307
2331
  JS_DumpGCObject(rt, p);
2332
+ leak = true;
2308
2333
  }
2309
2334
  }
2310
2335
 
@@ -2381,6 +2406,7 @@ void JS_FreeRuntime(JSRuntime *rt)
2381
2406
  } else {
2382
2407
  printf("\n");
2383
2408
  }
2409
+ leak = true;
2384
2410
  }
2385
2411
  }
2386
2412
  }
@@ -2429,6 +2455,7 @@ void JS_FreeRuntime(JSRuntime *rt)
2429
2455
  }
2430
2456
  if (rt->rt_info)
2431
2457
  printf("\n");
2458
+ leak = true;
2432
2459
  }
2433
2460
  #endif
2434
2461
 
@@ -2448,14 +2475,20 @@ void JS_FreeRuntime(JSRuntime *rt)
2448
2475
  printf("Memory leak: %zd bytes lost in %zd block%s\n",
2449
2476
  s->malloc_size - sizeof(JSRuntime),
2450
2477
  s->malloc_count - 1, &"s"[s->malloc_count == 2]);
2478
+ leak = true;
2451
2479
  }
2452
2480
  }
2453
2481
  #endif
2454
2482
 
2483
+ leak &= check_dump_flag(rt, JS_ABORT_ON_LEAKS);
2484
+
2455
2485
  {
2456
2486
  JSMallocState *ms = &rt->malloc_state;
2457
2487
  rt->mf.js_free(ms->opaque, rt);
2458
2488
  }
2489
+
2490
+ if (leak)
2491
+ abort();
2459
2492
  }
2460
2493
 
2461
2494
  JSContext *JS_NewContextRaw(JSRuntime *rt)
@@ -2500,7 +2533,6 @@ JSContext *JS_NewContextRaw(JSRuntime *rt)
2500
2533
  JSContext *JS_NewContext(JSRuntime *rt)
2501
2534
  {
2502
2535
  JSContext *ctx;
2503
-
2504
2536
  ctx = JS_NewContextRaw(rt);
2505
2537
  if (!ctx)
2506
2538
  return NULL;
@@ -2515,7 +2547,7 @@ JSContext *JS_NewContext(JSRuntime *rt)
2515
2547
  JS_AddIntrinsicTypedArrays(ctx) ||
2516
2548
  JS_AddIntrinsicPromise(ctx) ||
2517
2549
  JS_AddIntrinsicWeakRef(ctx) ||
2518
- JS_AddIntrinsicDOMException(ctx) ||
2550
+ JS_AddIntrinsicAToB(ctx) ||
2519
2551
  JS_AddPerformance(ctx)) {
2520
2552
  JS_FreeContext(ctx);
2521
2553
  return NULL;
@@ -3404,10 +3436,23 @@ static JSValue JS_NewSymbolFromAtom(JSContext *ctx, JSAtom descr,
3404
3436
  /* `description` may be pure ASCII or UTF-8 encoded */
3405
3437
  JSValue JS_NewSymbol(JSContext *ctx, const char *description, bool is_global)
3406
3438
  {
3439
+ if (description == NULL) {
3440
+ if (!is_global) {
3441
+ /* Local symbol without description: Symbol() */
3442
+ return JS_NewSymbolInternal(ctx, NULL, JS_ATOM_TYPE_SYMBOL);
3443
+ }
3444
+ /* Global symbol without description: Symbol.for()
3445
+ Per ES spec, ToString(undefined) becomes "undefined" */
3446
+ description = "undefined";
3447
+ }
3407
3448
  JSAtom atom = JS_NewAtom(ctx, description);
3408
3449
  if (atom == JS_ATOM_NULL)
3409
3450
  return JS_EXCEPTION;
3410
- return JS_NewSymbolFromAtom(ctx, atom, is_global ? JS_ATOM_TYPE_GLOBAL_SYMBOL : JS_ATOM_TYPE_SYMBOL);
3451
+ int atom_type =
3452
+ is_global ? JS_ATOM_TYPE_GLOBAL_SYMBOL : JS_ATOM_TYPE_SYMBOL;
3453
+ JSValue symbol = JS_NewSymbolFromAtom(ctx, atom, atom_type);
3454
+ JS_FreeAtom(ctx, atom);
3455
+ return symbol;
3411
3456
  }
3412
3457
 
3413
3458
  #define ATOM_GET_STR_BUF_SIZE 64
@@ -4329,18 +4374,18 @@ JSValue JS_NewStringLen(JSContext *ctx, const char *buf, size_t buf_len)
4329
4374
  size_t len;
4330
4375
  int kind;
4331
4376
 
4332
- if (buf_len <= 0) {
4377
+ if (unlikely(buf_len <= 0))
4333
4378
  return js_empty_string(ctx->rt);
4334
- }
4379
+
4335
4380
  /* Compute string kind and length: 7-bit, 8-bit, 16-bit, 16-bit UTF-16 */
4336
4381
  kind = utf8_scan(buf, buf_len, &len);
4337
- if (len > JS_STRING_LEN_MAX)
4382
+ if (unlikely(len > JS_STRING_LEN_MAX))
4338
4383
  return JS_ThrowRangeError(ctx, "invalid string length");
4339
4384
 
4340
4385
  switch (kind) {
4341
4386
  case UTF8_PLAIN_ASCII:
4342
4387
  str = js_alloc_string(ctx, len, 0);
4343
- if (!str)
4388
+ if (unlikely(!str))
4344
4389
  return JS_EXCEPTION;
4345
4390
  memcpy(str8(str), buf, len);
4346
4391
  str8(str)[len] = '\0';
@@ -4348,7 +4393,7 @@ JSValue JS_NewStringLen(JSContext *ctx, const char *buf, size_t buf_len)
4348
4393
  case UTF8_NON_ASCII:
4349
4394
  /* buf contains non-ASCII code-points, but limited to 8-bit values */
4350
4395
  str = js_alloc_string(ctx, len, 0);
4351
- if (!str)
4396
+ if (unlikely(!str))
4352
4397
  return JS_EXCEPTION;
4353
4398
  utf8_decode_buf8(str8(str), len + 1, buf, buf_len);
4354
4399
  break;
@@ -4357,7 +4402,7 @@ JSValue JS_NewStringLen(JSContext *ctx, const char *buf, size_t buf_len)
4357
4402
  //if (kind & UTF8_HAS_ERRORS)
4358
4403
  // return JS_ThrowRangeError(ctx, "invalid UTF-8 sequence");
4359
4404
  str = js_alloc_string(ctx, len, 1);
4360
- if (!str)
4405
+ if (unlikely(!str))
4361
4406
  return JS_EXCEPTION;
4362
4407
  utf8_decode_buf16(str16(str), len, buf, buf_len);
4363
4408
  break;
@@ -4369,10 +4414,13 @@ JSValue JS_NewStringUTF16(JSContext *ctx, const uint16_t *buf, size_t len)
4369
4414
  {
4370
4415
  JSString *str;
4371
4416
 
4372
- if (!len)
4417
+ if (unlikely(!len))
4373
4418
  return js_empty_string(ctx->rt);
4419
+ if (unlikely(len > JS_STRING_LEN_MAX))
4420
+ return JS_ThrowRangeError(ctx, "invalid string length");
4421
+
4374
4422
  str = js_alloc_string(ctx, len, 1);
4375
- if (!str)
4423
+ if (unlikely(!str))
4376
4424
  return JS_EXCEPTION;
4377
4425
  memcpy(str16(str), buf, len * sizeof(*buf));
4378
4426
  return JS_MKPTR(JS_TAG_STRING, str);
@@ -6460,7 +6508,7 @@ static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref)
6460
6508
  static void js_array_finalizer(JSRuntime *rt, JSValueConst val)
6461
6509
  {
6462
6510
  JSObject *p = JS_VALUE_GET_OBJ(val);
6463
- int i;
6511
+ uint32_t i;
6464
6512
 
6465
6513
  for(i = 0; i < p->u.array.count; i++) {
6466
6514
  JS_FreeValueRT(rt, p->u.array.u.values[i]);
@@ -6472,7 +6520,7 @@ static void js_array_mark(JSRuntime *rt, JSValueConst val,
6472
6520
  JS_MarkFunc *mark_func)
6473
6521
  {
6474
6522
  JSObject *p = JS_VALUE_GET_OBJ(val);
6475
- int i;
6523
+ uint32_t i;
6476
6524
 
6477
6525
  for(i = 0; i < p->u.array.count; i++) {
6478
6526
  JS_MarkValue(rt, p->u.array.u.values[i], mark_func);
@@ -7720,7 +7768,7 @@ static void build_backtrace(JSContext *ctx, JSValueConst error_val,
7720
7768
  int line_num, int col_num, int backtrace_flags)
7721
7769
  {
7722
7770
  JSStackFrame *sf, *sf_start;
7723
- JSValue stack, prepare, saved_exception;
7771
+ JSValue stack, prepare, saved_exception, error_obj;
7724
7772
  DynBuf dbuf;
7725
7773
  const char *func_name_str;
7726
7774
  const char *str1;
@@ -7737,6 +7785,7 @@ static void build_backtrace(JSContext *ctx, JSValueConst error_val,
7737
7785
  if (rt->in_build_stack_trace)
7738
7786
  return;
7739
7787
  rt->in_build_stack_trace = true;
7788
+ error_obj = js_dup(error_val);
7740
7789
 
7741
7790
  // Save exception because conversion to double may fail.
7742
7791
  saved_exception = JS_GetException(ctx);
@@ -7882,7 +7931,7 @@ static void build_backtrace(JSContext *ctx, JSValueConst error_val,
7882
7931
  JS_FreeValue(ctx, csd[k].func_name);
7883
7932
  }
7884
7933
  JSValueConst args[] = {
7885
- error_val,
7934
+ error_obj,
7886
7935
  stack,
7887
7936
  };
7888
7937
  JSValue stack2 = JS_Call(ctx, prepare, ctx->error_ctor, countof(args), args);
@@ -7903,13 +7952,14 @@ static void build_backtrace(JSContext *ctx, JSValueConst error_val,
7903
7952
 
7904
7953
  if (JS_IsUndefined(ctx->error_back_trace))
7905
7954
  ctx->error_back_trace = js_dup(stack);
7906
- if (has_filter_func || can_add_backtrace(error_val)) {
7907
- JS_DefinePropertyValue(ctx, error_val, JS_ATOM_stack, stack,
7955
+ if (has_filter_func || can_add_backtrace(error_obj)) {
7956
+ JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_stack, stack,
7908
7957
  JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
7909
7958
  } else {
7910
7959
  JS_FreeValue(ctx, stack);
7911
7960
  }
7912
7961
 
7962
+ JS_FreeValue(ctx, error_obj);
7913
7963
  rt->in_build_stack_trace = false;
7914
7964
  }
7915
7965
 
@@ -9878,6 +9928,7 @@ static int set_array_length(JSContext *ctx, JSObject *p, JSValue val,
9878
9928
  if (len < old_len) {
9879
9929
  for(i = len; i < old_len; i++) {
9880
9930
  JS_FreeValue(ctx, p->u.array.u.values[i]);
9931
+ p->u.array.u.values[i] = JS_UNDEFINED;
9881
9932
  }
9882
9933
  p->u.array.count = len;
9883
9934
  }
@@ -9956,13 +10007,18 @@ static int expand_fast_array(JSContext *ctx, JSObject *p, uint32_t new_len)
9956
10007
  size_t slack;
9957
10008
  JSValue *new_array_prop;
9958
10009
 
10010
+ if (unlikely(new_len > (uint32_t)INT32_MAX)) {
10011
+ JS_ThrowOutOfMemory(ctx);
10012
+ return -1;
10013
+ }
10014
+
9959
10015
  old_size = p->u.array.u1.size;
9960
- new_size = old_size + old_size/2; // grow by 50%
9961
- if (new_size < old_size) { // integer overflow
10016
+ new_size = old_size + old_size/2;
10017
+ if (new_size < old_size) {
9962
10018
  JS_ThrowOutOfMemory(ctx);
9963
10019
  return -1;
9964
10020
  }
9965
- new_size = max_int(new_len, new_size);
10021
+ new_size = max_uint32(new_len, new_size);
9966
10022
  new_array_prop = js_realloc2(ctx, p->u.array.u.values, sizeof(JSValue) * new_size, &slack);
9967
10023
  if (!new_array_prop)
9968
10024
  return -1;
@@ -10004,6 +10060,35 @@ static int add_fast_array_element(JSContext *ctx, JSObject *p,
10004
10060
  return true;
10005
10061
  }
10006
10062
 
10063
+ /* Allocate a new fast array initialized to JS_UNDEFINED. Its maximum
10064
+ size is 2^31-1 elements. For convenience, 'len' is a 64 bit
10065
+ integer. */
10066
+ static JSValue js_allocate_fast_array(JSContext *ctx, int64_t len)
10067
+ {
10068
+ JSValue arr;
10069
+ JSObject *p;
10070
+ int i;
10071
+
10072
+ if (len > INT32_MAX)
10073
+ return JS_ThrowRangeError(ctx, "invalid array length");
10074
+ arr = JS_NewArray(ctx);
10075
+ if (JS_IsException(arr))
10076
+ return arr;
10077
+ if (len > 0) {
10078
+ p = JS_VALUE_GET_OBJ(arr);
10079
+ if (expand_fast_array(ctx, p, len) < 0) {
10080
+ JS_FreeValue(ctx, arr);
10081
+ return JS_EXCEPTION;
10082
+ }
10083
+ p->u.array.count = len;
10084
+ for(i = 0; i < len; i++)
10085
+ p->u.array.u.values[i] = JS_UNDEFINED;
10086
+ /* update the 'length' field */
10087
+ set_value(ctx, &p->prop[0].u.value, js_int32(len));
10088
+ }
10089
+ return arr;
10090
+ }
10091
+
10007
10092
  static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc)
10008
10093
  {
10009
10094
  JS_FreeValue(ctx, desc->getter);
@@ -10198,12 +10283,11 @@ retry:
10198
10283
  goto done;
10199
10284
  }
10200
10285
 
10201
- if (unlikely(!p->extensible)) {
10202
- ret = JS_ThrowTypeErrorOrFalse(ctx, flags, "object is not extensible");
10203
- goto done;
10204
- }
10205
-
10206
10286
  if (p == JS_VALUE_GET_OBJ(obj)) {
10287
+ if (unlikely(!p->extensible)) {
10288
+ ret = JS_ThrowTypeErrorOrFalse(ctx, flags, "object is not extensible");
10289
+ goto done;
10290
+ }
10207
10291
  if (p->is_exotic) {
10208
10292
  if (p->class_id == JS_CLASS_ARRAY && p->fast_array &&
10209
10293
  __JS_AtomIsTaggedInt(prop)) {
@@ -10244,6 +10328,10 @@ retry:
10244
10328
  JS_UNDEFINED, JS_UNDEFINED,
10245
10329
  JS_PROP_HAS_VALUE);
10246
10330
  } else {
10331
+ if (unlikely(!p->extensible)) {
10332
+ ret = JS_ThrowTypeErrorOrFalse(ctx, flags, "object is not extensible");
10333
+ goto done;
10334
+ }
10247
10335
  generic_create_prop:
10248
10336
  ret = JS_CreateProperty(ctx, p, prop, val, JS_UNDEFINED, JS_UNDEFINED,
10249
10337
  flags |
@@ -16625,6 +16713,63 @@ static JSValue JS_IteratorGetCompleteValue(JSContext *ctx, JSValue obj,
16625
16713
  return JS_EXCEPTION;
16626
16714
  }
16627
16715
 
16716
+ static JSValue js_sync_dispose_wrapper(JSContext *ctx, JSValueConst this_val,
16717
+ int argc, JSValueConst *argv,
16718
+ int magic, JSValueConst *func_data);
16719
+
16720
+ static __exception int js_op_using_check(JSContext *ctx, JSValueConst val,
16721
+ int hint, JSValue *pmethod)
16722
+ {
16723
+ JSValue method;
16724
+ bool is_sync_fallback = false;
16725
+
16726
+ *pmethod = JS_UNDEFINED;
16727
+ if (JS_IsNull(val) || JS_IsUndefined(val))
16728
+ return 0;
16729
+ if (!JS_IsObject(val)) {
16730
+ JS_ThrowTypeErrorNotAnObject(ctx);
16731
+ return -1;
16732
+ }
16733
+ if (hint == 1) {
16734
+ method = JS_GetProperty(ctx, val, JS_ATOM_Symbol_asyncDispose);
16735
+ if (JS_IsException(method))
16736
+ return -1;
16737
+ if (JS_IsUndefined(method) || JS_IsNull(method)) {
16738
+ JS_FreeValue(ctx, method);
16739
+ method = JS_GetProperty(ctx, val, JS_ATOM_Symbol_dispose);
16740
+ if (JS_IsException(method))
16741
+ return -1;
16742
+ is_sync_fallback = true;
16743
+ }
16744
+ } else {
16745
+ method = JS_GetProperty(ctx, val, JS_ATOM_Symbol_dispose);
16746
+ if (JS_IsException(method))
16747
+ return -1;
16748
+ }
16749
+ if (JS_IsUndefined(method) || JS_IsNull(method)) {
16750
+ JS_ThrowTypeError(ctx, "value is not disposable");
16751
+ return -1;
16752
+ }
16753
+ if (!JS_IsFunction(ctx, method)) {
16754
+ JS_FreeValue(ctx, method);
16755
+ JS_ThrowTypeError(ctx, "dispose method is not a function");
16756
+ return -1;
16757
+ }
16758
+ if (is_sync_fallback) {
16759
+ JSValueConst data[1];
16760
+ JSValue wrapped;
16761
+ data[0] = method;
16762
+ wrapped = JS_NewCFunctionData(ctx, js_sync_dispose_wrapper, 0, 0,
16763
+ 1, data);
16764
+ JS_FreeValue(ctx, method);
16765
+ if (JS_IsException(wrapped))
16766
+ return -1;
16767
+ method = wrapped;
16768
+ }
16769
+ *pmethod = method;
16770
+ return 0;
16771
+ }
16772
+
16628
16773
  static __exception int js_iterator_get_value_done(JSContext *ctx, JSValue *sp)
16629
16774
  {
16630
16775
  JSValue obj, value;
@@ -18689,9 +18834,9 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
18689
18834
  goto exception;
18690
18835
  sp += 1;
18691
18836
  BREAK;
18692
- CASE(OP_iterator_check_object):
18837
+ CASE(OP_check_object):
18693
18838
  if (unlikely(!JS_IsObject(sp[-1]))) {
18694
- JS_ThrowTypeError(ctx, "iterator must return an object");
18839
+ JS_ThrowTypeErrorNotAnObject(ctx);
18695
18840
  goto exception;
18696
18841
  }
18697
18842
  BREAK;
@@ -18727,6 +18872,124 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
18727
18872
  }
18728
18873
  BREAK;
18729
18874
 
18875
+ CASE(OP_using_dispose_init):
18876
+ sp[0] = JS_UNINITIALIZED;
18877
+ sp++;
18878
+ BREAK;
18879
+
18880
+ CASE(OP_using_dispose):
18881
+ {
18882
+ int idx;
18883
+ JSValueConst val, method;
18884
+ JSValue ret, error_state;
18885
+
18886
+ idx = get_u16(pc);
18887
+ pc += 2;
18888
+ val = var_buf[idx];
18889
+ method = var_buf[idx + 1];
18890
+ error_state = sp[-1];
18891
+
18892
+ if (JS_IsNull(val) || JS_IsUndefined(val) ||
18893
+ JS_IsUninitialized(val)) {
18894
+ /* null/undefined (spec-permitted) or uninitialized
18895
+ (declaration threw before assignment). */
18896
+ BREAK;
18897
+ }
18898
+ sf->cur_pc = pc;
18899
+ ret = JS_Call(ctx, method, val, 0, NULL);
18900
+ if (JS_IsException(ret)) {
18901
+ JSValue new_error = JS_GetException(ctx);
18902
+ if (!JS_IsUninitialized(error_state)) {
18903
+ JSValue se;
18904
+ se = js_new_suppressed_error(ctx, new_error,
18905
+ error_state);
18906
+ JS_FreeValue(ctx, new_error);
18907
+ JS_FreeValue(ctx, error_state);
18908
+ if (JS_IsException(se)) {
18909
+ sp[-1] = JS_GetException(ctx);
18910
+ } else {
18911
+ sp[-1] = se;
18912
+ }
18913
+ } else {
18914
+ sp[-1] = new_error;
18915
+ }
18916
+ } else {
18917
+ JS_FreeValue(ctx, ret);
18918
+ }
18919
+ }
18920
+ BREAK;
18921
+
18922
+ CASE(OP_using_dispose_async):
18923
+ {
18924
+ int idx;
18925
+ JSValueConst val, method;
18926
+ JSValue ret;
18927
+
18928
+ idx = get_u16(pc);
18929
+ pc += 2;
18930
+ val = var_buf[idx];
18931
+ method = var_buf[idx + 1];
18932
+
18933
+ if (JS_IsNull(val) || JS_IsUndefined(val) ||
18934
+ JS_IsUninitialized(val)) {
18935
+ sp[0] = JS_UNDEFINED;
18936
+ sp++;
18937
+ BREAK;
18938
+ }
18939
+ sf->cur_pc = pc;
18940
+ ret = JS_Call(ctx, method, val, 0, NULL);
18941
+ if (JS_IsException(ret))
18942
+ goto exception;
18943
+ sp[0] = ret;
18944
+ sp++;
18945
+ }
18946
+ BREAK;
18947
+
18948
+ CASE(OP_using_dispose_merge):
18949
+ {
18950
+ JSValue new_error = sp[-1];
18951
+ JSValue error_state = sp[-2];
18952
+ sp--;
18953
+ if (!JS_IsUninitialized(error_state)) {
18954
+ JSValue se = js_new_suppressed_error(ctx, new_error,
18955
+ error_state);
18956
+ JS_FreeValue(ctx, new_error);
18957
+ JS_FreeValue(ctx, error_state);
18958
+ if (JS_IsException(se)) {
18959
+ sp[-1] = JS_GetException(ctx);
18960
+ } else {
18961
+ sp[-1] = se;
18962
+ }
18963
+ } else {
18964
+ sp[-1] = new_error;
18965
+ }
18966
+ }
18967
+ BREAK;
18968
+
18969
+ CASE(OP_using_dispose_end):
18970
+ {
18971
+ JSValue error_state = sp[-1];
18972
+ sp--;
18973
+ if (!JS_IsUninitialized(error_state)) {
18974
+ JS_Throw(ctx, error_state);
18975
+ goto exception;
18976
+ }
18977
+ }
18978
+ BREAK;
18979
+
18980
+ CASE(OP_using_check):
18981
+ {
18982
+ int hint = pc[0];
18983
+ JSValue method;
18984
+ pc += 1;
18985
+ sf->cur_pc = pc;
18986
+ if (js_op_using_check(ctx, sp[-1], hint, &method))
18987
+ goto exception;
18988
+ sp[0] = method;
18989
+ sp++;
18990
+ }
18991
+ BREAK;
18992
+
18730
18993
  CASE(OP_iterator_next):
18731
18994
  /* stack: iter_obj next catch_offset val */
18732
18995
  {
@@ -21287,6 +21550,7 @@ enum {
21287
21550
  TOK_EXTENDS,
21288
21551
  TOK_IMPORT,
21289
21552
  TOK_SUPER,
21553
+ TOK_USING,
21290
21554
  /* FutureReservedWords when parsing strict mode code */
21291
21555
  TOK_IMPLEMENTS,
21292
21556
  TOK_INTERFACE,
@@ -21321,6 +21585,8 @@ typedef struct BlockEnv {
21321
21585
  int scope_level;
21322
21586
  uint8_t has_iterator : 1;
21323
21587
  uint8_t is_regular_stmt : 1; // i.e. not a loop statement
21588
+ uint8_t has_using : 1; /* scope has using declarations needing disposal */
21589
+ int using_scope_level; /* scope level for OP_dispose_scope (-1 if none) */
21324
21590
  } BlockEnv;
21325
21591
 
21326
21592
  typedef struct JSGlobalVar {
@@ -22097,6 +22363,12 @@ static __exception int ident_realloc(JSContext *ctx, char **pbuf, size_t *psize,
22097
22363
  /* convert a TOK_IDENT to a keyword when needed */
22098
22364
  static void update_token_ident(JSParseState *s)
22099
22365
  {
22366
+ /* `using` is contextually reserved, not a true keyword. Leave it as
22367
+ TOK_IDENT so it can be used as a regular identifier in expressions.
22368
+ Using declarations are detected explicitly at statement and
22369
+ for-loop head parsing via token_is_pseudo_keyword. */
22370
+ if (s->token.u.ident.atom == JS_ATOM_using)
22371
+ return;
22100
22372
  if (s->token.u.ident.atom <= JS_ATOM_LAST_KEYWORD ||
22101
22373
  (s->token.u.ident.atom <= JS_ATOM_LAST_STRICT_KEYWORD &&
22102
22374
  s->cur_func->is_strict_mode) ||
@@ -22415,6 +22687,7 @@ static __exception int next_token(JSParseState *s)
22415
22687
  if (JS_VALUE_IS_NAN(ret) ||
22416
22688
  lre_js_is_ident_next(utf8_decode(p, &p1))) {
22417
22689
  JS_FreeValue(s->ctx, ret);
22690
+ s->col_num = max_int(1, s->mark - s->eol);
22418
22691
  js_parse_error(s, "invalid number literal");
22419
22692
  goto fail;
22420
22693
  }
@@ -23040,6 +23313,9 @@ static int simple_next_token(const uint8_t **pp, bool no_line_terminator)
23040
23313
  } else if (c == 'a' && p[0] == 'w' && p[1] == 'a' &&
23041
23314
  p[2] == 'i' && p[3] == 't' && !lre_js_is_ident_next(p[4])) {
23042
23315
  return TOK_AWAIT;
23316
+ } else if (c == 'u' && p[0] == 's' && p[1] == 'i' &&
23317
+ p[2] == 'n' && p[3] == 'g' && !lre_js_is_ident_next(p[4])) {
23318
+ return TOK_USING;
23043
23319
  }
23044
23320
  return TOK_IDENT;
23045
23321
  }
@@ -23119,14 +23395,19 @@ static void emit_u32(JSParseState *s, uint32_t val)
23119
23395
  dbuf_put_u32(&s->cur_func->byte_code, val);
23120
23396
  }
23121
23397
 
23122
- static void emit_source_loc(JSParseState *s)
23398
+ static void emit_source_loc_at(JSParseState *s, int line_num, int col_num)
23123
23399
  {
23124
23400
  JSFunctionDef *fd = s->cur_func;
23125
23401
  DynBuf *bc = &fd->byte_code;
23126
23402
 
23127
23403
  dbuf_putc(bc, OP_source_loc);
23128
- dbuf_put_u32(bc, s->token.line_num);
23129
- dbuf_put_u32(bc, s->token.col_num);
23404
+ dbuf_put_u32(bc, line_num);
23405
+ dbuf_put_u32(bc, col_num);
23406
+ }
23407
+
23408
+ static void emit_source_loc(JSParseState *s)
23409
+ {
23410
+ emit_source_loc_at(s, s->token.line_num, s->token.col_num);
23130
23411
  }
23131
23412
 
23132
23413
  static void emit_op(JSParseState *s, uint8_t val)
@@ -23347,7 +23628,7 @@ static int find_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name)
23347
23628
  if (i == -1)
23348
23629
  goto not_found;
23349
23630
  vd = &fd->vars[i];
23350
- if (fd->vars[i].scope_level == 0)
23631
+ if (vd->scope_level == 0)
23351
23632
  return i;
23352
23633
  }
23353
23634
  for(i = fd->var_count; i-- > 0;) {
@@ -23473,6 +23754,10 @@ static int push_scope(JSParseState *s) {
23473
23754
  fd->scope_count++;
23474
23755
  fd->scopes[scope].parent = fd->scope_level;
23475
23756
  fd->scopes[scope].first = fd->scope_first;
23757
+ fd->scopes[scope].has_using = 0;
23758
+ fd->scopes[scope].is_await_using = 0;
23759
+ fd->scopes[scope].using_label_catch = -1;
23760
+ fd->scopes[scope].using_label_end = -1;
23476
23761
  emit_op(s, OP_enter_scope);
23477
23762
  emit_u16(s, scope);
23478
23763
  return fd->scope_level = scope;
@@ -23646,6 +23931,7 @@ typedef enum {
23646
23931
  JS_VAR_DEF_NEW_FUNCTION_DECL, /* async/generator function declaration */
23647
23932
  JS_VAR_DEF_CATCH,
23648
23933
  JS_VAR_DEF_VAR,
23934
+ JS_VAR_DEF_USING,
23649
23935
  } JSVarDefEnum;
23650
23936
 
23651
23937
  static int define_var(JSParseState *s, JSFunctionDef *fd, JSAtom name,
@@ -23662,6 +23948,7 @@ static int define_var(JSParseState *s, JSFunctionDef *fd, JSAtom name,
23662
23948
 
23663
23949
  case JS_VAR_DEF_LET:
23664
23950
  case JS_VAR_DEF_CONST:
23951
+ case JS_VAR_DEF_USING:
23665
23952
  case JS_VAR_DEF_FUNCTION_DECL:
23666
23953
  case JS_VAR_DEF_NEW_FUNCTION_DECL:
23667
23954
  idx = find_lexical_decl(ctx, fd, name, fd->scope_first, true);
@@ -23708,9 +23995,10 @@ static int define_var(JSParseState *s, JSFunctionDef *fd, JSAtom name,
23708
23995
  }
23709
23996
 
23710
23997
  if (fd->is_eval &&
23711
- (fd->eval_type == JS_EVAL_TYPE_GLOBAL ||
23712
- fd->eval_type == JS_EVAL_TYPE_MODULE) &&
23713
- fd->scope_level == fd->body_scope) {
23998
+ (fd->eval_type == JS_EVAL_TYPE_GLOBAL ||
23999
+ fd->eval_type == JS_EVAL_TYPE_MODULE) &&
24000
+ fd->scope_level == fd->body_scope &&
24001
+ var_def_type != JS_VAR_DEF_USING) {
23714
24002
  JSGlobalVar *hf;
23715
24003
  hf = add_global_var(s->ctx, fd, name);
23716
24004
  if (!hf)
@@ -23724,13 +24012,16 @@ static int define_var(JSParseState *s, JSFunctionDef *fd, JSAtom name,
23724
24012
  var_kind = JS_VAR_FUNCTION_DECL;
23725
24013
  else if (var_def_type == JS_VAR_DEF_NEW_FUNCTION_DECL)
23726
24014
  var_kind = JS_VAR_NEW_FUNCTION_DECL;
24015
+ else if (var_def_type == JS_VAR_DEF_USING)
24016
+ var_kind = JS_VAR_USING;
23727
24017
  else
23728
24018
  var_kind = JS_VAR_NORMAL;
23729
24019
  idx = add_scope_var(ctx, fd, name, var_kind);
23730
24020
  if (idx >= 0) {
23731
24021
  vd = &fd->vars[idx];
23732
24022
  vd->is_lexical = 1;
23733
- vd->is_const = (var_def_type == JS_VAR_DEF_CONST);
24023
+ vd->is_const = (var_def_type == JS_VAR_DEF_CONST ||
24024
+ var_def_type == JS_VAR_DEF_USING);
23734
24025
  }
23735
24026
  }
23736
24027
  break;
@@ -24443,6 +24734,7 @@ static __exception int js_parse_object_literal(JSParseState *s)
24443
24734
  #define PF_POW_ALLOWED (1 << 2)
24444
24735
  /* forbid the exponentiation operator in js_parse_unary() */
24445
24736
  #define PF_POW_FORBIDDEN (1 << 3)
24737
+ #define PF_AWAIT_USING (1 << 4)
24446
24738
 
24447
24739
  static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags);
24448
24740
 
@@ -25595,6 +25887,9 @@ static __exception int js_define_var(JSParseState *s, JSAtom name, int tok)
25595
25887
  case TOK_VAR:
25596
25888
  var_def_type = JS_VAR_DEF_VAR;
25597
25889
  break;
25890
+ case TOK_USING:
25891
+ var_def_type = JS_VAR_DEF_USING;
25892
+ break;
25598
25893
  case TOK_CATCH:
25599
25894
  var_def_type = JS_VAR_DEF_CATCH;
25600
25895
  break;
@@ -25971,8 +26266,11 @@ static int js_parse_destructuring_element(JSParseState *s, int tok,
25971
26266
  } else if (s->token.val == '[') {
25972
26267
  bool has_spread;
25973
26268
  int enum_depth;
26269
+ int source_line_num, source_col_num;
25974
26270
  BlockEnv block_env;
25975
26271
 
26272
+ source_line_num = s->token.line_num;
26273
+ source_col_num = s->token.col_num;
25976
26274
  if (next_token(s))
25977
26275
  return -1;
25978
26276
  /* the block environment is only needed in generators in case
@@ -26068,6 +26366,7 @@ static int js_parse_destructuring_element(JSParseState *s, int tok,
26068
26366
  }
26069
26367
  /* close iterator object:
26070
26368
  if completed, enum_obj has been replaced by undefined */
26369
+ emit_source_loc_at(s, source_line_num, source_col_num);
26071
26370
  emit_op(s, OP_iterator_close);
26072
26371
  pop_break_entry(s->cur_func);
26073
26372
  if (next_token(s))
@@ -26880,6 +27179,9 @@ static __exception int js_parse_delete(JSParseState *s)
26880
27179
  return 0;
26881
27180
  }
26882
27181
 
27182
+ static __exception int js_parse_var(JSParseState *s, int parse_flags, int tok,
27183
+ bool export_flag);
27184
+
26883
27185
  /* allowed parse_flags: PF_POW_ALLOWED, PF_POW_FORBIDDEN */
26884
27186
  static __exception int js_parse_unary(JSParseState *s, int parse_flags)
26885
27187
  {
@@ -26966,8 +27268,8 @@ static __exception int js_parse_unary(JSParseState *s, int parse_flags)
26966
27268
  return -1;
26967
27269
  if (js_parse_unary(s, PF_POW_FORBIDDEN))
26968
27270
  return -1;
26969
- s->cur_func->has_await = true;
26970
27271
  emit_op(s, OP_await);
27272
+ s->cur_func->has_await = true;
26971
27273
  parse_flags = 0;
26972
27274
  break;
26973
27275
  default:
@@ -27323,7 +27625,7 @@ static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags)
27323
27625
  emit_op(s, OP_iterator_next);
27324
27626
  if (is_async)
27325
27627
  emit_op(s, OP_await);
27326
- emit_op(s, OP_iterator_check_object);
27628
+ emit_op(s, OP_check_object);
27327
27629
  emit_op(s, OP_get_field2);
27328
27630
  emit_atom(s, JS_ATOM_done);
27329
27631
  label_next = emit_goto(s, OP_if_true, -1); /* end of loop */
@@ -27356,7 +27658,7 @@ static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags)
27356
27658
  label_return1 = emit_goto(s, OP_if_true, -1);
27357
27659
  if (is_async)
27358
27660
  emit_op(s, OP_await);
27359
- emit_op(s, OP_iterator_check_object);
27661
+ emit_op(s, OP_check_object);
27360
27662
  emit_op(s, OP_get_field2);
27361
27663
  emit_atom(s, JS_ATOM_done);
27362
27664
  emit_goto(s, OP_if_false, label_yield);
@@ -27377,7 +27679,7 @@ static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags)
27377
27679
  label_throw1 = emit_goto(s, OP_if_true, -1);
27378
27680
  if (is_async)
27379
27681
  emit_op(s, OP_await);
27380
- emit_op(s, OP_iterator_check_object);
27682
+ emit_op(s, OP_check_object);
27381
27683
  emit_op(s, OP_get_field2);
27382
27684
  emit_atom(s, JS_ATOM_done);
27383
27685
  emit_goto(s, OP_if_false, label_yield);
@@ -27624,6 +27926,8 @@ static void push_break_entry(JSFunctionDef *fd, BlockEnv *be,
27624
27926
  be->scope_level = fd->scope_level;
27625
27927
  be->has_iterator = false;
27626
27928
  be->is_regular_stmt = false;
27929
+ be->has_using = false;
27930
+ be->using_scope_level = -1;
27627
27931
  }
27628
27932
 
27629
27933
  static void pop_break_entry(JSFunctionDef *fd)
@@ -27664,6 +27968,12 @@ static __exception int emit_break(JSParseState *s, JSAtom name, int is_cont)
27664
27968
  }
27665
27969
  for(; i < top->drop_count; i++)
27666
27970
  emit_op(s, OP_drop);
27971
+ if (top->has_using) {
27972
+ emit_op(s, OP_using_dispose_init);
27973
+ emit_op(s, OP_dispose_scope);
27974
+ emit_u16(s, top->using_scope_level);
27975
+ emit_op(s, OP_using_dispose_end);
27976
+ }
27667
27977
  if (top->label_finally != -1) {
27668
27978
  /* must push dummy value to keep same stack depth */
27669
27979
  emit_op(s, OP_undefined);
@@ -27701,7 +28011,8 @@ static void emit_return(JSParseState *s, bool hasval)
27701
28011
 
27702
28012
  top = s->cur_func->top_break;
27703
28013
  while (top != NULL) {
27704
- if (top->has_iterator || top->label_finally != -1) {
28014
+ if (top->has_iterator || top->label_finally != -1 ||
28015
+ top->has_using) {
27705
28016
  if (!hasval) {
27706
28017
  emit_op(s, OP_undefined);
27707
28018
  hasval = true;
@@ -27725,7 +28036,7 @@ static void emit_return(JSParseState *s, bool hasval)
27725
28036
  label_next = emit_goto(s, OP_if_true, -1);
27726
28037
  emit_op(s, OP_call_method);
27727
28038
  emit_u16(s, 0);
27728
- emit_op(s, OP_iterator_check_object);
28039
+ emit_op(s, OP_check_object);
27729
28040
  emit_op(s, OP_await);
27730
28041
  label_next2 = emit_goto(s, OP_goto, -1);
27731
28042
  emit_label(s, label_next);
@@ -27737,6 +28048,14 @@ static void emit_return(JSParseState *s, bool hasval)
27737
28048
  emit_op(s, OP_undefined); /* dummy catch offset */
27738
28049
  emit_op(s, OP_iterator_close);
27739
28050
  }
28051
+ } else if (top->has_using) {
28052
+ /* Dispose using variables. The return value is on TOS.
28053
+ stack: ret_val */
28054
+ emit_op(s, OP_using_dispose_init); /* initial error_state */
28055
+ emit_op(s, OP_dispose_scope);
28056
+ emit_u16(s, top->using_scope_level);
28057
+ emit_op(s, OP_using_dispose_end);
28058
+ /* stack: ret_val */
27740
28059
  } else {
27741
28060
  /* execute the "finally" block */
27742
28061
  emit_goto(s, OP_gosub, top->label_finally);
@@ -27788,16 +28107,53 @@ static __exception int js_parse_statement(JSParseState *s)
27788
28107
 
27789
28108
  static __exception int js_parse_block(JSParseState *s)
27790
28109
  {
28110
+ JSFunctionDef *fd = s->cur_func;
28111
+
27791
28112
  if (js_parse_expect(s, '{'))
27792
28113
  return -1;
27793
28114
  if (s->token.val != '}') {
28115
+ BlockEnv using_be;
28116
+ int has_using_be = 0;
28117
+ int scope_level;
28118
+
27794
28119
  push_scope(s);
28120
+ scope_level = fd->scope_level;
27795
28121
  for(;;) {
27796
28122
  if (js_parse_statement_or_decl(s, DECL_MASK_ALL))
27797
28123
  return -1;
28124
+ if (!has_using_be && fd->scopes[scope_level].has_using) {
28125
+ has_using_be = 1;
28126
+ push_break_entry(fd, &using_be, JS_ATOM_NULL, -1, -1, 1);
28127
+ using_be.has_using = true;
28128
+ using_be.using_scope_level = scope_level;
28129
+ }
27798
28130
  if (s->token.val == '}')
27799
28131
  break;
27800
28132
  }
28133
+ if (has_using_be) {
28134
+ int label_catch = fd->scopes[scope_level].using_label_catch;
28135
+ int label_end = fd->scopes[scope_level].using_label_end;
28136
+
28137
+ pop_break_entry(fd);
28138
+
28139
+ if (js_is_live_code(s)) {
28140
+ emit_op(s, OP_drop); /* drop catch_offset */
28141
+ emit_op(s, OP_using_dispose_init); /* initial error_state: no error */
28142
+ emit_op(s, OP_dispose_scope);
28143
+ emit_u16(s, scope_level);
28144
+ emit_op(s, OP_using_dispose_end);
28145
+ emit_goto(s, OP_goto, label_end);
28146
+ }
28147
+
28148
+ emit_label(s, label_catch);
28149
+ /* Stack: exception_value (= initial error_state) */
28150
+ emit_op(s, OP_dispose_scope);
28151
+ emit_u16(s, scope_level);
28152
+ /* Stack: final_error_state (original or SuppressedError) */
28153
+ emit_op(s, OP_throw);
28154
+
28155
+ emit_label(s, label_end);
28156
+ }
27801
28157
  pop_scope(s);
27802
28158
  }
27803
28159
  if (next_token(s))
@@ -27819,14 +28175,29 @@ static __exception int js_parse_var(JSParseState *s, int parse_flags, int tok,
27819
28175
  return js_parse_error_reserved_identifier(s);
27820
28176
  }
27821
28177
  name = JS_DupAtom(ctx, s->token.u.ident.atom);
27822
- if (name == JS_ATOM_let && (tok == TOK_LET || tok == TOK_CONST)) {
28178
+ if (name == JS_ATOM_let &&
28179
+ (tok == TOK_LET || tok == TOK_CONST || tok == TOK_USING)) {
27823
28180
  js_parse_error(s, "'let' is not a valid lexical identifier");
27824
28181
  goto var_error;
27825
28182
  }
28183
+ int using_method_idx = -1;
27826
28184
  if (next_token(s))
27827
28185
  goto var_error;
27828
28186
  if (js_define_var(s, name, tok))
27829
28187
  goto var_error;
28188
+ if (tok == TOK_USING) {
28189
+ /* Allocate a paired hidden local for the cached dispose
28190
+ method. Must be allocated immediately after the value
28191
+ var so OP_using_dispose can locate it at value_idx + 1.
28192
+ */
28193
+ using_method_idx = add_scope_var(ctx, fd,
28194
+ JS_ATOM__using_dispose_,
28195
+ JS_VAR_USING_METHOD);
28196
+ if (using_method_idx < 0)
28197
+ goto var_error;
28198
+ fd->vars[using_method_idx].is_lexical = 1;
28199
+ fd->vars[using_method_idx].is_const = 1;
28200
+ }
27830
28201
  if (export_flag) {
27831
28202
  if (!add_export_entry(s, s->cur_func->module, name, name,
27832
28203
  JS_EXPORT_TYPE_LOCAL))
@@ -27854,17 +28225,54 @@ static __exception int js_parse_var(JSParseState *s, int parse_flags, int tok,
27854
28225
  put_lvalue(s, opcode, scope, name1, label,
27855
28226
  PUT_LVALUE_NOKEEP, false);
27856
28227
  } else {
28228
+ bool init;
28229
+
28230
+ if (tok == TOK_USING) {
28231
+ bool is_await = (parse_flags & PF_AWAIT_USING) != 0;
28232
+
28233
+ if (!fd->scopes[fd->scope_level].has_using) {
28234
+ /* First 'using' in this scope: set up labels
28235
+ for the catch handler and end of disposal */
28236
+ fd->scopes[fd->scope_level].has_using = 1;
28237
+ fd->scopes[fd->scope_level].using_label_catch =
28238
+ new_label(s);
28239
+ fd->scopes[fd->scope_level].using_label_end =
28240
+ new_label(s);
28241
+
28242
+ /* Emit OP_catch: push catch_offset on the value
28243
+ stack. If an exception occurs, control jumps
28244
+ to catch_label with the exception value on the
28245
+ stack instead of catch_offset. */
28246
+ emit_goto(s, OP_catch,
28247
+ fd->scopes[fd->scope_level].using_label_catch);
28248
+ }
28249
+ if (is_await)
28250
+ fd->scopes[fd->scope_level].is_await_using = 1;
28251
+ }
28252
+
27857
28253
  if (js_parse_assign_expr2(s, parse_flags))
27858
28254
  goto var_error;
27859
28255
  set_object_name(s, name);
27860
- emit_op(s, (tok == TOK_CONST || tok == TOK_LET) ?
27861
- OP_scope_put_var_init : OP_scope_put_var);
28256
+
28257
+ if (tok == TOK_USING) {
28258
+ emit_op(s, OP_using_check);
28259
+ emit_u8(s, (parse_flags & PF_AWAIT_USING) != 0);
28260
+ /* Stack: value, method. Store the method first.
28261
+ Emit OP_put_loc directly (bypasses atom lookup)
28262
+ so multiple using decls with the shared hidden
28263
+ atom name don't collide. */
28264
+ emit_op(s, OP_put_loc);
28265
+ emit_u16(s, using_method_idx);
28266
+ }
28267
+
28268
+ init = (tok == TOK_CONST || tok == TOK_LET || tok == TOK_USING);
28269
+ emit_op(s, init ? OP_scope_put_var_init : OP_scope_put_var);
27862
28270
  emit_atom(s, name);
27863
28271
  emit_u16(s, fd->scope_level);
27864
28272
  }
27865
28273
  } else {
27866
- if (tok == TOK_CONST) {
27867
- js_parse_error(s, "missing initializer for const variable");
28274
+ if (tok == TOK_CONST || tok == TOK_USING) {
28275
+ js_parse_error(s, "missing initializer for variable");
27868
28276
  goto var_error;
27869
28277
  }
27870
28278
  if (tok == TOK_LET) {
@@ -27880,6 +28288,10 @@ static __exception int js_parse_var(JSParseState *s, int parse_flags, int tok,
27880
28288
  int skip_bits;
27881
28289
  if ((s->token.val == '[' || s->token.val == '{')
27882
28290
  && js_parse_skip_parens_token(s, &skip_bits, false) == '=') {
28291
+ /* using declarations do not allow binding patterns */
28292
+ if (tok == TOK_USING) {
28293
+ return js_parse_error(s, "binding patterns are not allowed in using declarations");
28294
+ }
27883
28295
  emit_op(s, OP_undefined);
27884
28296
  if (js_parse_destructuring_element(s, tok, false, true, skip_bits & SKIP_HAS_ELLIPSIS, true, export_flag) < 0)
27885
28297
  return -1;
@@ -27948,10 +28360,38 @@ static int is_let(JSParseState *s, int decl_mask)
27948
28360
  return res;
27949
28361
  }
27950
28362
 
28363
+ /* Return 1 if the current token is `using` introducing a UsingDeclaration,
28364
+ 0 if it is a plain identifier usage, or -1 on error.
28365
+ If `is_for_of` is true, `using of` is specifically treated as identifier
28366
+ per the for-of lookahead restriction. */
28367
+ static int is_using(JSParseState *s, bool is_for_of)
28368
+ {
28369
+ int res = false;
28370
+ if (token_is_pseudo_keyword(s, JS_ATOM_using)) {
28371
+ JSParsePos pos;
28372
+ js_parse_get_pos(s, &pos);
28373
+ if (next_token(s))
28374
+ return -1;
28375
+ /* No line terminator allowed between `using` and the binding */
28376
+ if (s->last_line_num == s->token.line_num) {
28377
+ if (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved) {
28378
+ if (!(is_for_of && s->token.u.ident.atom == JS_ATOM_of)) {
28379
+ res = true;
28380
+ }
28381
+ }
28382
+ }
28383
+ if (js_parse_seek_token(s, &pos))
28384
+ res = -1;
28385
+ }
28386
+ return res;
28387
+ }
28388
+
27951
28389
  /* XXX: handle IteratorClose when exiting the loop before the
27952
28390
  enumeration is done */
27953
28391
  static __exception int js_parse_for_in_of(JSParseState *s, int label_name,
27954
- bool is_async)
28392
+ bool is_async,
28393
+ int source_line_num,
28394
+ int source_col_num)
27955
28395
  {
27956
28396
  JSContext *ctx = s->ctx;
27957
28397
  JSFunctionDef *fd = s->cur_func;
@@ -27997,7 +28437,27 @@ static __exception int js_parse_for_in_of(JSParseState *s, int label_name,
27997
28437
  default:
27998
28438
  return -1;
27999
28439
  }
28000
- if (tok == TOK_VAR || tok == TOK_LET || tok == TOK_CONST) {
28440
+ bool is_await_using = false;
28441
+ if (tok == TOK_AWAIT) {
28442
+ int u;
28443
+ if (next_token(s))
28444
+ return -1;
28445
+ u = is_using(s, false);
28446
+ if (u < 0)
28447
+ return -1;
28448
+ if (!u)
28449
+ return js_parse_error(s, "'using' expected");
28450
+ tok = TOK_USING;
28451
+ is_await_using = true;
28452
+ s->cur_func->has_await = true;
28453
+ } else if (token_is_pseudo_keyword(s, JS_ATOM_using)) {
28454
+ int u = is_using(s, true);
28455
+ if (u < 0)
28456
+ return -1;
28457
+ if (u)
28458
+ tok = TOK_USING;
28459
+ }
28460
+ if (tok == TOK_VAR || tok == TOK_LET || tok == TOK_CONST || tok == TOK_USING) {
28001
28461
  if (next_token(s))
28002
28462
  return -1;
28003
28463
 
@@ -28011,7 +28471,13 @@ static __exception int js_parse_for_in_of(JSParseState *s, int label_name,
28011
28471
  }
28012
28472
  var_name = JS_ATOM_NULL;
28013
28473
  } else {
28474
+ bool init;
28014
28475
  var_name = JS_DupAtom(ctx, s->token.u.ident.atom);
28476
+ if (var_name == JS_ATOM_let &&
28477
+ (tok == TOK_LET || tok == TOK_CONST || tok == TOK_USING)) {
28478
+ JS_FreeAtom(s->ctx, var_name);
28479
+ return js_parse_error(s, "'let' is not a valid lexical identifier");
28480
+ }
28015
28481
  if (next_token(s)) {
28016
28482
  JS_FreeAtom(s->ctx, var_name);
28017
28483
  return -1;
@@ -28020,10 +28486,33 @@ static __exception int js_parse_for_in_of(JSParseState *s, int label_name,
28020
28486
  JS_FreeAtom(s->ctx, var_name);
28021
28487
  return -1;
28022
28488
  }
28023
- emit_op(s, (tok == TOK_CONST || tok == TOK_LET) ?
28024
- OP_scope_put_var_init : OP_scope_put_var);
28489
+ if (tok == TOK_USING) {
28490
+ int mi = add_scope_var(ctx, fd, JS_ATOM__using_dispose_,
28491
+ JS_VAR_USING_METHOD);
28492
+ if (mi < 0) {
28493
+ JS_FreeAtom(s->ctx, var_name);
28494
+ return -1;
28495
+ }
28496
+ fd->vars[mi].is_lexical = 1;
28497
+ fd->vars[mi].is_const = 1;
28498
+ emit_op(s, OP_using_check);
28499
+ emit_u8(s, is_await_using);
28500
+ emit_op(s, OP_put_loc);
28501
+ emit_u16(s, mi);
28502
+ }
28503
+ init = (tok == TOK_CONST || tok == TOK_LET || tok == TOK_USING);
28504
+ emit_op(s, init ? OP_scope_put_var_init : OP_scope_put_var);
28025
28505
  emit_atom(s, var_name);
28026
28506
  emit_u16(s, fd->scope_level);
28507
+ if (tok == TOK_USING) {
28508
+ if (!fd->scopes[fd->scope_level].has_using) {
28509
+ fd->scopes[fd->scope_level].has_using = 1;
28510
+ fd->scopes[fd->scope_level].using_label_catch = new_label(s);
28511
+ fd->scopes[fd->scope_level].using_label_end = new_label(s);
28512
+ }
28513
+ if (is_await_using)
28514
+ fd->scopes[fd->scope_level].is_await_using = 1;
28515
+ }
28027
28516
  }
28028
28517
  } else if (!is_async && token_is_pseudo_keyword(s, JS_ATOM_async) && peek_token(s, false) == TOK_OF) {
28029
28518
  return js_parse_error(s, "'for of' expression cannot start with 'async'");
@@ -28074,6 +28563,8 @@ static __exception int js_parse_for_in_of(JSParseState *s, int label_name,
28074
28563
  } else if (s->token.val == TOK_IN) {
28075
28564
  if (is_async)
28076
28565
  return js_parse_error(s, "'for await' loop should be used with 'of'");
28566
+ if (tok == TOK_USING)
28567
+ return js_parse_error(s, "using declaration not allowed in for-in");
28077
28568
  if (has_initializer &&
28078
28569
  (tok != TOK_VAR || fd->is_strict_mode || has_destructuring)) {
28079
28570
  initializer_error:
@@ -28136,8 +28627,46 @@ static __exception int js_parse_for_in_of(JSParseState *s, int label_name,
28136
28627
  }
28137
28628
 
28138
28629
  emit_label(s, label_body);
28139
- if (js_parse_statement(s))
28140
- return -1;
28630
+ {
28631
+ bool scope_has_using = fd->scopes[fd->scope_level].has_using;
28632
+ int using_scope_level = fd->scope_level;
28633
+ BlockEnv using_be;
28634
+ int had_using_be = 0;
28635
+
28636
+ if (scope_has_using) {
28637
+ emit_goto(s, OP_catch, fd->scopes[using_scope_level].using_label_catch);
28638
+ push_break_entry(fd, &using_be, JS_ATOM_NULL, -1, -1, 1);
28639
+ using_be.has_using = true;
28640
+ using_be.using_scope_level = using_scope_level;
28641
+ had_using_be = 1;
28642
+ }
28643
+
28644
+ if (js_parse_statement(s))
28645
+ return -1;
28646
+
28647
+ if (had_using_be) {
28648
+ pop_break_entry(fd);
28649
+ }
28650
+
28651
+ if (scope_has_using && js_is_live_code(s)) {
28652
+ emit_op(s, OP_drop);
28653
+ emit_op(s, OP_using_dispose_init);
28654
+ emit_op(s, OP_dispose_scope);
28655
+ emit_u16(s, using_scope_level);
28656
+ emit_op(s, OP_using_dispose_end);
28657
+ emit_goto(s, OP_goto,
28658
+ fd->scopes[using_scope_level].using_label_end);
28659
+ }
28660
+
28661
+ if (scope_has_using) {
28662
+ emit_label(s, fd->scopes[using_scope_level].using_label_catch);
28663
+ emit_op(s, OP_dispose_scope);
28664
+ emit_u16(s, using_scope_level);
28665
+ emit_op(s, OP_throw);
28666
+
28667
+ emit_label(s, fd->scopes[using_scope_level].using_label_end);
28668
+ }
28669
+ }
28141
28670
 
28142
28671
  close_scopes(s, s->cur_func->scope_level, block_scope_level);
28143
28672
 
@@ -28169,6 +28698,7 @@ static __exception int js_parse_for_in_of(JSParseState *s, int label_name,
28169
28698
  emit_label(s, label_break);
28170
28699
  if (is_for_of) {
28171
28700
  /* close and drop enum_rec */
28701
+ emit_source_loc_at(s, source_line_num, source_col_num);
28172
28702
  emit_op(s, OP_iterator_close);
28173
28703
  } else {
28174
28704
  emit_op(s, OP_drop);
@@ -28278,6 +28808,36 @@ static __exception int js_parse_statement_or_decl(JSParseState *s,
28278
28808
  if (js_parse_expect_semi(s))
28279
28809
  goto fail;
28280
28810
  break;
28811
+ case TOK_AWAIT:
28812
+ /* Check for 'await using' declaration */
28813
+ if (s->cur_func->func_kind & JS_FUNC_ASYNC) {
28814
+ JSParsePos pos;
28815
+ int u;
28816
+ js_parse_get_pos(s, &pos);
28817
+ if (next_token(s)) /* skip 'await' */
28818
+ goto fail;
28819
+ u = is_using(s, false);
28820
+ if (u < 0)
28821
+ goto fail;
28822
+ if (u) {
28823
+ if (!(decl_mask & DECL_MASK_OTHER)) {
28824
+ js_parse_error(s, "lexical declarations can't appear in single-statement context");
28825
+ goto fail;
28826
+ }
28827
+ s->cur_func->has_await = true;
28828
+ if (next_token(s)) /* skip 'using' */
28829
+ goto fail;
28830
+ if (js_parse_var(s, PF_IN_ACCEPTED | PF_AWAIT_USING, TOK_USING, /*export_flag*/false))
28831
+ goto fail;
28832
+ if (js_parse_expect_semi(s))
28833
+ goto fail;
28834
+ break;
28835
+ }
28836
+ /* Not 'await using': restore to parse as expression */
28837
+ if (js_parse_seek_token(s, &pos))
28838
+ goto fail;
28839
+ }
28840
+ goto hasexpr;
28281
28841
  case TOK_LET:
28282
28842
  case TOK_CONST:
28283
28843
  haslet:
@@ -28401,10 +28961,14 @@ static __exception int js_parse_statement_or_decl(JSParseState *s,
28401
28961
  {
28402
28962
  int label_cont, label_break, label_body, label_test;
28403
28963
  int pos_cont, pos_body, block_scope_level;
28964
+ int for_scope_level;
28404
28965
  BlockEnv break_entry;
28405
28966
  int tok, bits;
28967
+ int source_line_num, source_col_num;
28406
28968
  bool is_async;
28407
28969
 
28970
+ source_line_num = s->token.line_num;
28971
+ source_col_num = s->token.col_num;
28408
28972
  if (next_token(s))
28409
28973
  goto fail;
28410
28974
 
@@ -28428,7 +28992,8 @@ static __exception int js_parse_statement_or_decl(JSParseState *s,
28428
28992
 
28429
28993
  if (!(bits & SKIP_HAS_SEMI)) {
28430
28994
  /* parse for/in or for/of */
28431
- if (js_parse_for_in_of(s, label_name, is_async))
28995
+ if (js_parse_for_in_of(s, label_name, is_async,
28996
+ source_line_num, source_col_num))
28432
28997
  goto fail;
28433
28998
  break;
28434
28999
  }
@@ -28437,6 +29002,7 @@ static __exception int js_parse_statement_or_decl(JSParseState *s,
28437
29002
  /* create scope for the lexical variables declared in the initial,
28438
29003
  test and increment expressions */
28439
29004
  push_scope(s);
29005
+ for_scope_level = s->cur_func->scope_level;
28440
29006
  /* initial expression */
28441
29007
  tok = s->token.val;
28442
29008
  if (tok != ';') {
@@ -28449,10 +29015,48 @@ static __exception int js_parse_statement_or_decl(JSParseState *s,
28449
29015
  default:
28450
29016
  goto fail;
28451
29017
  }
28452
- if (tok == TOK_VAR || tok == TOK_LET || tok == TOK_CONST) {
29018
+ if (tok != TOK_VAR && tok != TOK_LET && tok != TOK_CONST &&
29019
+ token_is_pseudo_keyword(s, JS_ATOM_using)) {
29020
+ int u = is_using(s, false);
29021
+ if (u < 0)
29022
+ goto fail;
29023
+ if (u)
29024
+ tok = TOK_USING;
29025
+ }
29026
+ if (tok == TOK_AWAIT &&
29027
+ (s->cur_func->func_kind & JS_FUNC_ASYNC)) {
29028
+ /* Check for `await using` declaration head */
29029
+ JSParsePos pos;
29030
+ int u;
29031
+ js_parse_get_pos(s, &pos);
29032
+ if (next_token(s)) /* skip 'await' */
29033
+ goto fail;
29034
+ u = is_using(s, false);
29035
+ if (u < 0)
29036
+ goto fail;
29037
+ if (u) {
29038
+ s->cur_func->has_await = true;
29039
+ tok = TOK_USING;
29040
+ if (next_token(s)) /* skip 'using' */
29041
+ goto fail;
29042
+ if (js_parse_var(s, PF_IN_ACCEPTED | PF_AWAIT_USING,
29043
+ TOK_USING, false))
29044
+ goto fail;
29045
+ goto for_init_done;
29046
+ }
29047
+ if (js_parse_seek_token(s, &pos))
29048
+ goto fail;
29049
+ }
29050
+ if (tok == TOK_VAR
29051
+ || tok == TOK_LET
29052
+ || tok == TOK_CONST
29053
+ || tok == TOK_USING) {
29054
+ int pf = 0;
29055
+ if (tok == TOK_USING && is_async)
29056
+ pf |= PF_AWAIT_USING;
28453
29057
  if (next_token(s))
28454
29058
  goto fail;
28455
- if (js_parse_var(s, 0, tok, /*export_flag*/false))
29059
+ if (js_parse_var(s, pf, tok, /*export_flag*/false))
28456
29060
  goto fail;
28457
29061
  } else {
28458
29062
  if (js_parse_expr2(s, false))
@@ -28460,6 +29064,7 @@ static __exception int js_parse_statement_or_decl(JSParseState *s,
28460
29064
  emit_op(s, OP_drop);
28461
29065
  }
28462
29066
 
29067
+ for_init_done:
28463
29068
  /* close the closures before the first iteration */
28464
29069
  close_scopes(s, s->cur_func->scope_level, block_scope_level);
28465
29070
  }
@@ -28473,6 +29078,11 @@ static __exception int js_parse_statement_or_decl(JSParseState *s,
28473
29078
 
28474
29079
  push_break_entry(s->cur_func, &break_entry,
28475
29080
  label_name, label_break, label_cont, 0);
29081
+ if (s->cur_func->scopes[for_scope_level].has_using) {
29082
+ break_entry.has_using = true;
29083
+ break_entry.using_scope_level = for_scope_level;
29084
+ break_entry.drop_count = 1; /* catch_offset from OP_catch */
29085
+ }
28476
29086
 
28477
29087
  /* test expression */
28478
29088
  if (s->token.val == ';') {
@@ -28540,6 +29150,27 @@ static __exception int js_parse_statement_or_decl(JSParseState *s,
28540
29150
  emit_label(s, label_break);
28541
29151
 
28542
29152
  pop_break_entry(s->cur_func);
29153
+
29154
+ if (s->cur_func->scopes[for_scope_level].has_using) {
29155
+ int label_catch = s->cur_func->scopes[for_scope_level].using_label_catch;
29156
+ int label_end = s->cur_func->scopes[for_scope_level].using_label_end;
29157
+
29158
+ /* Normal exit: drop catch_offset, dispose */
29159
+ emit_op(s, OP_drop);
29160
+ emit_op(s, OP_using_dispose_init);
29161
+ emit_op(s, OP_dispose_scope);
29162
+ emit_u16(s, for_scope_level);
29163
+ emit_op(s, OP_using_dispose_end);
29164
+ emit_goto(s, OP_goto, label_end);
29165
+
29166
+ /* Catch handler: exception on stack */
29167
+ emit_label(s, label_catch);
29168
+ emit_op(s, OP_dispose_scope);
29169
+ emit_u16(s, for_scope_level);
29170
+ emit_op(s, OP_throw);
29171
+
29172
+ emit_label(s, label_end);
29173
+ }
28543
29174
  pop_scope(s);
28544
29175
  }
28545
29176
  break;
@@ -28642,6 +29273,35 @@ static __exception int js_parse_statement_or_decl(JSParseState *s,
28642
29273
  js_parse_error(s, "invalid switch statement");
28643
29274
  goto fail;
28644
29275
  }
29276
+ /* `using` / `await using` declarations are not allowed
29277
+ directly within a CaseClause or DefaultClause
29278
+ (early error per spec). */
29279
+ if (token_is_pseudo_keyword(s, JS_ATOM_using)) {
29280
+ int u = is_using(s, false);
29281
+ if (u < 0)
29282
+ goto fail;
29283
+ if (u) {
29284
+ js_parse_error(s, "using declaration is not allowed in this context");
29285
+ goto fail;
29286
+ }
29287
+ }
29288
+ if (s->token.val == TOK_AWAIT &&
29289
+ (s->cur_func->func_kind & JS_FUNC_ASYNC)) {
29290
+ JSParsePos pos;
29291
+ int u;
29292
+ js_parse_get_pos(s, &pos);
29293
+ if (next_token(s))
29294
+ goto fail;
29295
+ u = is_using(s, false);
29296
+ if (u < 0)
29297
+ goto fail;
29298
+ if (js_parse_seek_token(s, &pos))
29299
+ goto fail;
29300
+ if (u) {
29301
+ js_parse_error(s, "await using declaration is not allowed in this context");
29302
+ goto fail;
29303
+ }
29304
+ }
28645
29305
  if (js_parse_statement_or_decl(s, DECL_MASK_ALL))
28646
29306
  goto fail;
28647
29307
  }
@@ -28875,6 +29535,33 @@ static __exception int js_parse_statement_or_decl(JSParseState *s,
28875
29535
  default:
28876
29536
  goto fail;
28877
29537
  }
29538
+ if (token_is_pseudo_keyword(s, JS_ATOM_using)) {
29539
+ int u = is_using(s, false);
29540
+ if (u < 0)
29541
+ goto fail;
29542
+ if (u) {
29543
+ JSFunctionDef *fd = s->cur_func;
29544
+ if (!(decl_mask & DECL_MASK_OTHER)) {
29545
+ js_parse_error(s, "lexical declarations can't appear in single-statement context");
29546
+ goto fail;
29547
+ }
29548
+ if (fd->is_eval &&
29549
+ (fd->eval_type == JS_EVAL_TYPE_GLOBAL ||
29550
+ fd->eval_type == JS_EVAL_TYPE_DIRECT ||
29551
+ fd->eval_type == JS_EVAL_TYPE_INDIRECT) &&
29552
+ fd->scope_level == fd->body_scope) {
29553
+ js_parse_error(s, "using declaration is not allowed at the top level of a script");
29554
+ goto fail;
29555
+ }
29556
+ if (next_token(s))
29557
+ goto fail;
29558
+ if (js_parse_var(s, PF_IN_ACCEPTED, TOK_USING, /*export_flag*/false))
29559
+ goto fail;
29560
+ if (js_parse_expect_semi(s))
29561
+ goto fail;
29562
+ break;
29563
+ }
29564
+ }
28878
29565
  if (token_is_pseudo_keyword(s, JS_ATOM_async) &&
28879
29566
  peek_token(s, true) == TOK_FUNCTION) {
28880
29567
  if (!(decl_mask & DECL_MASK_OTHER)) {
@@ -29037,33 +29724,6 @@ static void js_free_module_def(JSContext *ctx, JSModuleDef *m)
29037
29724
  js_free(ctx, m);
29038
29725
  }
29039
29726
 
29040
- /* mikrojs patch: public wrapper to free a single module (remove it from
29041
- * the context's loaded-modules cache and release all its resources).
29042
- * Callers must ensure the module is not mid-linking or mid-evaluating and
29043
- * that no other modules hold live import bindings to its exports (or accept
29044
- * that such bindings remain valid via refcounted JSVarRefs). */
29045
- void JS_FreeModule(JSContext *ctx, JSModuleDef *m)
29046
- {
29047
- js_free_module_def(ctx, m);
29048
- }
29049
-
29050
- static JSModuleDef *js_find_loaded_module(JSContext *ctx, JSAtom name);
29051
-
29052
- /* mikrojs patch: lookup a module in the context cache by name atom.
29053
- * Returns NULL if no module with that name is loaded. */
29054
- JSModuleDef *JS_FindLoadedModule(JSContext *ctx, JSAtom name)
29055
- {
29056
- return js_find_loaded_module(ctx, name);
29057
- }
29058
-
29059
- /* mikrojs patch: return the module's current status as an integer.
29060
- * 0=UNLINKED, 1=LINKING, 2=LINKED, 3=EVALUATING, 4=EVALUATING_ASYNC, 5=EVALUATED. */
29061
- int JS_GetModuleStatus(JSContext *ctx, JSModuleDef *m)
29062
- {
29063
- (void)ctx;
29064
- return (int)m->status;
29065
- }
29066
-
29067
29727
  #ifndef QJS_DISABLE_PARSER
29068
29728
 
29069
29729
  static int add_req_module_entry(JSContext *ctx, JSModuleDef *m,
@@ -31247,6 +31907,7 @@ static __exception int js_parse_export(JSParseState *s)
31247
31907
  case TOK_VAR:
31248
31908
  case TOK_LET:
31249
31909
  case TOK_CONST:
31910
+ case TOK_USING:
31250
31911
  return js_parse_var(s, PF_IN_ACCEPTED, tok, /*export_flag*/true);
31251
31912
  default:
31252
31913
  return js_parse_error(s, "invalid export syntax");
@@ -31500,6 +32161,10 @@ static JSFunctionDef *js_new_function_def(JSContext *ctx,
31500
32161
  fd->scope_count = 1;
31501
32162
  fd->scopes[0].first = -1;
31502
32163
  fd->scopes[0].parent = -1;
32164
+ fd->scopes[0].has_using = 0;
32165
+ fd->scopes[0].is_await_using = 0;
32166
+ fd->scopes[0].using_label_catch = -1;
32167
+ fd->scopes[0].using_label_end = -1;
31503
32168
  fd->scope_level = 0; /* 0: var/arg scope */
31504
32169
  fd->scope_first = -1;
31505
32170
  fd->body_scope = -1;
@@ -33861,6 +34526,62 @@ static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s)
33861
34526
  }
33862
34527
  break;
33863
34528
 
34529
+ case OP_dispose_scope:
34530
+ {
34531
+ int scope_idx, scope = get_u16(bc_buf + pos + 1);
34532
+ bool is_async = s->scopes[scope].is_await_using;
34533
+
34534
+ for(scope_idx = s->scopes[scope].first; scope_idx >= 0;) {
34535
+ JSVarDef *vd = &s->vars[scope_idx];
34536
+ if (vd->scope_level == scope) {
34537
+ if (vd->var_kind == JS_VAR_USING) {
34538
+ if (is_async) {
34539
+ int label_catch = new_label_fd(s);
34540
+ int label_end = new_label_fd(s);
34541
+ if (label_catch < 0 || label_end < 0) {
34542
+ dbuf_set_error(&bc_out);
34543
+ break;
34544
+ }
34545
+
34546
+ dbuf_putc(&bc_out, OP_catch);
34547
+ dbuf_put_u32(&bc_out, label_catch);
34548
+ update_label(s, label_catch, 1);
34549
+ s->jump_size++;
34550
+
34551
+ dbuf_putc(&bc_out, OP_using_dispose_async);
34552
+ dbuf_put_u16(&bc_out, scope_idx);
34553
+
34554
+ dbuf_putc(&bc_out, OP_await);
34555
+ dbuf_putc(&bc_out, OP_drop);
34556
+ dbuf_putc(&bc_out, OP_drop);
34557
+
34558
+ dbuf_putc(&bc_out, OP_goto);
34559
+ dbuf_put_u32(&bc_out, label_end);
34560
+ update_label(s, label_end, 1);
34561
+ s->jump_size++;
34562
+
34563
+ dbuf_putc(&bc_out, OP_label);
34564
+ dbuf_put_u32(&bc_out, label_catch);
34565
+ s->label_slots[label_catch].pos2 = bc_out.size;
34566
+
34567
+ dbuf_putc(&bc_out, OP_using_dispose_merge);
34568
+
34569
+ dbuf_putc(&bc_out, OP_label);
34570
+ dbuf_put_u32(&bc_out, label_end);
34571
+ s->label_slots[label_end].pos2 = bc_out.size;
34572
+ } else {
34573
+ dbuf_putc(&bc_out, OP_using_dispose);
34574
+ dbuf_put_u16(&bc_out, scope_idx);
34575
+ }
34576
+ }
34577
+ scope_idx = vd->scope_next;
34578
+ } else {
34579
+ break;
34580
+ }
34581
+ }
34582
+ }
34583
+ break;
34584
+
33864
34585
  case OP_set_name:
33865
34586
  {
33866
34587
  /* remove dummy set_name opcodes */
@@ -35671,6 +36392,7 @@ static __exception int js_parse_directives(JSParseState *s)
35671
36392
  case TOK_IF:
35672
36393
  case TOK_RETURN:
35673
36394
  case TOK_VAR:
36395
+ case TOK_USING:
35674
36396
  case TOK_THIS:
35675
36397
  case TOK_DELETE:
35676
36398
  case TOK_TYPEOF:
@@ -36235,25 +36957,59 @@ static __exception int js_parse_function_decl2(JSParseState *s,
36235
36957
  if (js_parse_function_check_names(s, fd, func_name))
36236
36958
  goto fail;
36237
36959
 
36238
- while (s->token.val != '}') {
36239
- if (js_parse_source_element(s))
36960
+ {
36961
+ BlockEnv using_be;
36962
+ int has_using_be = 0;
36963
+
36964
+ while (s->token.val != '}') {
36965
+ if (js_parse_source_element(s))
36966
+ goto fail;
36967
+ /* Check if a 'using' was encountered in the body scope */
36968
+ if (!has_using_be && fd->scopes[fd->body_scope].has_using) {
36969
+ has_using_be = 1;
36970
+ push_break_entry(fd, &using_be, JS_ATOM_NULL, -1, -1, 1);
36971
+ using_be.has_using = true;
36972
+ using_be.using_scope_level = fd->body_scope;
36973
+ }
36974
+ }
36975
+
36976
+ /* save the function source code */
36977
+ fd->source_len = s->buf_ptr - ptr;
36978
+ fd->source = js_strndup(ctx, (const char *)ptr, fd->source_len);
36979
+ if (!fd->source)
36240
36980
  goto fail;
36241
- }
36242
36981
 
36243
- /* save the function source code */
36244
- fd->source_len = s->buf_ptr - ptr;
36245
- fd->source = js_strndup(ctx, (const char *)ptr, fd->source_len);
36246
- if (!fd->source)
36247
- goto fail;
36982
+ if (next_token(s)) {
36983
+ /* consume the '}' */
36984
+ goto fail;
36985
+ }
36248
36986
 
36249
- if (next_token(s)) {
36250
- /* consume the '}' */
36251
- goto fail;
36252
- }
36987
+ if (has_using_be) {
36988
+ int label_catch = fd->scopes[fd->body_scope].using_label_catch;
36989
+ int label_end = fd->scopes[fd->body_scope].using_label_end;
36253
36990
 
36254
- /* in case there is no return, add one */
36255
- if (js_is_live_code(s)) {
36256
- emit_return(s, false);
36991
+ pop_break_entry(fd);
36992
+
36993
+ if (js_is_live_code(s)) {
36994
+ emit_op(s, OP_drop); /* drop catch_offset */
36995
+ emit_op(s, OP_using_dispose_init); /* initial error_state */
36996
+ emit_op(s, OP_dispose_scope);
36997
+ emit_u16(s, fd->body_scope);
36998
+ emit_op(s, OP_using_dispose_end);
36999
+ emit_return(s, false);
37000
+ }
37001
+
37002
+ emit_label(s, label_catch);
37003
+ emit_op(s, OP_dispose_scope);
37004
+ emit_u16(s, fd->body_scope);
37005
+ emit_op(s, OP_throw);
37006
+
37007
+ emit_label(s, label_end);
37008
+ } else {
37009
+ if (js_is_live_code(s)) {
37010
+ emit_return(s, false);
37011
+ }
37012
+ }
36257
37013
  }
36258
37014
  done:
36259
37015
  s->cur_func = fd->parent;
@@ -36391,6 +37147,8 @@ static __exception int js_parse_program(JSParseState *s)
36391
37147
  {
36392
37148
  JSFunctionDef *fd = s->cur_func;
36393
37149
  int idx;
37150
+ BlockEnv using_be;
37151
+ int has_using_be = 0;
36394
37152
 
36395
37153
  if (next_token(s))
36396
37154
  return -1;
@@ -36412,28 +37170,71 @@ static __exception int js_parse_program(JSParseState *s)
36412
37170
  while (s->token.val != TOK_EOF) {
36413
37171
  if (js_parse_source_element(s))
36414
37172
  return -1;
37173
+ /* Check if a 'using' was encountered at the body scope level */
37174
+ if (!has_using_be && fd->scopes[fd->body_scope].has_using) {
37175
+ has_using_be = 1;
37176
+ push_break_entry(fd, &using_be, JS_ATOM_NULL, -1, -1, 1);
37177
+ using_be.has_using = true;
37178
+ using_be.using_scope_level = fd->body_scope;
37179
+ }
36415
37180
  }
36416
37181
 
36417
- if (!s->is_module) {
36418
- /* return the value of the hidden variable eval_ret_idx */
36419
- if (fd->func_kind == JS_FUNC_ASYNC) {
36420
- /* wrap the return value in an object so that promises can
36421
- be safely returned */
36422
- emit_op(s, OP_object);
36423
- emit_op(s, OP_dup);
37182
+ if (has_using_be) {
37183
+ int label_catch = fd->scopes[fd->body_scope].using_label_catch;
37184
+ int label_end = fd->scopes[fd->body_scope].using_label_end;
36424
37185
 
36425
- emit_op(s, OP_get_loc);
36426
- emit_u16(s, fd->eval_ret_idx);
37186
+ pop_break_entry(fd);
36427
37187
 
36428
- emit_op(s, OP_put_field);
36429
- emit_atom(s, JS_ATOM_value);
37188
+ if (js_is_live_code(s)) {
37189
+ /* Normal path: drop catch_offset, dispose, then return */
37190
+ emit_op(s, OP_drop); /* drop catch_offset */
37191
+ emit_op(s, OP_using_dispose_init); /* initial error_state */
37192
+ emit_op(s, OP_dispose_scope);
37193
+ emit_u16(s, fd->body_scope);
37194
+ emit_op(s, OP_using_dispose_end);
37195
+ }
37196
+
37197
+ if (!s->is_module) {
37198
+ if (fd->func_kind == JS_FUNC_ASYNC) {
37199
+ emit_op(s, OP_object);
37200
+ emit_op(s, OP_dup);
37201
+ emit_op(s, OP_get_loc);
37202
+ emit_u16(s, fd->eval_ret_idx);
37203
+ emit_op(s, OP_put_field);
37204
+ emit_atom(s, JS_ATOM_value);
37205
+ } else {
37206
+ emit_op(s, OP_get_loc);
37207
+ emit_u16(s, fd->eval_ret_idx);
37208
+ }
37209
+ emit_return(s, true);
36430
37210
  } else {
36431
- emit_op(s, OP_get_loc);
36432
- emit_u16(s, fd->eval_ret_idx);
37211
+ emit_return(s, false);
36433
37212
  }
36434
- emit_return(s, true);
37213
+
37214
+ /* Catch handler */
37215
+ emit_label(s, label_catch);
37216
+ emit_op(s, OP_dispose_scope);
37217
+ emit_u16(s, fd->body_scope);
37218
+ emit_op(s, OP_throw);
37219
+
37220
+ emit_label(s, label_end);
36435
37221
  } else {
36436
- emit_return(s, false);
37222
+ if (!s->is_module) {
37223
+ if (fd->func_kind == JS_FUNC_ASYNC) {
37224
+ emit_op(s, OP_object);
37225
+ emit_op(s, OP_dup);
37226
+ emit_op(s, OP_get_loc);
37227
+ emit_u16(s, fd->eval_ret_idx);
37228
+ emit_op(s, OP_put_field);
37229
+ emit_atom(s, JS_ATOM_value);
37230
+ } else {
37231
+ emit_op(s, OP_get_loc);
37232
+ emit_u16(s, fd->eval_ret_idx);
37233
+ }
37234
+ emit_return(s, true);
37235
+ } else {
37236
+ emit_return(s, false);
37237
+ }
36437
37238
  }
36438
37239
 
36439
37240
  return 0;
@@ -36852,7 +37653,7 @@ typedef enum BCTagEnum {
36852
37653
  BC_TAG_SYMBOL,
36853
37654
  } BCTagEnum;
36854
37655
 
36855
- #define BC_VERSION 25
37656
+ #define BC_VERSION 26
36856
37657
 
36857
37658
  typedef struct BCWriterState {
36858
37659
  JSContext *ctx;
@@ -39256,6 +40057,20 @@ static JSValue JS_NewGlobalCConstructor(JSContext *ctx, const char *name,
39256
40057
  return func_obj;
39257
40058
  }
39258
40059
 
40060
+ static JSValue JS_NewGlobalCConstructorMagic(JSContext *ctx, const char *name,
40061
+ JSCFunctionMagic *func, int length,
40062
+ JSValueConst proto, int magic)
40063
+ {
40064
+ /* Used to squelch a -Wcast-function-type warning. */
40065
+ JSCFunctionType ft = { .constructor_magic = func };
40066
+ JSValue func_obj;
40067
+
40068
+ func_obj = JS_NewCFunction2(ctx, ft.constructor, name, length,
40069
+ JS_CFUNC_constructor_or_func_magic, magic);
40070
+ JS_NewGlobalCConstructor2(ctx, func_obj, name, proto);
40071
+ return func_obj;
40072
+ }
40073
+
39259
40074
  static JSValue JS_NewObjectProtoList(JSContext *ctx, JSValueConst proto,
39260
40075
  const JSCFunctionListEntry *fields, int n_fields)
39261
40076
  {
@@ -39427,7 +40242,7 @@ JSValue JS_ToObject(JSContext *ctx, JSValueConst val)
39427
40242
  if (!JS_IsException(obj)) {
39428
40243
  JS_DefinePropertyValue(ctx, obj, JS_ATOM_length,
39429
40244
  JS_NewInt32(ctx, JS_VALUE_GET_STRING(str)->len), 0);
39430
- JS_SetObjectData(ctx, obj, JS_DupValue(ctx, str));
40245
+ JS_SetObjectData(ctx, obj, js_dup(str));
39431
40246
  }
39432
40247
  JS_FreeValue(ctx, str);
39433
40248
  return obj;
@@ -41101,10 +41916,16 @@ static JSValue js_error_constructor(JSContext *ctx, JSValueConst new_target,
41101
41916
  JS_FreeValue(ctx, proto);
41102
41917
  if (JS_IsException(obj))
41103
41918
  return obj;
41104
- if (magic == JS_AGGREGATE_ERROR) {
41919
+ switch (magic) {
41920
+ case JS_AGGREGATE_ERROR:
41105
41921
  message = argv[1];
41106
41922
  opts = 2;
41107
- } else {
41923
+ break;
41924
+ case JS_SUPPRESSED_ERROR:
41925
+ message = argv[2];
41926
+ opts = 3;
41927
+ break;
41928
+ default:
41108
41929
  message = argv[0];
41109
41930
  opts = 1;
41110
41931
  }
@@ -41138,6 +41959,15 @@ static JSValue js_error_constructor(JSContext *ctx, JSValueConst new_target,
41138
41959
  JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
41139
41960
  }
41140
41961
 
41962
+ if (magic == JS_SUPPRESSED_ERROR) {
41963
+ JS_DefinePropertyValue(ctx, obj, JS_ATOM_error,
41964
+ js_dup(argv[0]),
41965
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
41966
+ JS_DefinePropertyValue(ctx, obj, JS_ATOM_suppressed,
41967
+ js_dup(argv[1]),
41968
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
41969
+ }
41970
+
41141
41971
  /* skip the Error() function in the backtrace */
41142
41972
  build_backtrace(ctx, obj, JS_UNDEFINED, NULL, 0, 0, JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL);
41143
41973
  return obj;
@@ -41626,11 +42456,6 @@ static JSValue js_array_with(JSContext *ctx, JSValueConst this_val,
41626
42456
  if (js_get_length64(ctx, &len, obj))
41627
42457
  goto exception;
41628
42458
 
41629
- if (len > UINT32_MAX) {
41630
- JS_ThrowRangeError(ctx, "invalid array length");
41631
- goto exception;
41632
- }
41633
-
41634
42459
  if (JS_ToInt64Sat(ctx, &idx, argv[0]))
41635
42460
  goto exception;
41636
42461
 
@@ -41642,15 +42467,11 @@ static JSValue js_array_with(JSContext *ctx, JSValueConst this_val,
41642
42467
  goto exception;
41643
42468
  }
41644
42469
 
41645
- arr = JS_NewArray(ctx);
42470
+ arr = js_allocate_fast_array(ctx, len);
41646
42471
  if (JS_IsException(arr))
41647
42472
  goto exception;
41648
42473
 
41649
42474
  p = JS_VALUE_GET_OBJ(arr);
41650
- if (expand_fast_array(ctx, p, len) < 0)
41651
- goto exception;
41652
- p->u.array.count = len;
41653
-
41654
42475
  i = 0;
41655
42476
  pval = p->u.array.u.values;
41656
42477
  if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) {
@@ -41662,21 +42483,14 @@ static JSValue js_array_with(JSContext *ctx, JSValueConst this_val,
41662
42483
  } else {
41663
42484
  for (; i < idx; i++, pval++)
41664
42485
  if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval))
41665
- goto fill_and_fail;
42486
+ goto exception;
41666
42487
  *pval = js_dup(argv[1]);
41667
42488
  for (i++, pval++; i < len; i++, pval++) {
41668
- if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval)) {
41669
- fill_and_fail:
41670
- for (; i < len; i++, pval++)
41671
- *pval = JS_UNDEFINED;
42489
+ if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval))
41672
42490
  goto exception;
41673
- }
41674
42491
  }
41675
42492
  }
41676
42493
 
41677
- if (JS_SetProperty(ctx, arr, JS_ATOM_length, js_int64(len)) < 0)
41678
- goto exception;
41679
-
41680
42494
  ret = arr;
41681
42495
  arr = JS_UNDEFINED;
41682
42496
 
@@ -42454,7 +43268,7 @@ static JSValue js_array_push(JSContext *ctx, JSValueConst this_val,
42454
43268
  (p->shape->prop->flags & JS_PROP_WRITABLE))) {
42455
43269
  array_len = JS_VALUE_GET_INT(p->prop[0].u.value);
42456
43270
  new_len = array_len + argc;
42457
- if (likely(new_len >= array_len)) { /* no overflow */
43271
+ if (likely(new_len >= array_len && new_len <= (uint32_t)INT32_MAX)) { /* no overflow and within fast-array bounds */
42458
43272
  if (unlikely(new_len > p->u.array.u1.size)) {
42459
43273
  if (expand_fast_array(ctx, p, new_len))
42460
43274
  return JS_EXCEPTION;
@@ -42463,7 +43277,7 @@ static JSValue js_array_push(JSContext *ctx, JSValueConst this_val,
42463
43277
  p->u.array.u.values[array_len + i] = js_dup(argv[i]);
42464
43278
  }
42465
43279
  p->u.array.count = new_len;
42466
- p->prop[0].u.value = js_int32(new_len);
43280
+ p->prop[0].u.value = js_uint32(new_len);
42467
43281
  return js_int32(new_len);
42468
43282
  }
42469
43283
  }
@@ -42586,20 +43400,12 @@ static JSValue js_array_toReversed(JSContext *ctx, JSValueConst this_val,
42586
43400
  if (js_get_length64(ctx, &len, obj))
42587
43401
  goto exception;
42588
43402
 
42589
- if (len > UINT32_MAX) {
42590
- JS_ThrowRangeError(ctx, "invalid array length");
42591
- goto exception;
42592
- }
42593
-
42594
- arr = JS_NewArray(ctx);
43403
+ arr = js_allocate_fast_array(ctx, len);
42595
43404
  if (JS_IsException(arr))
42596
43405
  goto exception;
42597
43406
 
42598
43407
  if (len > 0) {
42599
43408
  p = JS_VALUE_GET_OBJ(arr);
42600
- if (expand_fast_array(ctx, p, len) < 0)
42601
- goto exception;
42602
- p->u.array.count = len;
42603
43409
 
42604
43410
  i = len - 1;
42605
43411
  pval = p->u.array.u.values;
@@ -42609,17 +43415,10 @@ static JSValue js_array_toReversed(JSContext *ctx, JSValueConst this_val,
42609
43415
  } else {
42610
43416
  // Query order is observable; test262 expects descending order.
42611
43417
  for (; i >= 0; i--, pval++) {
42612
- if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval)) {
42613
- // Exception; initialize remaining elements.
42614
- for (; i >= 0; i--, pval++)
42615
- *pval = JS_UNDEFINED;
43418
+ if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval))
42616
43419
  goto exception;
42617
- }
42618
43420
  }
42619
43421
  }
42620
-
42621
- if (JS_SetProperty(ctx, arr, JS_ATOM_length, js_int64(len)) < 0)
42622
- goto exception;
42623
43422
  }
42624
43423
 
42625
43424
  ret = arr;
@@ -42745,8 +43544,6 @@ static JSValue js_array_toSpliced(JSContext *ctx, JSValueConst this_val,
42745
43544
  int64_t i, j, len, newlen, start, add, del;
42746
43545
  uint32_t count32;
42747
43546
 
42748
- pval = NULL;
42749
- last = NULL;
42750
43547
  ret = JS_EXCEPTION;
42751
43548
  arr = JS_UNDEFINED;
42752
43549
 
@@ -42771,17 +43568,12 @@ static JSValue js_array_toSpliced(JSContext *ctx, JSValueConst this_val,
42771
43568
  add = argc - 2;
42772
43569
 
42773
43570
  newlen = len + add - del;
42774
- if (newlen > UINT32_MAX) {
42775
- // Per spec: TypeError if newlen >= 2**53, RangeError below
42776
- if (newlen > MAX_SAFE_INTEGER) {
42777
- JS_ThrowTypeError(ctx, "invalid array length");
42778
- } else {
42779
- JS_ThrowRangeError(ctx, "invalid array length");
42780
- }
43571
+ if (newlen > MAX_SAFE_INTEGER) {
43572
+ JS_ThrowTypeError(ctx, "invalid array length");
42781
43573
  goto exception;
42782
43574
  }
42783
43575
 
42784
- arr = JS_NewArray(ctx);
43576
+ arr = js_allocate_fast_array(ctx, newlen);
42785
43577
  if (JS_IsException(arr))
42786
43578
  goto exception;
42787
43579
 
@@ -42789,10 +43581,6 @@ static JSValue js_array_toSpliced(JSContext *ctx, JSValueConst this_val,
42789
43581
  goto done;
42790
43582
 
42791
43583
  p = JS_VALUE_GET_OBJ(arr);
42792
- if (expand_fast_array(ctx, p, newlen) < 0)
42793
- goto exception;
42794
-
42795
- p->u.array.count = newlen;
42796
43584
  pval = &p->u.array.u.values[0];
42797
43585
  last = &p->u.array.u.values[newlen];
42798
43586
 
@@ -42816,17 +43604,11 @@ static JSValue js_array_toSpliced(JSContext *ctx, JSValueConst this_val,
42816
43604
 
42817
43605
  assert(pval == last);
42818
43606
 
42819
- if (JS_SetProperty(ctx, arr, JS_ATOM_length, js_int64(newlen)) < 0)
42820
- goto exception;
42821
-
42822
43607
  done:
42823
43608
  ret = arr;
42824
43609
  arr = JS_UNDEFINED;
42825
43610
 
42826
43611
  exception:
42827
- while (pval != last)
42828
- *pval++ = JS_UNDEFINED;
42829
-
42830
43612
  JS_FreeValue(ctx, arr);
42831
43613
  JS_FreeValue(ctx, obj);
42832
43614
  return ret;
@@ -43159,21 +43941,12 @@ static JSValue js_array_toSorted(JSContext *ctx, JSValueConst this_val,
43159
43941
  if (js_get_length64(ctx, &len, obj))
43160
43942
  goto exception;
43161
43943
 
43162
- if (len > UINT32_MAX) {
43163
- JS_ThrowRangeError(ctx, "invalid array length");
43164
- goto exception;
43165
- }
43166
-
43167
- arr = JS_NewArray(ctx);
43944
+ arr = js_allocate_fast_array(ctx, len);
43168
43945
  if (JS_IsException(arr))
43169
43946
  goto exception;
43170
43947
 
43171
43948
  if (len > 0) {
43172
43949
  p = JS_VALUE_GET_OBJ(arr);
43173
- if (expand_fast_array(ctx, p, len) < 0)
43174
- goto exception;
43175
- p->u.array.count = len;
43176
-
43177
43950
  i = 0;
43178
43951
  pval = p->u.array.u.values;
43179
43952
  if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) {
@@ -43181,16 +43954,10 @@ static JSValue js_array_toSorted(JSContext *ctx, JSValueConst this_val,
43181
43954
  *pval = js_dup(arrp[i]);
43182
43955
  } else {
43183
43956
  for (; i < len; i++, pval++) {
43184
- if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval)) {
43185
- for (; i < len; i++, pval++)
43186
- *pval = JS_UNDEFINED;
43957
+ if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval))
43187
43958
  goto exception;
43188
- }
43189
43959
  }
43190
43960
  }
43191
-
43192
- if (JS_SetProperty(ctx, arr, JS_ATOM_length, js_int64(len)) < 0)
43193
- goto exception;
43194
43961
  }
43195
43962
 
43196
43963
  ret = js_array_sort(ctx, arr, argc, argv);
@@ -44048,6 +44815,66 @@ exception:
44048
44815
  return JS_EXCEPTION;
44049
44816
  }
44050
44817
 
44818
+ static JSValue js_async_dispose_to_undef(JSContext *ctx, JSValueConst this_val,
44819
+ int argc, JSValueConst *argv,
44820
+ int magic, JSValueConst *func_data);
44821
+
44822
+ static JSValue js_iterator_proto_dispose(JSContext *ctx, JSValueConst this_val,
44823
+ int argc, JSValueConst *argv)
44824
+ {
44825
+ JSValue method;
44826
+
44827
+ method = JS_GetProperty(ctx, this_val, JS_ATOM_return);
44828
+ if (JS_IsException(method))
44829
+ return JS_EXCEPTION;
44830
+ if (JS_IsUndefined(method))
44831
+ return JS_UNDEFINED;
44832
+ return JS_CallFree(ctx, method, this_val, 0, NULL);
44833
+ }
44834
+
44835
+ static JSValue js_async_iterator_proto_dispose(JSContext *ctx,
44836
+ JSValueConst this_val,
44837
+ int argc, JSValueConst *argv)
44838
+ {
44839
+ JSValue method, ret, promise, undef_fn, then_args[1], result;
44840
+ JSValue undef = JS_UNDEFINED;
44841
+
44842
+ method = JS_GetProperty(ctx, this_val, JS_ATOM_return);
44843
+ if (JS_IsException(method)) {
44844
+ JSValue exc = JS_GetException(ctx);
44845
+ JSValue p = js_promise_resolve(ctx, ctx->promise_ctor, 1, vc(&exc), 1);
44846
+ JS_FreeValue(ctx, exc);
44847
+ return p;
44848
+ }
44849
+ if (JS_IsUndefined(method)) {
44850
+ return js_promise_resolve(ctx, ctx->promise_ctor, 1, vc(&undef), 0);
44851
+ }
44852
+ ret = JS_Call(ctx, method, this_val, 0, NULL);
44853
+ JS_FreeValue(ctx, method);
44854
+ if (JS_IsException(ret)) {
44855
+ JSValue exc = JS_GetException(ctx);
44856
+ JSValue p = js_promise_resolve(ctx, ctx->promise_ctor, 1, vc(&exc), 1);
44857
+ JS_FreeValue(ctx, exc);
44858
+ return p;
44859
+ }
44860
+ /* Wrap in Promise.resolve(ret).then(() => undefined) */
44861
+ promise = js_promise_resolve(ctx, ctx->promise_ctor, 1, vc(&ret), 0);
44862
+ JS_FreeValue(ctx, ret);
44863
+ if (JS_IsException(promise))
44864
+ return promise;
44865
+ undef_fn = JS_NewCFunctionData(ctx, js_async_dispose_to_undef, 0, 0, 0,
44866
+ NULL);
44867
+ if (JS_IsException(undef_fn)) {
44868
+ JS_FreeValue(ctx, promise);
44869
+ return JS_EXCEPTION;
44870
+ }
44871
+ then_args[0] = undef_fn;
44872
+ result = JS_Invoke(ctx, promise, JS_ATOM_then, 1, vc(then_args));
44873
+ JS_FreeValue(ctx, undef_fn);
44874
+ JS_FreeValue(ctx, promise);
44875
+ return result;
44876
+ }
44877
+
44051
44878
  static JSValue js_iterator_proto_iterator(JSContext *ctx, JSValueConst this_val,
44052
44879
  int argc, JSValueConst *argv)
44053
44880
  {
@@ -44376,6 +45203,7 @@ static const JSCFunctionListEntry js_iterator_proto_funcs[] = {
44376
45203
  JS_CFUNC_MAGIC_DEF("some", 1, js_iterator_proto_func, JS_ITERATOR_HELPER_KIND_SOME ),
44377
45204
  JS_CFUNC_DEF("reduce", 1, js_iterator_proto_reduce ),
44378
45205
  JS_CFUNC_DEF("toArray", 0, js_iterator_proto_toArray ),
45206
+ JS_CFUNC_DEF("[Symbol.dispose]", 0, js_iterator_proto_dispose ),
44379
45207
  JS_CFUNC_DEF("[Symbol.iterator]", 0, js_iterator_proto_iterator ),
44380
45208
  JS_CGETSET_DEF("[Symbol.toStringTag]", js_iterator_proto_get_toStringTag, js_iterator_proto_set_toStringTag),
44381
45209
  };
@@ -51137,6 +51965,8 @@ static const JSCFunctionListEntry js_symbol_funcs[] = {
51137
51965
  JS_PROP_SYMBOL_DEF("species", JS_ATOM_Symbol_species, 0),
51138
51966
  JS_PROP_SYMBOL_DEF("unscopables", JS_ATOM_Symbol_unscopables, 0),
51139
51967
  JS_PROP_SYMBOL_DEF("asyncIterator", JS_ATOM_Symbol_asyncIterator, 0),
51968
+ JS_PROP_SYMBOL_DEF("dispose", JS_ATOM_Symbol_dispose, 0),
51969
+ JS_PROP_SYMBOL_DEF("asyncDispose", JS_ATOM_Symbol_asyncDispose, 0),
51140
51970
  };
51141
51971
 
51142
51972
  /* Set/Map/WeakSet/WeakMap */
@@ -52729,6 +53559,658 @@ static const JSCFunctionListEntry js_generator_proto_funcs[] = {
52729
53559
  JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Generator", JS_PROP_CONFIGURABLE),
52730
53560
  };
52731
53561
 
53562
+ /* Explicit resource management */
53563
+
53564
+ enum {
53565
+ JS_DISPOSE_HINT_SYNC, /* use: Call(method, value) */
53566
+ JS_DISPOSE_HINT_ADOPT, /* adopt: Call(method, undefined, [value]) */
53567
+ JS_DISPOSE_HINT_DEFER, /* defer: Call(method, undefined) */
53568
+ };
53569
+
53570
+ typedef struct JSDisposableResource {
53571
+ JSValue value;
53572
+ JSValue method; /* dispose method */
53573
+ uint8_t hint;
53574
+ } JSDisposableResource;
53575
+
53576
+ typedef struct JSDisposableStack {
53577
+ bool disposed;
53578
+ int resource_count;
53579
+ int resource_capacity;
53580
+ JSDisposableResource *resources;
53581
+ } JSDisposableStack;
53582
+
53583
+ static JSValue js_new_suppressed_error(JSContext *ctx, JSValueConst error,
53584
+ JSValueConst suppressed)
53585
+ {
53586
+ JSValue obj;
53587
+
53588
+ /* Construct via the intrinsic prototype rather than going through
53589
+ SuppressedError.prototype.constructor, which is user-writable. */
53590
+ obj = JS_NewObjectProtoClass(ctx,
53591
+ ctx->native_error_proto[JS_SUPPRESSED_ERROR],
53592
+ JS_CLASS_ERROR);
53593
+ if (JS_IsException(obj))
53594
+ return JS_EXCEPTION;
53595
+ JS_DefinePropertyValue(ctx, obj, JS_ATOM_error, js_dup(error),
53596
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
53597
+ JS_DefinePropertyValue(ctx, obj, JS_ATOM_suppressed, js_dup(suppressed),
53598
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
53599
+ build_backtrace(ctx, obj, JS_UNDEFINED, NULL, 0, 0,
53600
+ JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL);
53601
+ return obj;
53602
+ }
53603
+
53604
+ /* Perform DisposeResources. Returns 0 on success, -1 on exception.
53605
+ completion_error is the pending error (JS_UNDEFINED if none).
53606
+ It is consumed (freed) by this function. */
53607
+ static int js_dispose_resources(JSContext *ctx, JSDisposableStack *ds,
53608
+ JSValue completion_error)
53609
+ {
53610
+ JSValue error = completion_error;
53611
+ bool has_error = !JS_IsUndefined(error);
53612
+ int i;
53613
+
53614
+ ds->disposed = true;
53615
+ /* dispose in LIFO order */
53616
+ for (i = ds->resource_count - 1; i >= 0; i--) {
53617
+ JSDisposableResource *res = &ds->resources[i];
53618
+ JSValue ret;
53619
+ if (JS_IsUndefined(res->method)) {
53620
+ /* null/undefined resource, skip */
53621
+ JS_FreeValue(ctx, res->value);
53622
+ continue;
53623
+ }
53624
+ switch (res->hint) {
53625
+ case JS_DISPOSE_HINT_ADOPT:
53626
+ ret = JS_Call(ctx, res->method, JS_UNDEFINED, 1, vc(&res->value));
53627
+ break;
53628
+ case JS_DISPOSE_HINT_DEFER:
53629
+ ret = JS_Call(ctx, res->method, JS_UNDEFINED, 0, NULL);
53630
+ break;
53631
+ default: /* JS_DISPOSE_HINT_SYNC */
53632
+ ret = JS_Call(ctx, res->method, res->value, 0, NULL);
53633
+ break;
53634
+ }
53635
+ JS_FreeValue(ctx, res->value);
53636
+ JS_FreeValue(ctx, res->method);
53637
+ if (JS_IsException(ret)) {
53638
+ JSValue new_error = JS_GetException(ctx);
53639
+ if (has_error) {
53640
+ JSValue suppressed = js_new_suppressed_error(ctx, new_error, error);
53641
+ JS_FreeValue(ctx, new_error);
53642
+ JS_FreeValue(ctx, error);
53643
+ if (JS_IsException(suppressed)) {
53644
+ error = JS_GetException(ctx);
53645
+ } else {
53646
+ error = suppressed;
53647
+ }
53648
+ } else {
53649
+ error = new_error;
53650
+ has_error = true;
53651
+ }
53652
+ } else {
53653
+ JS_FreeValue(ctx, ret);
53654
+ }
53655
+ }
53656
+ ds->resource_count = 0;
53657
+ if (has_error) {
53658
+ JS_Throw(ctx, error);
53659
+ return -1;
53660
+ }
53661
+ return 0;
53662
+ }
53663
+
53664
+ static void js_disposable_stack_clear(JSRuntime *rt, JSDisposableStack *ds)
53665
+ {
53666
+ int i;
53667
+ for (i = 0; i < ds->resource_count; i++) {
53668
+ JS_FreeValueRT(rt, ds->resources[i].value);
53669
+ JS_FreeValueRT(rt, ds->resources[i].method);
53670
+ }
53671
+ js_free_rt(rt, ds->resources);
53672
+ }
53673
+
53674
+ static int js_disposable_stack_add(JSContext *ctx, JSDisposableStack *ds,
53675
+ JSValueConst value, JSValueConst method,
53676
+ int hint)
53677
+ {
53678
+ if (ds->resource_count >= ds->resource_capacity) {
53679
+ int new_cap = max_int(ds->resource_capacity * 2, 4);
53680
+ JSDisposableResource *new_res;
53681
+ new_res = js_realloc(ctx, ds->resources,
53682
+ new_cap * sizeof(JSDisposableResource));
53683
+ if (!new_res)
53684
+ return -1;
53685
+ ds->resources = new_res;
53686
+ ds->resource_capacity = new_cap;
53687
+ }
53688
+ ds->resources[ds->resource_count].value = js_dup(value);
53689
+ ds->resources[ds->resource_count].method = js_dup(method);
53690
+ ds->resources[ds->resource_count].hint = hint;
53691
+ ds->resource_count++;
53692
+ return 0;
53693
+ }
53694
+
53695
+ static JSValue js_sync_dispose_wrapper(JSContext *ctx, JSValueConst this_val,
53696
+ int argc, JSValueConst *argv,
53697
+ int magic, JSValueConst *func_data)
53698
+ {
53699
+ JSValueConst method = func_data[0];
53700
+ JSValue ret = JS_Call(ctx, method, this_val, 0, NULL);
53701
+ if (JS_IsException(ret))
53702
+ return JS_EXCEPTION;
53703
+ JS_FreeValue(ctx, ret);
53704
+ return JS_UNDEFINED;
53705
+ }
53706
+
53707
+ static JSValue js_get_dispose_method(JSContext *ctx, JSValueConst value, int hint)
53708
+ {
53709
+ JSValue method;
53710
+
53711
+ if (hint == 1) {
53712
+ /* async: try Symbol.asyncDispose first */
53713
+ method = JS_GetProperty(ctx, value, JS_ATOM_Symbol_asyncDispose);
53714
+ if (JS_IsException(method))
53715
+ return JS_EXCEPTION;
53716
+ if (!JS_IsUndefined(method) && !JS_IsNull(method)) {
53717
+ if (!JS_IsFunction(ctx, method)) {
53718
+ JS_FreeValue(ctx, method);
53719
+ return JS_ThrowTypeError(ctx, "property is not a function");
53720
+ }
53721
+ return method;
53722
+ }
53723
+ JS_FreeValue(ctx, method);
53724
+ /* Fall back to Symbol.dispose, but wrap it so its return value is
53725
+ NOT awaited (per spec GetDisposeMethod). */
53726
+ method = JS_GetProperty(ctx, value, JS_ATOM_Symbol_dispose);
53727
+ if (JS_IsException(method))
53728
+ return JS_EXCEPTION;
53729
+ if (JS_IsUndefined(method) || JS_IsNull(method))
53730
+ return JS_ThrowTypeError(ctx, "property is not a function");
53731
+ if (!JS_IsFunction(ctx, method)) {
53732
+ JS_FreeValue(ctx, method);
53733
+ return JS_ThrowTypeError(ctx, "property is not a function");
53734
+ }
53735
+
53736
+ {
53737
+ JSValue data[1], wrapped;
53738
+ data[0] = method;
53739
+ wrapped = JS_NewCFunctionData(ctx, js_sync_dispose_wrapper, 0, 0,
53740
+ 1, vc(data));
53741
+ JS_FreeValue(ctx, method);
53742
+ return wrapped;
53743
+ }
53744
+ }
53745
+
53746
+ /* sync dispose */
53747
+ method = JS_GetProperty(ctx, value, JS_ATOM_Symbol_dispose);
53748
+ if (JS_IsException(method))
53749
+ return JS_EXCEPTION;
53750
+ if (JS_IsUndefined(method) || JS_IsNull(method))
53751
+ return JS_ThrowTypeError(ctx, "property is not a function");
53752
+ if (!JS_IsFunction(ctx, method)) {
53753
+ JS_FreeValue(ctx, method);
53754
+ return JS_ThrowTypeError(ctx, "property is not a function");
53755
+ }
53756
+ return method;
53757
+ }
53758
+
53759
+ static JSValue js_disposable_stack_constructor(JSContext *ctx,
53760
+ JSValueConst new_target,
53761
+ int argc, JSValueConst *argv,
53762
+ int class_id)
53763
+ {
53764
+ JSDisposableStack *s;
53765
+ JSValue obj;
53766
+
53767
+ if (JS_IsUndefined(new_target))
53768
+ return JS_ThrowTypeError(ctx, "Constructor requires 'new'");
53769
+ obj = js_create_from_ctor(ctx, new_target, class_id);
53770
+ if (JS_IsException(obj))
53771
+ return JS_EXCEPTION;
53772
+ s = js_mallocz(ctx, sizeof(*s));
53773
+ if (!s) {
53774
+ JS_FreeValue(ctx, obj);
53775
+ return JS_EXCEPTION;
53776
+ }
53777
+ JS_SetOpaqueInternal(obj, s);
53778
+ return obj;
53779
+ }
53780
+
53781
+ static void js_disposable_stack_finalizer(JSRuntime *rt, JSValueConst val)
53782
+ {
53783
+ JSObject *p;
53784
+ JSDisposableStack *s;
53785
+
53786
+ p = JS_VALUE_GET_OBJ(val);
53787
+ s = p->u.opaque;
53788
+ if (s) {
53789
+ js_disposable_stack_clear(rt, s);
53790
+ js_free_rt(rt, s);
53791
+ }
53792
+ }
53793
+
53794
+ static void js_disposable_stack_mark(JSRuntime *rt, JSValueConst val,
53795
+ JS_MarkFunc *mark_func)
53796
+ {
53797
+ JSObject *p;
53798
+ JSDisposableStack *s;
53799
+ int i;
53800
+
53801
+ p = JS_VALUE_GET_OBJ(val);
53802
+ s = p->u.opaque;
53803
+ if (s) {
53804
+ for (i = 0; i < s->resource_count; i++) {
53805
+ JS_MarkValue(rt, s->resources[i].value, mark_func);
53806
+ JS_MarkValue(rt, s->resources[i].method, mark_func);
53807
+ }
53808
+ }
53809
+ }
53810
+
53811
+ static JSDisposableStack *js_disposable_stack_get(JSContext *ctx,
53812
+ JSValueConst this_val,
53813
+ int class_id)
53814
+ {
53815
+ JSDisposableStack *s;
53816
+ s = JS_GetOpaque2(ctx, this_val, class_id);
53817
+ if (!s)
53818
+ return NULL;
53819
+ if (s->disposed) {
53820
+ JS_ThrowReferenceError(ctx, "DisposableStack has been disposed");
53821
+ return NULL;
53822
+ }
53823
+ return s;
53824
+ }
53825
+
53826
+ static JSValue js_disposable_stack_use(JSContext *ctx, JSValueConst this_val,
53827
+ int argc, JSValueConst *argv, int class_id)
53828
+ {
53829
+ JSDisposableStack *s;
53830
+ JSValueConst value;
53831
+ JSValue method;
53832
+ int hint = (class_id == JS_CLASS_ASYNC_DISPOSABLE_STACK) ? 1 : 0;
53833
+
53834
+ s = js_disposable_stack_get(ctx, this_val, class_id);
53835
+ if (!s)
53836
+ return JS_EXCEPTION;
53837
+ value = argv[0];
53838
+ if (JS_IsNull(value) || JS_IsUndefined(value)) {
53839
+ /* For async stacks, a null/undefined resource still needs a record
53840
+ so disposeAsync performs the required Await(undefined). */
53841
+ if (class_id == JS_CLASS_ASYNC_DISPOSABLE_STACK) {
53842
+ if (js_disposable_stack_add(ctx, s, JS_UNDEFINED, JS_UNDEFINED,
53843
+ JS_DISPOSE_HINT_SYNC) < 0)
53844
+ return JS_EXCEPTION;
53845
+ }
53846
+ return js_dup(value);
53847
+ }
53848
+ if (!JS_IsObject(value))
53849
+ return JS_ThrowTypeError(ctx, "not an object");
53850
+ method = js_get_dispose_method(ctx, value, hint);
53851
+ if (JS_IsException(method))
53852
+ return JS_EXCEPTION;
53853
+ if (js_disposable_stack_add(ctx, s, value, method, JS_DISPOSE_HINT_SYNC) < 0) {
53854
+ JS_FreeValue(ctx, method);
53855
+ return JS_EXCEPTION;
53856
+ }
53857
+ JS_FreeValue(ctx, method);
53858
+ return js_dup(value);
53859
+ }
53860
+
53861
+ static JSValue js_disposable_stack_adopt(JSContext *ctx, JSValueConst this_val,
53862
+ int argc, JSValueConst *argv, int class_id)
53863
+ {
53864
+ JSDisposableStack *s;
53865
+ JSValueConst value, on_dispose;
53866
+
53867
+ s = js_disposable_stack_get(ctx, this_val, class_id);
53868
+ if (!s)
53869
+ return JS_EXCEPTION;
53870
+ value = argv[0];
53871
+ on_dispose = argv[1];
53872
+ if (!JS_IsFunction(ctx, on_dispose))
53873
+ return JS_ThrowTypeError(ctx, "not a function");
53874
+ if (js_disposable_stack_add(ctx, s, value, on_dispose, JS_DISPOSE_HINT_ADOPT) < 0)
53875
+ return JS_EXCEPTION;
53876
+ return js_dup(value);
53877
+ }
53878
+
53879
+ static JSValue js_disposable_stack_defer(JSContext *ctx, JSValueConst this_val,
53880
+ int argc, JSValueConst *argv, int class_id)
53881
+ {
53882
+ JSDisposableStack *s;
53883
+ JSValueConst on_dispose;
53884
+
53885
+ s = js_disposable_stack_get(ctx, this_val, class_id);
53886
+ if (!s)
53887
+ return JS_EXCEPTION;
53888
+ on_dispose = argv[0];
53889
+ if (!JS_IsFunction(ctx, on_dispose))
53890
+ return JS_ThrowTypeError(ctx, "not a function");
53891
+ if (js_disposable_stack_add(ctx, s, JS_UNDEFINED, on_dispose, JS_DISPOSE_HINT_DEFER) < 0)
53892
+ return JS_EXCEPTION;
53893
+ return JS_UNDEFINED;
53894
+ }
53895
+
53896
+ /* Simple .then handler that discards its argument and returns undefined;
53897
+ used to normalize the chain's resolved value after all disposals. */
53898
+ static JSValue js_async_dispose_to_undef(JSContext *ctx, JSValueConst this_val,
53899
+ int argc, JSValueConst *argv,
53900
+ int magic, JSValueConst *func_data)
53901
+ {
53902
+ return JS_UNDEFINED;
53903
+ }
53904
+
53905
+ static JSValue js_async_dispose_rethrow(JSContext *ctx, JSValueConst this_val,
53906
+ int argc, JSValueConst *argv,
53907
+ int magic, JSValueConst *func_data)
53908
+ {
53909
+ JSValue prev_err = js_dup(func_data[0]);
53910
+ if (magic == 0) {
53911
+ return JS_Throw(ctx, prev_err);
53912
+ } else {
53913
+ JSValue se = js_new_suppressed_error(ctx, argv[0], prev_err);
53914
+ JS_FreeValue(ctx, prev_err);
53915
+ if (JS_IsException(se))
53916
+ return JS_EXCEPTION;
53917
+ return JS_Throw(ctx, se);
53918
+ }
53919
+ }
53920
+
53921
+ static JSValue js_async_dispose_step(JSContext *ctx, JSValueConst this_val,
53922
+ int argc, JSValueConst *argv,
53923
+ int magic, JSValueConst *func_data)
53924
+ {
53925
+ JSValueConst value = func_data[0];
53926
+ JSValueConst method = func_data[1];
53927
+ int hint = JS_VALUE_GET_INT(func_data[2]);
53928
+ bool has_prev_err = (magic == 1);
53929
+ JSValue ret;
53930
+
53931
+ if (JS_IsUndefined(method)) {
53932
+ /* null/undefined resource on async stack: Await(undefined) */
53933
+ if (has_prev_err)
53934
+ return JS_Throw(ctx, js_dup(argv[0]));
53935
+ return JS_UNDEFINED;
53936
+ }
53937
+
53938
+ switch (hint) {
53939
+ case JS_DISPOSE_HINT_ADOPT:
53940
+ ret = JS_Call(ctx, method, JS_UNDEFINED, 1, &value);
53941
+ break;
53942
+ case JS_DISPOSE_HINT_DEFER:
53943
+ ret = JS_Call(ctx, method, JS_UNDEFINED, 0, NULL);
53944
+ break;
53945
+ default:
53946
+ ret = JS_Call(ctx, method, value, 0, NULL);
53947
+ break;
53948
+ }
53949
+
53950
+ if (JS_IsException(ret)) {
53951
+ JSValue new_err = JS_GetException(ctx);
53952
+ if (has_prev_err) {
53953
+ JSValue se = js_new_suppressed_error(ctx, new_err, argv[0]);
53954
+ JS_FreeValue(ctx, new_err);
53955
+ if (JS_IsException(se))
53956
+ return JS_EXCEPTION;
53957
+ return JS_Throw(ctx, se);
53958
+ }
53959
+ return JS_Throw(ctx, new_err);
53960
+ }
53961
+
53962
+ if (!has_prev_err) {
53963
+ /* Propagate method result; next .then awaits it */
53964
+ return ret;
53965
+ }
53966
+
53967
+ /* Await ret, then rethrow the stored error (possibly wrapped) */
53968
+ {
53969
+ JSValueConst prev_err = argv[0];
53970
+ JSValue ret_promise, resolve_fn, reject_fn, then_args[2], result;
53971
+ ret_promise = js_promise_resolve(ctx, ctx->promise_ctor, 1, vc(&ret), 0);
53972
+ JS_FreeValue(ctx, ret);
53973
+ if (JS_IsException(ret_promise))
53974
+ return JS_EXCEPTION;
53975
+ resolve_fn = JS_NewCFunctionData(ctx, js_async_dispose_rethrow, 0, 0,
53976
+ 1, &prev_err);
53977
+ reject_fn = JS_NewCFunctionData(ctx, js_async_dispose_rethrow, 0, 1,
53978
+ 1, &prev_err);
53979
+ then_args[0] = resolve_fn;
53980
+ then_args[1] = reject_fn;
53981
+ result = JS_Invoke(ctx, ret_promise, JS_ATOM_then, 2, vc(then_args));
53982
+ JS_FreeValue(ctx, resolve_fn);
53983
+ JS_FreeValue(ctx, reject_fn);
53984
+ JS_FreeValue(ctx, ret_promise);
53985
+ return result;
53986
+ }
53987
+ }
53988
+
53989
+ static JSValue js_disposable_stack_dispose(JSContext *ctx,
53990
+ JSValueConst this_val,
53991
+ int argc,
53992
+ JSValueConst *argv,
53993
+ int class_id)
53994
+ {
53995
+ JSDisposableStack *s;
53996
+
53997
+ s = JS_GetOpaque2(ctx, this_val, class_id);
53998
+ if (!s) {
53999
+ if (class_id == JS_CLASS_ASYNC_DISPOSABLE_STACK) {
54000
+ JSValue exc = JS_GetException(ctx);
54001
+ JSValue p = js_promise_resolve(ctx, ctx->promise_ctor, 1, vc(&exc),
54002
+ /*is_reject*/1);
54003
+ JS_FreeValue(ctx, exc);
54004
+ return p;
54005
+ }
54006
+ return JS_EXCEPTION;
54007
+ }
54008
+ if (s->disposed) {
54009
+ if (class_id == JS_CLASS_ASYNC_DISPOSABLE_STACK) {
54010
+ JSValue undef = JS_UNDEFINED;
54011
+ return js_promise_resolve(ctx, ctx->promise_ctor, 1, vc(&undef),
54012
+ /*is_reject*/0);
54013
+ }
54014
+ return JS_UNDEFINED;
54015
+ }
54016
+ if (class_id == JS_CLASS_ASYNC_DISPOSABLE_STACK) {
54017
+ /* Per spec DisposeResources: iterate resources in LIFO order and
54018
+ for each do Call + Await. The first Call happens synchronously
54019
+ inside disposeAsync(); subsequent Calls fire in microtasks via a
54020
+ Promise.then() chain so that each call sees the previous
54021
+ dispose's promise already settled. */
54022
+ int i, count = s->resource_count;
54023
+ JSValue chain, undef, ret;
54024
+
54025
+ s->disposed = true;
54026
+
54027
+ /* First (top-of-stack) resource: synchronous Call. */
54028
+ undef = JS_UNDEFINED;
54029
+ i = count - 1;
54030
+ if (i < 0) {
54031
+ chain = js_promise_resolve(ctx, ctx->promise_ctor, 1, vc(&undef),
54032
+ /*is_reject*/0);
54033
+ if (JS_IsException(chain))
54034
+ goto async_dispose_fail;
54035
+ } else {
54036
+ JSDisposableResource *res = &s->resources[i];
54037
+ if (JS_IsUndefined(res->method)) {
54038
+ /* null/undefined resource: Await(undefined) */
54039
+ chain = js_promise_resolve(ctx, ctx->promise_ctor, 1,
54040
+ vc(&undef), /*is_reject*/0);
54041
+ } else {
54042
+ switch (res->hint) {
54043
+ case JS_DISPOSE_HINT_ADOPT:
54044
+ ret = JS_Call(ctx, res->method, JS_UNDEFINED, 1,
54045
+ vc(&res->value));
54046
+ break;
54047
+ case JS_DISPOSE_HINT_DEFER:
54048
+ ret = JS_Call(ctx, res->method, JS_UNDEFINED, 0, NULL);
54049
+ break;
54050
+ default:
54051
+ ret = JS_Call(ctx, res->method, res->value, 0, NULL);
54052
+ break;
54053
+ }
54054
+ if (JS_IsException(ret)) {
54055
+ JSValue err = JS_GetException(ctx);
54056
+ chain = js_promise_resolve(ctx, ctx->promise_ctor, 1,
54057
+ vc(&err), /*is_reject*/1);
54058
+ JS_FreeValue(ctx, err);
54059
+ } else {
54060
+ chain = js_promise_resolve(ctx, ctx->promise_ctor, 1,
54061
+ vc(&ret), /*is_reject*/0);
54062
+ JS_FreeValue(ctx, ret);
54063
+ }
54064
+ }
54065
+ JS_FreeValue(ctx, res->value);
54066
+ JS_FreeValue(ctx, res->method);
54067
+ res->value = JS_UNDEFINED;
54068
+ res->method = JS_UNDEFINED;
54069
+ if (JS_IsException(chain)) {
54070
+ i--;
54071
+ goto async_dispose_fail;
54072
+ }
54073
+ i--;
54074
+ }
54075
+
54076
+ /* Remaining resources: chain lazy steps. */
54077
+ for (; i >= 0; i--) {
54078
+ JSDisposableResource *res = &s->resources[i];
54079
+ JSValueConst data[3];
54080
+ JSValue hint_val, resolve_fn, reject_fn, then_args[2], new_chain;
54081
+
54082
+ hint_val = JS_NewInt32(ctx, res->hint);
54083
+ data[0] = res->value;
54084
+ data[1] = res->method;
54085
+ data[2] = hint_val;
54086
+ resolve_fn = JS_NewCFunctionData(ctx, js_async_dispose_step, 0, 0,
54087
+ 3, data);
54088
+ reject_fn = JS_NewCFunctionData(ctx, js_async_dispose_step, 0, 1,
54089
+ 3, data);
54090
+ JS_FreeValue(ctx, hint_val);
54091
+ JS_FreeValue(ctx, res->value);
54092
+ JS_FreeValue(ctx, res->method);
54093
+ res->value = JS_UNDEFINED;
54094
+ res->method = JS_UNDEFINED;
54095
+ if (JS_IsException(resolve_fn) || JS_IsException(reject_fn)) {
54096
+ JS_FreeValue(ctx, resolve_fn);
54097
+ JS_FreeValue(ctx, reject_fn);
54098
+ JS_FreeValue(ctx, chain);
54099
+ chain = JS_EXCEPTION;
54100
+ goto async_dispose_fail;
54101
+ }
54102
+ then_args[0] = resolve_fn;
54103
+ then_args[1] = reject_fn;
54104
+ new_chain = JS_Invoke(ctx, chain, JS_ATOM_then, 2, vc(then_args));
54105
+ JS_FreeValue(ctx, resolve_fn);
54106
+ JS_FreeValue(ctx, reject_fn);
54107
+ JS_FreeValue(ctx, chain);
54108
+ if (JS_IsException(new_chain)) {
54109
+ chain = JS_EXCEPTION;
54110
+ goto async_dispose_fail;
54111
+ }
54112
+ chain = new_chain;
54113
+ }
54114
+ s->resource_count = 0;
54115
+
54116
+ if (count > 0) {
54117
+ JSValue undef_fn, then_args[1], new_chain;
54118
+ undef_fn = JS_NewCFunctionData(ctx, js_async_dispose_to_undef,
54119
+ 0, 0, 0, NULL);
54120
+ if (JS_IsException(undef_fn)) {
54121
+ JS_FreeValue(ctx, chain);
54122
+ return JS_EXCEPTION;
54123
+ }
54124
+ then_args[0] = undef_fn;
54125
+ new_chain = JS_Invoke(ctx, chain, JS_ATOM_then, 1, vc(then_args));
54126
+ JS_FreeValue(ctx, undef_fn);
54127
+ JS_FreeValue(ctx, chain);
54128
+ return new_chain;
54129
+ }
54130
+ return chain;
54131
+
54132
+ async_dispose_fail:
54133
+ for (; i >= 0; i--) {
54134
+ JSDisposableResource *res = &s->resources[i];
54135
+ JS_FreeValue(ctx, res->value);
54136
+ JS_FreeValue(ctx, res->method);
54137
+ res->value = JS_UNDEFINED;
54138
+ res->method = JS_UNDEFINED;
54139
+ }
54140
+ s->resource_count = 0;
54141
+ return JS_EXCEPTION;
54142
+ }
54143
+ if (js_dispose_resources(ctx, s, JS_UNDEFINED) < 0)
54144
+ return JS_EXCEPTION;
54145
+ return JS_UNDEFINED;
54146
+ }
54147
+
54148
+ static JSValue js_disposable_stack_move(JSContext *ctx, JSValueConst this_val,
54149
+ int argc, JSValueConst *argv, int class_id)
54150
+ {
54151
+ JSDisposableStack *s, *ns;
54152
+ JSValue new_obj;
54153
+
54154
+ s = js_disposable_stack_get(ctx, this_val, class_id);
54155
+ if (!s)
54156
+ return JS_EXCEPTION;
54157
+ /* Use the intrinsic prototype directly so tampering with the global
54158
+ binding or subclassing cannot redirect move(). */
54159
+ new_obj = JS_NewObjectProtoClass(ctx, ctx->class_proto[class_id],
54160
+ class_id);
54161
+ if (JS_IsException(new_obj))
54162
+ return JS_EXCEPTION;
54163
+ ns = js_mallocz(ctx, sizeof(*ns));
54164
+ if (!ns) {
54165
+ JS_FreeValue(ctx, new_obj);
54166
+ return JS_EXCEPTION;
54167
+ }
54168
+ JS_SetOpaqueInternal(new_obj, ns);
54169
+ /* Transfer resources to new stack */
54170
+ ns->resources = s->resources;
54171
+ ns->resource_count = s->resource_count;
54172
+ ns->resource_capacity = s->resource_capacity;
54173
+ /* Reset original stack */
54174
+ s->resources = NULL;
54175
+ s->resource_count = 0;
54176
+ s->resource_capacity = 0;
54177
+ s->disposed = true;
54178
+ return new_obj;
54179
+ }
54180
+
54181
+ static JSValue js_disposable_stack_get_disposed(JSContext *ctx,
54182
+ JSValueConst this_val, int class_id)
54183
+ {
54184
+ JSDisposableStack *s;
54185
+
54186
+ s = JS_GetOpaque2(ctx, this_val, class_id);
54187
+ if (!s)
54188
+ return JS_EXCEPTION;
54189
+ return js_bool(s->disposed);
54190
+ }
54191
+
54192
+ static const JSCFunctionListEntry js_disposable_stack_proto_funcs[] = {
54193
+ JS_CFUNC_MAGIC_DEF("adopt", 2, js_disposable_stack_adopt, JS_CLASS_DISPOSABLE_STACK ),
54194
+ JS_CFUNC_MAGIC_DEF("defer", 1, js_disposable_stack_defer, JS_CLASS_DISPOSABLE_STACK ),
54195
+ JS_CFUNC_MAGIC_DEF("dispose", 0, js_disposable_stack_dispose, JS_CLASS_DISPOSABLE_STACK ),
54196
+ JS_CFUNC_MAGIC_DEF("move", 0, js_disposable_stack_move, JS_CLASS_DISPOSABLE_STACK ),
54197
+ JS_CFUNC_MAGIC_DEF("use", 1, js_disposable_stack_use, JS_CLASS_DISPOSABLE_STACK ),
54198
+ JS_CGETSET_MAGIC_DEF("disposed", js_disposable_stack_get_disposed, NULL, JS_CLASS_DISPOSABLE_STACK ),
54199
+ JS_ALIAS_DEF("[Symbol.dispose]", "dispose" ),
54200
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "DisposableStack", JS_PROP_CONFIGURABLE ),
54201
+ };
54202
+
54203
+ static const JSCFunctionListEntry js_async_disposable_stack_proto_funcs[] = {
54204
+ JS_CFUNC_MAGIC_DEF("adopt", 2, js_disposable_stack_adopt, JS_CLASS_ASYNC_DISPOSABLE_STACK ),
54205
+ JS_CFUNC_MAGIC_DEF("defer", 1, js_disposable_stack_defer, JS_CLASS_ASYNC_DISPOSABLE_STACK ),
54206
+ JS_CFUNC_MAGIC_DEF("disposeAsync", 0, js_disposable_stack_dispose, JS_CLASS_ASYNC_DISPOSABLE_STACK ),
54207
+ JS_CFUNC_MAGIC_DEF("move", 0, js_disposable_stack_move, JS_CLASS_ASYNC_DISPOSABLE_STACK ),
54208
+ JS_CFUNC_MAGIC_DEF("use", 1, js_disposable_stack_use, JS_CLASS_ASYNC_DISPOSABLE_STACK ),
54209
+ JS_CGETSET_MAGIC_DEF("disposed", js_disposable_stack_get_disposed, NULL, JS_CLASS_ASYNC_DISPOSABLE_STACK ),
54210
+ JS_ALIAS_DEF("[Symbol.asyncDispose]", "disposeAsync" ),
54211
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncDisposableStack", JS_PROP_CONFIGURABLE),
54212
+ };
54213
+
52732
54214
  /* Promise */
52733
54215
 
52734
54216
  typedef struct JSPromiseData {
@@ -53877,6 +55359,7 @@ static JSValue js_async_from_sync_iterator_unwrap_func_create(JSContext *ctx,
53877
55359
 
53878
55360
  static const JSCFunctionListEntry js_async_iterator_proto_funcs[] = {
53879
55361
  JS_CFUNC_DEF("[Symbol.asyncIterator]", 0, js_iterator_proto_iterator ),
55362
+ JS_CFUNC_DEF("[Symbol.asyncDispose]", 0, js_async_iterator_proto_dispose ),
53880
55363
  };
53881
55364
 
53882
55365
  /* AsyncFromSyncIteratorPrototype */
@@ -54075,6 +55558,7 @@ static JSClassShortDef const js_async_class_def[] = {
54075
55558
  { JS_ATOM_empty_string, js_async_from_sync_iterator_finalizer, js_async_from_sync_iterator_mark }, /* JS_CLASS_ASYNC_FROM_SYNC_ITERATOR */
54076
55559
  { JS_ATOM_AsyncGeneratorFunction, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_ASYNC_GENERATOR_FUNCTION */
54077
55560
  { JS_ATOM_AsyncGenerator, js_async_generator_finalizer, js_async_generator_mark }, /* JS_CLASS_ASYNC_GENERATOR */
55561
+ { JS_ATOM_AsyncDisposableStack, js_disposable_stack_finalizer, js_disposable_stack_mark }, /* JS_CLASS_ASYNC_DISPOSABLE_STACK */
54078
55562
  };
54079
55563
 
54080
55564
  int JS_AddIntrinsicPromise(JSContext *ctx)
@@ -54154,9 +55638,21 @@ int JS_AddIntrinsicPromise(JSContext *ctx)
54154
55638
  return -1;
54155
55639
  JS_FreeValue(ctx, obj1);
54156
55640
 
54157
- return JS_SetConstructor2(ctx, ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION],
54158
- ctx->class_proto[JS_CLASS_ASYNC_GENERATOR],
54159
- JS_PROP_CONFIGURABLE, JS_PROP_CONFIGURABLE);
55641
+ if (JS_SetConstructor2(ctx, ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION],
55642
+ ctx->class_proto[JS_CLASS_ASYNC_GENERATOR],
55643
+ JS_PROP_CONFIGURABLE, JS_PROP_CONFIGURABLE))
55644
+ return -1;
55645
+
55646
+ /* AsyncDisposableStack */
55647
+ ctx->class_proto[JS_CLASS_ASYNC_DISPOSABLE_STACK] = JS_NewObject(ctx);
55648
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ASYNC_DISPOSABLE_STACK],
55649
+ js_async_disposable_stack_proto_funcs,
55650
+ countof(js_async_disposable_stack_proto_funcs));
55651
+ JS_NewGlobalCConstructorMagic(ctx, "AsyncDisposableStack",
55652
+ js_disposable_stack_constructor, 0,
55653
+ ctx->class_proto[JS_CLASS_ASYNC_DISPOSABLE_STACK],
55654
+ JS_CLASS_ASYNC_DISPOSABLE_STACK);
55655
+ return 0;
54160
55656
  }
54161
55657
 
54162
55658
  /* URI handling */
@@ -55830,6 +57326,7 @@ static const char * const native_error_name[JS_NATIVE_ERROR_COUNT] = {
55830
57326
  "EvalError", "RangeError", "ReferenceError",
55831
57327
  "SyntaxError", "TypeError", "URIError",
55832
57328
  "InternalError", "AggregateError",
57329
+ "SuppressedError",
55833
57330
  };
55834
57331
 
55835
57332
  /* Minimum amount of objects to be able to compile code and display
@@ -55978,7 +57475,16 @@ int JS_AddIntrinsicBaseObjects(JSContext *ctx)
55978
57475
  for(int i = 0; i < JS_NATIVE_ERROR_COUNT; i++) {
55979
57476
  JSValue func_obj;
55980
57477
  int n_args;
55981
- n_args = 1 + (i == JS_AGGREGATE_ERROR);
57478
+ switch (i) {
57479
+ case JS_AGGREGATE_ERROR:
57480
+ n_args = 2;
57481
+ break;
57482
+ case JS_SUPPRESSED_ERROR:
57483
+ n_args = 3;
57484
+ break;
57485
+ default:
57486
+ n_args = 1;
57487
+ }
55982
57488
  func_obj = JS_NewCFunction3(ctx, ft.generic,
55983
57489
  native_error_name[i], n_args,
55984
57490
  JS_CFUNC_constructor_or_func_magic, i,
@@ -56176,6 +57682,16 @@ int JS_AddIntrinsicBaseObjects(JSContext *ctx)
56176
57682
  JS_PROP_CONFIGURABLE, JS_PROP_CONFIGURABLE))
56177
57683
  return -1;
56178
57684
 
57685
+ /* explicit resource management */
57686
+ ctx->class_proto[JS_CLASS_DISPOSABLE_STACK] = JS_NewObject(ctx);
57687
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_DISPOSABLE_STACK],
57688
+ js_disposable_stack_proto_funcs,
57689
+ countof(js_disposable_stack_proto_funcs));
57690
+ JS_NewGlobalCConstructorMagic(ctx, "DisposableStack",
57691
+ js_disposable_stack_constructor, 0,
57692
+ ctx->class_proto[JS_CLASS_DISPOSABLE_STACK],
57693
+ JS_CLASS_DISPOSABLE_STACK);
57694
+
56179
57695
  /* global properties */
56180
57696
  ctx->eval_obj = JS_GetProperty(ctx, ctx->global_obj, JS_ATOM_eval);
56181
57697
  if (JS_IsException(ctx->eval_obj))
@@ -59885,6 +61401,8 @@ static int JS_AddIntrinsicAtomics(JSContext *ctx)
59885
61401
 
59886
61402
  #endif /* CONFIG_ATOMICS */
59887
61403
 
61404
+ static int js_uint8array_funcs_init(JSContext *ctx);
61405
+
59888
61406
  int JS_AddIntrinsicTypedArrays(JSContext *ctx)
59889
61407
  {
59890
61408
  JSValue typed_array_base_func, typed_array_base_proto, obj;
@@ -59959,6 +61477,10 @@ int JS_AddIntrinsicTypedArrays(JSContext *ctx)
59959
61477
  }
59960
61478
  JS_FreeValue(ctx, typed_array_base_func);
59961
61479
 
61480
+ /* Uint8Array base64/hex methods */
61481
+ if (js_uint8array_funcs_init(ctx))
61482
+ return -1;
61483
+
59962
61484
  /* DataView */
59963
61485
  obj = JS_NewCConstructor(ctx, JS_CLASS_DATAVIEW, "DataView",
59964
61486
  js_dataview_constructor, 1, JS_CFUNC_constructor, 0,
@@ -60829,6 +62351,993 @@ int JS_AddIntrinsicDOMException(JSContext *ctx)
60829
62351
  ctx->class_proto[JS_CLASS_DOM_EXCEPTION] = proto;
60830
62352
  return 0;
60831
62353
  }
62354
+ /* base64 */
62355
+
62356
+ static const unsigned char b64_enc[64] = {
62357
+ 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
62358
+ 'Q','R','S','T','U','V','W','X','Y','Z',
62359
+ 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p',
62360
+ 'q','r','s','t','u','v','w','x','y','z',
62361
+ '0','1','2','3','4','5','6','7','8','9',
62362
+ '+','/'
62363
+ };
62364
+
62365
+ static const unsigned char b64url_enc[64] = {
62366
+ 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
62367
+ 'Q','R','S','T','U','V','W','X','Y','Z',
62368
+ 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p',
62369
+ 'q','r','s','t','u','v','w','x','y','z',
62370
+ '0','1','2','3','4','5','6','7','8','9',
62371
+ '-','_'
62372
+ };
62373
+
62374
+ enum { K_VAL = 1u, K_WS = 2u };
62375
+
62376
+ static const uint8_t b64_val[256] = {
62377
+ ['A']=0, ['B']=1, ['C']=2, ['D']=3, ['E']=4, ['F']=5, ['G']=6, ['H']=7,
62378
+ ['I']=8, ['J']=9, ['K']=10,['L']=11,['M']=12,['N']=13,['O']=14,['P']=15,
62379
+ ['Q']=16,['R']=17,['S']=18,['T']=19,['U']=20,['V']=21,['W']=22,['X']=23,['Y']=24,['Z']=25,
62380
+ ['a']=26,['b']=27,['c']=28,['d']=29,['e']=30,['f']=31,['g']=32,['h']=33,
62381
+ ['i']=34,['j']=35,['k']=36,['l']=37,['m']=38,['n']=39,['o']=40,['p']=41,
62382
+ ['q']=42,['r']=43,['s']=44,['t']=45,['u']=46,['v']=47,['w']=48,['x']=49,['y']=50,['z']=51,
62383
+ ['0']=52,['1']=53,['2']=54,['3']=55,['4']=56,['5']=57,['6']=58,['7']=59,['8']=60,['9']=61,
62384
+ ['+']=62, ['/']=63,
62385
+ ['-']=62, ['_']=63,
62386
+ };
62387
+
62388
+ static const char b64_flags[256] = {
62389
+ [' ']=K_WS, ['\t']=K_WS, ['\n']=K_WS, ['\f']=K_WS, ['\r']=K_WS,
62390
+ ['A']=K_VAL,['B']=K_VAL,['C']=K_VAL,['D']=K_VAL,['E']=K_VAL,['F']=K_VAL,['G']=K_VAL,['H']=K_VAL,
62391
+ ['I']=K_VAL,['J']=K_VAL,['K']=K_VAL,['L']=K_VAL,['M']=K_VAL,['N']=K_VAL,['O']=K_VAL,['P']=K_VAL,
62392
+ ['Q']=K_VAL,['R']=K_VAL,['S']=K_VAL,['T']=K_VAL,['U']=K_VAL,['V']=K_VAL,['W']=K_VAL,['X']=K_VAL,
62393
+ ['Y']=K_VAL,['Z']=K_VAL,
62394
+ ['a']=K_VAL,['b']=K_VAL,['c']=K_VAL,['d']=K_VAL,['e']=K_VAL,['f']=K_VAL,['g']=K_VAL,['h']=K_VAL,
62395
+ ['i']=K_VAL,['j']=K_VAL,['k']=K_VAL,['l']=K_VAL,['m']=K_VAL,['n']=K_VAL,['o']=K_VAL,['p']=K_VAL,
62396
+ ['q']=K_VAL,['r']=K_VAL,['s']=K_VAL,['t']=K_VAL,['u']=K_VAL,['v']=K_VAL,['w']=K_VAL,['x']=K_VAL,
62397
+ ['y']=K_VAL,['z']=K_VAL,
62398
+ ['0']=K_VAL,['1']=K_VAL,['2']=K_VAL,['3']=K_VAL,['4']=K_VAL,['5']=K_VAL,['6']=K_VAL,['7']=K_VAL,
62399
+ ['8']=K_VAL,['9']=K_VAL,
62400
+ ['+']=K_VAL,['/']=K_VAL,
62401
+ };
62402
+
62403
+ static const char b64_flags_url[256] = {
62404
+ [' ']=K_WS, ['\t']=K_WS, ['\n']=K_WS, ['\f']=K_WS, ['\r']=K_WS,
62405
+ ['A']=K_VAL,['B']=K_VAL,['C']=K_VAL,['D']=K_VAL,['E']=K_VAL,['F']=K_VAL,['G']=K_VAL,['H']=K_VAL,
62406
+ ['I']=K_VAL,['J']=K_VAL,['K']=K_VAL,['L']=K_VAL,['M']=K_VAL,['N']=K_VAL,['O']=K_VAL,['P']=K_VAL,
62407
+ ['Q']=K_VAL,['R']=K_VAL,['S']=K_VAL,['T']=K_VAL,['U']=K_VAL,['V']=K_VAL,['W']=K_VAL,['X']=K_VAL,
62408
+ ['Y']=K_VAL,['Z']=K_VAL,
62409
+ ['a']=K_VAL,['b']=K_VAL,['c']=K_VAL,['d']=K_VAL,['e']=K_VAL,['f']=K_VAL,['g']=K_VAL,['h']=K_VAL,
62410
+ ['i']=K_VAL,['j']=K_VAL,['k']=K_VAL,['l']=K_VAL,['m']=K_VAL,['n']=K_VAL,['o']=K_VAL,['p']=K_VAL,
62411
+ ['q']=K_VAL,['r']=K_VAL,['s']=K_VAL,['t']=K_VAL,['u']=K_VAL,['v']=K_VAL,['w']=K_VAL,['x']=K_VAL,
62412
+ ['y']=K_VAL,['z']=K_VAL,
62413
+ ['0']=K_VAL,['1']=K_VAL,['2']=K_VAL,['3']=K_VAL,['4']=K_VAL,['5']=K_VAL,['6']=K_VAL,['7']=K_VAL,
62414
+ ['8']=K_VAL,['9']=K_VAL,
62415
+ ['-']=K_VAL,['_']=K_VAL,
62416
+ };
62417
+
62418
+ static size_t b64_encode(const uint8_t *src, size_t len, char *dst,
62419
+ const unsigned char *alpha)
62420
+ {
62421
+ size_t i = 0, j = 0;
62422
+ size_t main_len = (len / 3) * 3;
62423
+
62424
+ for (; i < main_len; i += 3, j += 4) {
62425
+ uint32_t v = 65536*src[i] + 256*src[i + 1] + src[i + 2];
62426
+ dst[j + 0] = alpha[(v >> 18) & 63];
62427
+ dst[j + 1] = alpha[(v >> 12) & 63];
62428
+ dst[j + 2] = alpha[(v >> 6) & 63];
62429
+ dst[j + 3] = alpha[v & 63];
62430
+ }
62431
+
62432
+ size_t rem = len - i;
62433
+ if (rem == 1) {
62434
+ uint32_t v = 65536*src[i];
62435
+ dst[j++] = alpha[(v >> 18) & 63];
62436
+ dst[j++] = alpha[(v >> 12) & 63];
62437
+ dst[j++] = '=';
62438
+ dst[j++] = '=';
62439
+ } else if (rem == 2) {
62440
+ uint32_t v = 65536*src[i] + 256*src[i + 1];
62441
+ dst[j++] = alpha[(v >> 18) & 63];
62442
+ dst[j++] = alpha[(v >> 12) & 63];
62443
+ dst[j++] = alpha[(v >> 6) & 63];
62444
+ dst[j++] = '=';
62445
+ }
62446
+ return j;
62447
+ }
62448
+
62449
+ /* Implements https://infra.spec.whatwg.org/#forgiving-base64-decode */
62450
+ static size_t
62451
+ b64_decode(const char *src, size_t len, uint8_t *dst, int *err)
62452
+ {
62453
+ size_t i, j;
62454
+ uint32_t acc;
62455
+ int seen, pad;
62456
+ unsigned ch;
62457
+
62458
+ acc = 0;
62459
+ seen = 0;
62460
+ for (i = 0, j = 0; i < len; i++) {
62461
+ ch = (unsigned char)src[i];
62462
+ if ((b64_flags[ch] & K_WS))
62463
+ continue;
62464
+ if (!(b64_flags[ch] & K_VAL))
62465
+ break;
62466
+ acc = (acc << 6) | b64_val[ch];
62467
+ seen++;
62468
+ if (seen == 4) {
62469
+ dst[j++] = (acc >> 16) & 0xFF;
62470
+ dst[j++] = (acc >> 8) & 0xFF;
62471
+ dst[j++] = acc & 0xFF;
62472
+ seen = 0;
62473
+ acc = 0;
62474
+ }
62475
+ }
62476
+
62477
+ if (seen != 0) {
62478
+ if (seen == 3) {
62479
+ dst[j++] = (acc >> 10) & 0xFF;
62480
+ dst[j++] = (acc >> 2) & 0xFF;
62481
+ } else if (seen == 2) {
62482
+ dst[j++] = (acc >> 4) & 0xFF;
62483
+ } else {
62484
+ *err = 1;
62485
+ return 0;
62486
+ }
62487
+ for (pad = 0; i < len; i++) {
62488
+ ch = (unsigned char)src[i];
62489
+ if (pad < 2 && ch == '=')
62490
+ pad++;
62491
+ else if (!(b64_flags[ch] & K_WS))
62492
+ break;
62493
+ }
62494
+ if (pad != 0 && seen + pad != 4) {
62495
+ *err = 1;
62496
+ return 0;
62497
+ }
62498
+ }
62499
+
62500
+ *err = i < len;
62501
+ return j;
62502
+ }
62503
+
62504
+ static JSValue js_btoa(JSContext *ctx, JSValueConst this_val,
62505
+ int argc, JSValueConst *argv)
62506
+ {
62507
+ const uint8_t *in8;
62508
+ uint8_t *tmp = NULL;
62509
+ uint8_t *outp;
62510
+ JSValue val, ret = JS_EXCEPTION;
62511
+ JSString *s, *ostr;
62512
+ size_t len, out_len, written;
62513
+
62514
+ val = JS_ToString(ctx, argv[0]);
62515
+ if (unlikely(JS_IsException(val)))
62516
+ return JS_EXCEPTION;
62517
+
62518
+ s = JS_VALUE_GET_STRING(val);
62519
+ len = (size_t)s->len;
62520
+
62521
+ if (likely(!s->is_wide_char)) {
62522
+ in8 = (const uint8_t *)str8(s);
62523
+ } else {
62524
+ const uint16_t *src = str16(s);
62525
+ tmp = js_malloc(ctx, likely(len) ? len : 1);
62526
+ if (unlikely(!tmp))
62527
+ goto fail;
62528
+ for (size_t i = 0; i < len; i++) {
62529
+ uint32_t c = src[i];
62530
+ if (unlikely(c > 0xFF)) {
62531
+ JS_ThrowDOMException(ctx, "InvalidCharacterError",
62532
+ "String contains an invalid character");
62533
+ goto fail;
62534
+ }
62535
+ tmp[i] = (uint8_t)c;
62536
+ }
62537
+ in8 = tmp;
62538
+ }
62539
+
62540
+ if (unlikely(len > (SIZE_MAX - 2) / 3)) {
62541
+ JS_ThrowRangeError(ctx, "input too large");
62542
+ goto fail;
62543
+ }
62544
+ out_len = 4 * ((len + 2) / 3);
62545
+ if (unlikely(out_len > JS_STRING_LEN_MAX)) {
62546
+ JS_ThrowRangeError(ctx, "output too large");
62547
+ goto fail;
62548
+ }
62549
+
62550
+ ostr = js_alloc_string(ctx, out_len, 0);
62551
+ if (unlikely(!ostr))
62552
+ goto fail;
62553
+
62554
+ outp = str8(ostr);
62555
+ written = b64_encode(in8, len, (char *)outp, b64_enc);
62556
+ outp[written] = '\0';
62557
+ ostr->len = out_len;
62558
+ ret = JS_MKPTR(JS_TAG_STRING, ostr);
62559
+ fail:
62560
+ if (tmp)
62561
+ js_free(ctx, tmp);
62562
+ JS_FreeValue(ctx, val);
62563
+ return ret;
62564
+ }
62565
+
62566
+ static JSValue js_atob(JSContext *ctx, JSValueConst this_val,
62567
+ int argc, JSValueConst *argv)
62568
+ {
62569
+ const uint8_t *in;
62570
+ uint8_t *tmp = NULL, *outp;
62571
+ JSValue val, ret = JS_EXCEPTION;
62572
+ JSString *s, *ostr;
62573
+ size_t slen, out_cap, out_len;
62574
+ int err;
62575
+
62576
+ val = JS_ToString(ctx, argv[0]);
62577
+ if (unlikely(JS_IsException(val)))
62578
+ return JS_EXCEPTION;
62579
+
62580
+ s = JS_VALUE_GET_STRING(val);
62581
+ slen = (size_t)s->len;
62582
+
62583
+ if (likely(!s->is_wide_char)) {
62584
+ const uint8_t *p = (const uint8_t *)str8(s);
62585
+ for (size_t i = 0; i < slen; i++) {
62586
+ if (unlikely(p[i] & 0x80)) {
62587
+ JS_ThrowDOMException(ctx, "InvalidCharacterError",
62588
+ "The string to be decoded is not correctly encoded");
62589
+ goto fail;
62590
+ }
62591
+ }
62592
+ in = p;
62593
+ } else {
62594
+ const uint16_t *src = str16(s);
62595
+ tmp = js_malloc(ctx, likely(slen) ? slen : 1);
62596
+ if (unlikely(!tmp))
62597
+ goto fail;
62598
+ for (size_t i = 0; i < slen; i++) {
62599
+ if (unlikely(src[i] > 0x7F)) {
62600
+ JS_ThrowDOMException(ctx, "InvalidCharacterError",
62601
+ "The string to be decoded is not correctly encoded");
62602
+ goto fail;
62603
+ }
62604
+ tmp[i] = (uint8_t)src[i];
62605
+ }
62606
+ in = tmp;
62607
+ }
62608
+
62609
+ if (unlikely(slen > (SIZE_MAX / 3) * 4)) {
62610
+ JS_ThrowRangeError(ctx, "input too large");
62611
+ goto fail;
62612
+ }
62613
+ out_cap = (slen / 4) * 3 + 3;
62614
+ if (unlikely(out_cap > JS_STRING_LEN_MAX)) {
62615
+ JS_ThrowRangeError(ctx, "output too large");
62616
+ goto fail;
62617
+ }
62618
+
62619
+ ostr = js_alloc_string(ctx, out_cap, 0);
62620
+ if (unlikely(!ostr))
62621
+ goto fail;
62622
+
62623
+ outp = str8(ostr);
62624
+ err = 0;
62625
+ out_len = b64_decode((const char *)in, slen, outp, &err);
62626
+
62627
+ if (unlikely(err)) {
62628
+ js_free_string(ctx->rt, ostr);
62629
+ JS_ThrowDOMException(ctx, "InvalidCharacterError",
62630
+ "The string to be decoded is not correctly encoded");
62631
+ goto fail;
62632
+ }
62633
+ outp[out_len] = '\0';
62634
+ ostr->len = out_len;
62635
+ ret = JS_MKPTR(JS_TAG_STRING, ostr);
62636
+ fail:
62637
+ if (tmp)
62638
+ js_free(ctx, tmp);
62639
+ JS_FreeValue(ctx, val);
62640
+ return ret;
62641
+ }
62642
+
62643
+ static const JSCFunctionListEntry js_base64_funcs[] = {
62644
+ JS_CFUNC_DEF("btoa", 1, js_btoa),
62645
+ JS_CFUNC_DEF("atob", 1, js_atob),
62646
+ };
62647
+
62648
+
62649
+ /* Uint8Array base64/hex (tc39 proposal-arraybuffer-base64) */
62650
+
62651
+ enum {
62652
+ B64_ALPHABET_BASE64 = 0,
62653
+ B64_ALPHABET_BASE64URL = 1,
62654
+ };
62655
+
62656
+ enum {
62657
+ B64_LAST_LOOSE = 0,
62658
+ B64_LAST_STRICT = 1,
62659
+ B64_LAST_STOP_BEFORE_PARTIAL = 2,
62660
+ };
62661
+
62662
+
62663
+ static size_t b64_skip_ws(const char *src, size_t len, size_t index,
62664
+ const char *flags)
62665
+ {
62666
+ while (index < len && (flags[(unsigned char)src[index]] & K_WS))
62667
+ index++;
62668
+ return index;
62669
+ }
62670
+
62671
+ /* Implements the FromBase64 abstract operation.
62672
+ src/src_len: the input string (must be ASCII/latin1)
62673
+ dst/max_len: output buffer
62674
+ flags: b64_flags or b64_flags_url (selects valid characters)
62675
+ last_chunk: B64_LAST_LOOSE, B64_LAST_STRICT, or B64_LAST_STOP_BEFORE_PARTIAL
62676
+ *p_read: set to number of input characters consumed
62677
+ *p_err: set to 1 on error, 0 on success
62678
+ Returns: number of bytes written to dst */
62679
+ static size_t from_base64(const char *src, size_t src_len,
62680
+ uint8_t *dst, size_t max_len,
62681
+ const char *flags, int last_chunk,
62682
+ size_t *p_read, int *p_err)
62683
+ {
62684
+ size_t read = 0, written = 0;
62685
+ uint32_t acc = 0;
62686
+ int seen = 0;
62687
+ size_t index = 0;
62688
+
62689
+ *p_err = 0;
62690
+
62691
+ if (max_len == 0) {
62692
+ *p_read = 0;
62693
+ return 0;
62694
+ }
62695
+
62696
+ /* Fast path: decode complete groups of 4 valid characters.
62697
+ Breaks out on whitespace, padding, invalid chars, or capacity. */
62698
+ while (index + 4 <= src_len && written + 3 <= max_len) {
62699
+ uint8_t f = flags[(unsigned char)src[index]]
62700
+ & flags[(unsigned char)src[index + 1]]
62701
+ & flags[(unsigned char)src[index + 2]]
62702
+ & flags[(unsigned char)src[index + 3]];
62703
+ if (!(f & K_VAL))
62704
+ break;
62705
+ uint32_t v = ((uint32_t)b64_val[(unsigned char)src[index]] << 18)
62706
+ | ((uint32_t)b64_val[(unsigned char)src[index + 1]] << 12)
62707
+ | ((uint32_t)b64_val[(unsigned char)src[index + 2]] << 6)
62708
+ | (uint32_t)b64_val[(unsigned char)src[index + 3]];
62709
+ dst[written] = (uint8_t)(v >> 16);
62710
+ dst[written + 1] = (uint8_t)(v >> 8);
62711
+ dst[written + 2] = (uint8_t)(v);
62712
+ written += 3;
62713
+ index += 4;
62714
+ }
62715
+ read = index;
62716
+
62717
+ if (written >= max_len) {
62718
+ *p_read = read;
62719
+ return written;
62720
+ }
62721
+
62722
+ /* Slow path: handle whitespace, padding, partial groups, capacity. */
62723
+ for (;;) {
62724
+ index = b64_skip_ws(src, src_len, index, flags);
62725
+
62726
+ if (index == src_len) {
62727
+ if (seen > 0) {
62728
+ if (last_chunk == B64_LAST_STOP_BEFORE_PARTIAL) {
62729
+ *p_read = read;
62730
+ return written;
62731
+ }
62732
+ if (last_chunk == B64_LAST_STRICT) {
62733
+ *p_err = 1;
62734
+ return 0;
62735
+ }
62736
+ /* loose */
62737
+ if (seen == 1) {
62738
+ *p_err = 1;
62739
+ return 0;
62740
+ }
62741
+ goto decode_partial;
62742
+ }
62743
+ *p_read = src_len;
62744
+ return written;
62745
+ }
62746
+
62747
+ unsigned char ch = src[index++];
62748
+
62749
+ if (ch == '=') {
62750
+ if (seen < 2) {
62751
+ *p_err = 1;
62752
+ return 0;
62753
+ }
62754
+ index = b64_skip_ws(src, src_len, index, flags);
62755
+ if (seen == 2) {
62756
+ if (index == src_len) {
62757
+ if (last_chunk == B64_LAST_STOP_BEFORE_PARTIAL) {
62758
+ *p_read = read;
62759
+ return written;
62760
+ }
62761
+ *p_err = 1;
62762
+ return 0;
62763
+ }
62764
+ if (src[index] == '=') {
62765
+ index++;
62766
+ index = b64_skip_ws(src, src_len, index, flags);
62767
+ } else {
62768
+ *p_err = 1;
62769
+ return 0;
62770
+ }
62771
+ }
62772
+ /* After padding, only whitespace is allowed */
62773
+ if (index != src_len) {
62774
+ *p_err = 1;
62775
+ return 0;
62776
+ }
62777
+ if (last_chunk == B64_LAST_STRICT) {
62778
+ uint32_t mask = (seen == 2) ? 0xF : 0x3;
62779
+ if (acc & mask) {
62780
+ *p_err = 1;
62781
+ return 0;
62782
+ }
62783
+ }
62784
+ goto decode_partial;
62785
+ }
62786
+
62787
+ if (!(flags[ch] & K_VAL)) {
62788
+ *p_err = 1;
62789
+ return 0;
62790
+ }
62791
+
62792
+ /* Check remaining capacity before committing to this group */
62793
+ {
62794
+ size_t remaining = max_len - written;
62795
+ if ((remaining == 1 && seen == 2) ||
62796
+ (remaining == 2 && seen == 3)) {
62797
+ *p_read = read;
62798
+ return written;
62799
+ }
62800
+ }
62801
+
62802
+ acc = (acc << 6) | b64_val[ch];
62803
+ seen++;
62804
+
62805
+ if (seen == 4) {
62806
+ dst[written] = (uint8_t)(acc >> 16);
62807
+ dst[written + 1] = (uint8_t)(acc >> 8);
62808
+ dst[written + 2] = (uint8_t)(acc);
62809
+ written += 3;
62810
+ acc = 0;
62811
+ seen = 0;
62812
+ read = index;
62813
+ if (written >= max_len) {
62814
+ *p_read = read;
62815
+ return written;
62816
+ }
62817
+ }
62818
+ }
62819
+
62820
+ decode_partial:
62821
+ if (seen == 2) {
62822
+ dst[written++] = (uint8_t)(acc >> 4);
62823
+ } else if (seen == 3) {
62824
+ dst[written] = (uint8_t)(acc >> 10);
62825
+ dst[written + 1] = (uint8_t)(acc >> 2);
62826
+ written += 2;
62827
+ }
62828
+ *p_read = src_len;
62829
+ return written;
62830
+ }
62831
+
62832
+ /* Hex helpers */
62833
+ static const char u8a_hex_digits[] = "0123456789abcdef";
62834
+
62835
+ static int u8a_hex_nibble(unsigned char ch)
62836
+ {
62837
+ if (ch >= '0' && ch <= '9') return ch - '0';
62838
+ if (ch >= 'a' && ch <= 'f') return ch - 'a' + 10;
62839
+ if (ch >= 'A' && ch <= 'F') return ch - 'A' + 10;
62840
+ return -1;
62841
+ }
62842
+
62843
+ static size_t u8a_hex_encode(const uint8_t *src, size_t len, char *dst)
62844
+ {
62845
+ for (size_t i = 0; i < len; i++) {
62846
+ dst[i * 2] = u8a_hex_digits[src[i] >> 4];
62847
+ dst[i * 2 + 1] = u8a_hex_digits[src[i] & 0xF];
62848
+ }
62849
+ return len * 2;
62850
+ }
62851
+
62852
+ /* Decode hex string to bytes.
62853
+ Returns bytes written. Sets *p_read to chars consumed, *p_err on error. */
62854
+ static size_t u8a_hex_decode(const char *src, size_t src_len,
62855
+ uint8_t *dst, size_t max_len,
62856
+ size_t *p_read, int *p_err)
62857
+ {
62858
+ size_t written = 0, i = 0;
62859
+ *p_err = 0;
62860
+
62861
+ if (src_len & 1) {
62862
+ *p_err = 1;
62863
+ return 0;
62864
+ }
62865
+
62866
+ while (i < src_len && written < max_len) {
62867
+ int hi = u8a_hex_nibble(src[i]);
62868
+ int lo = u8a_hex_nibble(src[i + 1]);
62869
+ if (hi < 0 || lo < 0) {
62870
+ *p_err = 1;
62871
+ return 0;
62872
+ }
62873
+ dst[written++] = (uint8_t)((hi << 4) | lo);
62874
+ i += 2;
62875
+ }
62876
+
62877
+ *p_read = i;
62878
+ return written;
62879
+ }
62880
+
62881
+ /* Validate that this_val is a Uint8Array (type check only, no detach check).
62882
+ Returns the JSObject pointer or NULL on error (throws). */
62883
+ static JSObject *check_uint8array(JSContext *ctx, JSValueConst this_val)
62884
+ {
62885
+ JSObject *p;
62886
+
62887
+ if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)
62888
+ goto fail;
62889
+ p = JS_VALUE_GET_OBJ(this_val);
62890
+ if (p->class_id != JS_CLASS_UINT8_ARRAY)
62891
+ goto fail;
62892
+ return p;
62893
+ fail:
62894
+ JS_ThrowTypeError(ctx, "not a Uint8Array");
62895
+ return NULL;
62896
+ }
62897
+
62898
+ /* Get the data pointer and length of a Uint8Array, checking for detached
62899
+ buffers. Must be called after options are read (per spec ordering).
62900
+ Returns 0 on success, -1 on error (throws). */
62901
+ static int get_uint8array_bytes(JSContext *ctx, JSObject *p,
62902
+ uint8_t **pdata, size_t *plen)
62903
+ {
62904
+ if (typed_array_is_oob(p)) {
62905
+ JS_ThrowTypeErrorArrayBufferOOB(ctx);
62906
+ return -1;
62907
+ }
62908
+ *pdata = p->u.array.u.uint8_ptr;
62909
+ *plen = p->u.array.count;
62910
+ return 0;
62911
+ }
62912
+
62913
+ /* Validate options is undefined or an object (GetOptionsObject).
62914
+ Returns 0 on success, -1 on error (throws). */
62915
+ static int check_options_object(JSContext *ctx, JSValueConst options)
62916
+ {
62917
+ if (JS_IsUndefined(options))
62918
+ return 0;
62919
+ if (!JS_IsObject(options)) {
62920
+ JS_ThrowTypeError(ctx, "options must be an object");
62921
+ return -1;
62922
+ }
62923
+ return 0;
62924
+ }
62925
+
62926
+ /* Parse the 'alphabet' option from an options object.
62927
+ Returns B64_ALPHABET_BASE64 or B64_ALPHABET_BASE64URL, or -1 on error. */
62928
+ static int parse_alphabet_option(JSContext *ctx, JSValueConst options)
62929
+ {
62930
+ JSValue val;
62931
+ const char *str;
62932
+ int ret;
62933
+
62934
+ if (JS_IsUndefined(options))
62935
+ return B64_ALPHABET_BASE64;
62936
+
62937
+ val = JS_GetPropertyStr(ctx, options, "alphabet");
62938
+ if (JS_IsException(val))
62939
+ return -1;
62940
+ if (JS_IsUndefined(val))
62941
+ return B64_ALPHABET_BASE64;
62942
+ if (!JS_IsString(val)) {
62943
+ JS_FreeValue(ctx, val);
62944
+ JS_ThrowTypeError(ctx, "expected string for alphabet");
62945
+ return -1;
62946
+ }
62947
+
62948
+ str = JS_ToCString(ctx, val);
62949
+ JS_FreeValue(ctx, val);
62950
+ if (!str)
62951
+ return -1;
62952
+
62953
+ if (!strcmp(str, "base64"))
62954
+ ret = B64_ALPHABET_BASE64;
62955
+ else if (!strcmp(str, "base64url"))
62956
+ ret = B64_ALPHABET_BASE64URL;
62957
+ else {
62958
+ JS_ThrowTypeError(ctx, "invalid alphabet");
62959
+ ret = -1;
62960
+ }
62961
+ JS_FreeCString(ctx, str);
62962
+ return ret;
62963
+ }
62964
+
62965
+ /* Parse the 'lastChunkHandling' option. Returns mode or -1 on error. */
62966
+ static int parse_last_chunk_option(JSContext *ctx, JSValueConst options)
62967
+ {
62968
+ JSValue val;
62969
+ const char *str;
62970
+ int ret;
62971
+
62972
+ if (JS_IsUndefined(options))
62973
+ return B64_LAST_LOOSE;
62974
+
62975
+ val = JS_GetPropertyStr(ctx, options, "lastChunkHandling");
62976
+ if (JS_IsException(val))
62977
+ return -1;
62978
+ if (JS_IsUndefined(val))
62979
+ return B64_LAST_LOOSE;
62980
+ if (!JS_IsString(val)) {
62981
+ JS_FreeValue(ctx, val);
62982
+ JS_ThrowTypeError(ctx, "expected string for lastChunkHandling");
62983
+ return -1;
62984
+ }
62985
+
62986
+ str = JS_ToCString(ctx, val);
62987
+ JS_FreeValue(ctx, val);
62988
+ if (!str)
62989
+ return -1;
62990
+
62991
+ if (!strcmp(str, "loose"))
62992
+ ret = B64_LAST_LOOSE;
62993
+ else if (!strcmp(str, "strict"))
62994
+ ret = B64_LAST_STRICT;
62995
+ else if (!strcmp(str, "stop-before-partial"))
62996
+ ret = B64_LAST_STOP_BEFORE_PARTIAL;
62997
+ else {
62998
+ JS_ThrowTypeError(ctx, "invalid lastChunkHandling option");
62999
+ ret = -1;
63000
+ }
63001
+ JS_FreeCString(ctx, str);
63002
+ return ret;
63003
+ }
63004
+
63005
+ /* Uint8Array.prototype.toBase64([options]) */
63006
+ static JSValue js_uint8array_to_base64(JSContext *ctx, JSValueConst this_val,
63007
+ int argc, JSValueConst *argv)
63008
+ {
63009
+ uint8_t *data;
63010
+ size_t len;
63011
+ JSValue options;
63012
+ JSObject *p;
63013
+ int alphabet, omit_padding;
63014
+ size_t out_len, written;
63015
+ JSString *ostr;
63016
+ char *dst;
63017
+
63018
+ p = check_uint8array(ctx, this_val);
63019
+ if (!p)
63020
+ return JS_EXCEPTION;
63021
+
63022
+ options = argc > 0 ? unsafe_unconst(argv[0]) : JS_UNDEFINED;
63023
+ if (check_options_object(ctx, options))
63024
+ return JS_EXCEPTION;
63025
+ alphabet = parse_alphabet_option(ctx, options);
63026
+ if (alphabet < 0)
63027
+ return JS_EXCEPTION;
63028
+
63029
+ omit_padding = 0;
63030
+ if (!JS_IsUndefined(options)) {
63031
+ JSValue op_val = JS_GetPropertyStr(ctx, options, "omitPadding");
63032
+ if (JS_IsException(op_val))
63033
+ return JS_EXCEPTION;
63034
+ omit_padding = JS_ToBool(ctx, op_val);
63035
+ JS_FreeValue(ctx, op_val);
63036
+ }
63037
+
63038
+ if (get_uint8array_bytes(ctx, p, &data, &len))
63039
+ return JS_EXCEPTION;
63040
+
63041
+ out_len = 4 * ((len + 2) / 3);
63042
+
63043
+ if (unlikely(out_len > JS_STRING_LEN_MAX))
63044
+ return JS_ThrowRangeError(ctx, "output too large");
63045
+
63046
+ ostr = js_alloc_string(ctx, out_len, 0);
63047
+ if (!ostr)
63048
+ return JS_EXCEPTION;
63049
+
63050
+ dst = (char *)str8(ostr);
63051
+ written = b64_encode(data, len, dst,
63052
+ alphabet == B64_ALPHABET_BASE64URL ? b64url_enc : b64_enc);
63053
+ if (omit_padding) {
63054
+ while (written > 0 && dst[written - 1] == '=')
63055
+ written--;
63056
+ }
63057
+ dst[written] = '\0';
63058
+
63059
+ ostr->len = written;
63060
+ return JS_MKPTR(JS_TAG_STRING, ostr);
63061
+ }
63062
+
63063
+ /* Uint8Array.prototype.toHex() */
63064
+ static JSValue js_uint8array_to_hex(JSContext *ctx, JSValueConst this_val,
63065
+ int argc, JSValueConst *argv)
63066
+ {
63067
+ uint8_t *data;
63068
+ size_t len, out_len;
63069
+ JSObject *p;
63070
+ JSString *ostr;
63071
+
63072
+ p = check_uint8array(ctx, this_val);
63073
+ if (!p)
63074
+ return JS_EXCEPTION;
63075
+ if (get_uint8array_bytes(ctx, p, &data, &len))
63076
+ return JS_EXCEPTION;
63077
+
63078
+ out_len = len * 2;
63079
+ if (unlikely(out_len > JS_STRING_LEN_MAX))
63080
+ return JS_ThrowRangeError(ctx, "output too large");
63081
+
63082
+ ostr = js_alloc_string(ctx, out_len, 0);
63083
+ if (!ostr)
63084
+ return JS_EXCEPTION;
63085
+
63086
+ u8a_hex_encode(data, len, (char *)str8(ostr));
63087
+ str8(ostr)[out_len] = '\0';
63088
+ return JS_MKPTR(JS_TAG_STRING, ostr);
63089
+ }
63090
+
63091
+ /* Uint8Array.fromBase64(string[, options]) */
63092
+ static JSValue js_uint8array_from_base64(JSContext *ctx, JSValueConst this_val,
63093
+ int argc, JSValueConst *argv)
63094
+ {
63095
+ const char *str;
63096
+ size_t str_len, read_pos, decoded_len, out_cap;
63097
+ int alphabet, last_chunk, err;
63098
+ uint8_t *buf;
63099
+ JSValue result, options;
63100
+
63101
+ if (!JS_IsString(argv[0]))
63102
+ return JS_ThrowTypeError(ctx, "expected string");
63103
+
63104
+ str = JS_ToCStringLen(ctx, &str_len, argv[0]);
63105
+ if (!str)
63106
+ return JS_EXCEPTION;
63107
+
63108
+ options = argc > 1 ? unsafe_unconst(argv[1]) : JS_UNDEFINED;
63109
+ if (check_options_object(ctx, options)) {
63110
+ JS_FreeCString(ctx, str);
63111
+ return JS_EXCEPTION;
63112
+ }
63113
+ alphabet = parse_alphabet_option(ctx, options);
63114
+ if (alphabet < 0) {
63115
+ JS_FreeCString(ctx, str);
63116
+ return JS_EXCEPTION;
63117
+ }
63118
+ last_chunk = parse_last_chunk_option(ctx, options);
63119
+ if (last_chunk < 0) {
63120
+ JS_FreeCString(ctx, str);
63121
+ return JS_EXCEPTION;
63122
+ }
63123
+
63124
+ out_cap = (str_len / 4) * 3 + 3;
63125
+ buf = js_malloc(ctx, out_cap ? out_cap : 1);
63126
+ if (!buf) {
63127
+ JS_FreeCString(ctx, str);
63128
+ return JS_EXCEPTION;
63129
+ }
63130
+
63131
+ decoded_len = from_base64(str, str_len, buf, out_cap,
63132
+ alphabet == B64_ALPHABET_BASE64URL
63133
+ ? b64_flags_url : b64_flags,
63134
+ last_chunk, &read_pos, &err);
63135
+ JS_FreeCString(ctx, str);
63136
+
63137
+ if (err) {
63138
+ js_free(ctx, buf);
63139
+ return JS_ThrowSyntaxError(ctx, "invalid base64 string");
63140
+ }
63141
+
63142
+ result = JS_NewUint8ArrayCopy(ctx, buf, decoded_len);
63143
+ js_free(ctx, buf);
63144
+ return result;
63145
+ }
63146
+
63147
+ /* Uint8Array.fromHex(string) */
63148
+ static JSValue js_uint8array_from_hex(JSContext *ctx, JSValueConst this_val,
63149
+ int argc, JSValueConst *argv)
63150
+ {
63151
+ const char *str;
63152
+ size_t str_len, read_pos, decoded_len, out_cap;
63153
+ int err;
63154
+ uint8_t *buf;
63155
+ JSValue result;
63156
+
63157
+ if (!JS_IsString(argv[0]))
63158
+ return JS_ThrowTypeError(ctx, "expected string");
63159
+
63160
+ str = JS_ToCStringLen(ctx, &str_len, argv[0]);
63161
+ if (!str)
63162
+ return JS_EXCEPTION;
63163
+
63164
+ out_cap = str_len / 2 + 1;
63165
+ buf = js_malloc(ctx, out_cap ? out_cap : 1);
63166
+ if (!buf) {
63167
+ JS_FreeCString(ctx, str);
63168
+ return JS_EXCEPTION;
63169
+ }
63170
+
63171
+ decoded_len = u8a_hex_decode(str, str_len, buf, out_cap, &read_pos, &err);
63172
+ JS_FreeCString(ctx, str);
63173
+
63174
+ if (err) {
63175
+ js_free(ctx, buf);
63176
+ return JS_ThrowSyntaxError(ctx, "invalid hex string");
63177
+ }
63178
+
63179
+ result = JS_NewUint8ArrayCopy(ctx, buf, decoded_len);
63180
+ js_free(ctx, buf);
63181
+ return result;
63182
+ }
63183
+
63184
+ /* Return a { read, written } result object */
63185
+ static JSValue js_make_read_written(JSContext *ctx, size_t read, size_t written)
63186
+ {
63187
+ JSValue obj = JS_NewObject(ctx);
63188
+ if (JS_IsException(obj))
63189
+ return JS_EXCEPTION;
63190
+ if (JS_DefinePropertyValueStr(ctx, obj, "read",
63191
+ js_uint32(read), JS_PROP_C_W_E) < 0)
63192
+ goto fail;
63193
+ if (JS_DefinePropertyValueStr(ctx, obj, "written",
63194
+ js_uint32(written), JS_PROP_C_W_E) < 0)
63195
+ goto fail;
63196
+ return obj;
63197
+ fail:
63198
+ JS_FreeValue(ctx, obj);
63199
+ return JS_EXCEPTION;
63200
+ }
63201
+
63202
+ /* Uint8Array.prototype.setFromBase64(string[, options]) */
63203
+ static JSValue js_uint8array_set_from_base64(JSContext *ctx,
63204
+ JSValueConst this_val,
63205
+ int argc, JSValueConst *argv)
63206
+ {
63207
+ uint8_t *data;
63208
+ size_t len;
63209
+ const char *str;
63210
+ size_t str_len, read_pos, decoded_len;
63211
+ JSObject *p;
63212
+ int alphabet, last_chunk, err;
63213
+ JSValue options;
63214
+
63215
+ p = check_uint8array(ctx, this_val);
63216
+ if (!p)
63217
+ return JS_EXCEPTION;
63218
+
63219
+ if (!JS_IsString(argv[0]))
63220
+ return JS_ThrowTypeError(ctx, "expected string");
63221
+
63222
+ str = JS_ToCStringLen(ctx, &str_len, argv[0]);
63223
+ if (!str)
63224
+ return JS_EXCEPTION;
63225
+
63226
+ options = argc > 1 ? unsafe_unconst(argv[1]) : JS_UNDEFINED;
63227
+ if (check_options_object(ctx, options)) {
63228
+ JS_FreeCString(ctx, str);
63229
+ return JS_EXCEPTION;
63230
+ }
63231
+ alphabet = parse_alphabet_option(ctx, options);
63232
+ if (alphabet < 0) {
63233
+ JS_FreeCString(ctx, str);
63234
+ return JS_EXCEPTION;
63235
+ }
63236
+ last_chunk = parse_last_chunk_option(ctx, options);
63237
+ if (last_chunk < 0) {
63238
+ JS_FreeCString(ctx, str);
63239
+ return JS_EXCEPTION;
63240
+ }
63241
+
63242
+ if (get_uint8array_bytes(ctx, p, &data, &len)) {
63243
+ JS_FreeCString(ctx, str);
63244
+ return JS_EXCEPTION;
63245
+ }
63246
+
63247
+ decoded_len = from_base64(str, str_len, data, len,
63248
+ alphabet == B64_ALPHABET_BASE64URL
63249
+ ? b64_flags_url : b64_flags,
63250
+ last_chunk, &read_pos, &err);
63251
+ JS_FreeCString(ctx, str);
63252
+
63253
+ if (err)
63254
+ return JS_ThrowSyntaxError(ctx, "invalid base64 string");
63255
+
63256
+ return js_make_read_written(ctx, read_pos, decoded_len);
63257
+ }
63258
+
63259
+ /* Uint8Array.prototype.setFromHex(string) */
63260
+ static JSValue js_uint8array_set_from_hex(JSContext *ctx,
63261
+ JSValueConst this_val,
63262
+ int argc, JSValueConst *argv)
63263
+ {
63264
+ uint8_t *data;
63265
+ size_t len;
63266
+ const char *str;
63267
+ size_t str_len, read_pos, decoded_len;
63268
+ JSObject *p;
63269
+ int err;
63270
+
63271
+ p = check_uint8array(ctx, this_val);
63272
+ if (!p)
63273
+ return JS_EXCEPTION;
63274
+
63275
+ if (!JS_IsString(argv[0]))
63276
+ return JS_ThrowTypeError(ctx, "expected string");
63277
+
63278
+ str = JS_ToCStringLen(ctx, &str_len, argv[0]);
63279
+ if (!str)
63280
+ return JS_EXCEPTION;
63281
+
63282
+ if (get_uint8array_bytes(ctx, p, &data, &len)) {
63283
+ JS_FreeCString(ctx, str);
63284
+ return JS_EXCEPTION;
63285
+ }
63286
+
63287
+ decoded_len = u8a_hex_decode(str, str_len, data, len, &read_pos, &err);
63288
+ JS_FreeCString(ctx, str);
63289
+
63290
+ if (err)
63291
+ return JS_ThrowSyntaxError(ctx, "invalid hex string");
63292
+
63293
+ return js_make_read_written(ctx, read_pos, decoded_len);
63294
+ }
63295
+
63296
+ static const JSCFunctionListEntry js_uint8array_proto_funcs[] = {
63297
+ JS_CFUNC_DEF("toBase64", 0, js_uint8array_to_base64),
63298
+ JS_CFUNC_DEF("toHex", 0, js_uint8array_to_hex),
63299
+ JS_CFUNC_DEF("setFromBase64", 1, js_uint8array_set_from_base64),
63300
+ JS_CFUNC_DEF("setFromHex", 1, js_uint8array_set_from_hex),
63301
+ };
63302
+
63303
+ static const JSCFunctionListEntry js_uint8array_funcs[] = {
63304
+ JS_CFUNC_DEF("fromBase64", 1, js_uint8array_from_base64),
63305
+ JS_CFUNC_DEF("fromHex", 1, js_uint8array_from_hex),
63306
+ };
63307
+
63308
+ static int js_uint8array_funcs_init(JSContext *ctx)
63309
+ {
63310
+ JSValue ctor, proto;
63311
+
63312
+ ctor = JS_GetProperty(ctx, ctx->global_obj, JS_ATOM_Uint8Array);
63313
+ if (JS_IsException(ctor))
63314
+ return -1;
63315
+ proto = JS_GetProperty(ctx, ctor, JS_ATOM_prototype);
63316
+ if (JS_IsException(proto)) {
63317
+ JS_FreeValue(ctx, ctor);
63318
+ return -1;
63319
+ }
63320
+ JS_SetPropertyFunctionList(ctx, proto,
63321
+ js_uint8array_proto_funcs,
63322
+ countof(js_uint8array_proto_funcs));
63323
+ JS_FreeValue(ctx, proto);
63324
+ JS_SetPropertyFunctionList(ctx, ctor,
63325
+ js_uint8array_funcs,
63326
+ countof(js_uint8array_funcs));
63327
+ JS_FreeValue(ctx, ctor);
63328
+ return 0;
63329
+ }
63330
+
63331
+ int JS_AddIntrinsicAToB(JSContext *ctx)
63332
+ {
63333
+ if (!JS_IsRegisteredClass(ctx->rt, JS_CLASS_DOM_EXCEPTION)) {
63334
+ if (JS_AddIntrinsicDOMException(ctx))
63335
+ return -1;
63336
+ }
63337
+ JS_SetPropertyFunctionList(ctx, ctx->global_obj,
63338
+ js_base64_funcs, countof(js_base64_funcs));
63339
+ return 0;
63340
+ }
60832
63341
 
60833
63342
  bool JS_DetectModule(const char *input, size_t input_len)
60834
63343
  {