@ugo-studio/jspp 0.1.5 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -78,7 +78,7 @@ export function visitSourceFile(node, context) {
78
78
  return code;
79
79
  }
80
80
  export function visitBlock(node, context) {
81
- let code = "{\n";
81
+ let code = `${this.indent()}{\n`;
82
82
  this.indentationLevel++;
83
83
  const block = node;
84
84
  // Collect ONLY block-scoped declarations (let/const)
@@ -232,35 +232,201 @@ export function visitThrowStatement(node, context) {
232
232
  }
233
233
  export function visitTryStatement(node, context) {
234
234
  const tryStmt = node;
235
+ if (context.isInsideAsyncFunction) {
236
+ if (tryStmt.finallyBlock) {
237
+ const declaredSymbols = new Set(context.topLevelScopeSymbols.toSet());
238
+ this.getDeclaredSymbols(tryStmt.tryBlock).forEach((s) => declaredSymbols.add(s));
239
+ if (tryStmt.catchClause) {
240
+ this.getDeclaredSymbols(tryStmt.catchClause).forEach((s) => declaredSymbols.add(s));
241
+ }
242
+ this.getDeclaredSymbols(tryStmt.finallyBlock).forEach((s) => declaredSymbols.add(s));
243
+ const resultVarName = this.generateUniqueName("__try_result_", declaredSymbols);
244
+ const hasReturnedFlagName = this.generateUniqueName("__try_has_returned_", declaredSymbols);
245
+ const catchAllExPtrName = this.generateUniqueName("__catch_all_exptr", declaredSymbols);
246
+ let code = `${this.indent()}{\n`;
247
+ this.indentationLevel++;
248
+ code += `${this.indent()}jspp::AnyValue ${resultVarName};\n`;
249
+ code +=
250
+ `${this.indent()}std::exception_ptr ${catchAllExPtrName} = nullptr;\n`;
251
+ code += `${this.indent()}bool ${hasReturnedFlagName} = false;\n`;
252
+ const returnType = "jspp::JsPromise";
253
+ const returnCmd = "co_return";
254
+ const callPrefix = "co_await ";
255
+ code += `${this.indent()}try {\n`;
256
+ this.indentationLevel++;
257
+ code +=
258
+ `${this.indent()}${resultVarName} = ${callPrefix}([=, &${hasReturnedFlagName}]() -> ${returnType} {\n`;
259
+ this.indentationLevel++;
260
+ const innerContext = {
261
+ ...context,
262
+ isFunctionBody: false,
263
+ isInsideTryCatchLambda: true,
264
+ hasReturnedFlag: hasReturnedFlagName,
265
+ };
266
+ const exPtr = this.generateUniqueName("__ex_ptr");
267
+ code += `${this.indent()}std::exception_ptr ${exPtr} = nullptr;\n`;
268
+ code += `${this.indent()}try {\n`;
269
+ this.indentationLevel++;
270
+ code += this.visit(tryStmt.tryBlock, innerContext);
271
+ this.indentationLevel--;
272
+ code +=
273
+ `${this.indent()}} catch (...) { ${exPtr} = std::current_exception(); }\n`;
274
+ if (tryStmt.catchClause) {
275
+ const exceptionName = this.generateUniqueExceptionName(tryStmt.catchClause.variableDeclaration?.name.getText(), declaredSymbols);
276
+ const caughtValVar = this.generateUniqueName("__caught_val");
277
+ const caughtFlagVar = this.generateUniqueName("__caught_flag");
278
+ code +=
279
+ `${this.indent()}jspp::AnyValue ${caughtValVar} = jspp::Constants::UNDEFINED;\n`;
280
+ code += `${this.indent()}bool ${caughtFlagVar} = false;\n`;
281
+ code += `${this.indent()}if (${exPtr}) {\n`;
282
+ this.indentationLevel++;
283
+ code +=
284
+ `${this.indent()}try { std::rethrow_exception(${exPtr}); } catch (const std::exception& ${exceptionName}) {\n`;
285
+ this.indentationLevel++;
286
+ code +=
287
+ `${this.indent()}${caughtValVar} = jspp::Exception::exception_to_any_value(${exceptionName});\n`;
288
+ code += `${this.indent()}${caughtFlagVar} = true;\n`;
289
+ this.indentationLevel--;
290
+ code += `${this.indent()}} catch (...) {\n`;
291
+ this.indentationLevel++;
292
+ code +=
293
+ `${this.indent()}${caughtValVar} = jspp::AnyValue::make_string("Unknown native exception");\n`;
294
+ code += `${this.indent()}${caughtFlagVar} = true;\n`;
295
+ this.indentationLevel--;
296
+ code += `${this.indent()}}\n`;
297
+ this.indentationLevel--;
298
+ code += `${this.indent()}}\n`;
299
+ code += `${this.indent()}if (${caughtFlagVar}) {\n`;
300
+ this.indentationLevel++;
301
+ code += `${this.indent()}{\n`; // Block scope
302
+ this.indentationLevel++;
303
+ if (tryStmt.catchClause.variableDeclaration) {
304
+ const varName = tryStmt.catchClause.variableDeclaration.name
305
+ .getText();
306
+ code +=
307
+ `${this.indent()}jspp::AnyValue ${varName} = ${caughtValVar};\n`;
308
+ }
309
+ const catchContext = { ...innerContext, exceptionName };
310
+ code += this.visit(tryStmt.catchClause.block, catchContext);
311
+ this.indentationLevel--;
312
+ code += `${this.indent()}}\n`;
313
+ this.indentationLevel--;
314
+ code += `${this.indent()}}\n`;
315
+ }
316
+ else {
317
+ code +=
318
+ `${this.indent()}if (${exPtr}) { std::rethrow_exception(${exPtr}); }\n`;
319
+ }
320
+ code +=
321
+ `${this.indent()}${returnCmd} jspp::Constants::UNDEFINED;\n`;
322
+ this.indentationLevel--;
323
+ code += `${this.indent()}})();\n`;
324
+ this.indentationLevel--;
325
+ code +=
326
+ `${this.indent()}} catch (...) { ${catchAllExPtrName} = std::current_exception(); }\n`;
327
+ code += `${this.indent()}// finally block\n`;
328
+ code += this.visit(tryStmt.finallyBlock, {
329
+ ...context,
330
+ isFunctionBody: false,
331
+ });
332
+ code += `${this.indent()}// re-throw or return\n`;
333
+ code +=
334
+ `${this.indent()}if (${catchAllExPtrName}) { std::rethrow_exception(${catchAllExPtrName}); }\n`;
335
+ code +=
336
+ `${this.indent()}if (${hasReturnedFlagName}) { ${returnCmd} ${resultVarName}; }\n`;
337
+ this.indentationLevel--;
338
+ code += `${this.indent()}}\n`;
339
+ return code;
340
+ }
341
+ else {
342
+ const exceptionName = this.generateUniqueExceptionName(tryStmt.catchClause?.variableDeclaration?.name.getText(), context.topLevelScopeSymbols, context.localScopeSymbols);
343
+ const newContext = {
344
+ ...context,
345
+ isFunctionBody: false,
346
+ exceptionName,
347
+ };
348
+ const exPtr = this.generateUniqueName("__ex_ptr");
349
+ let code = `${this.indent()}{\n`;
350
+ this.indentationLevel++;
351
+ code += `${this.indent()}std::exception_ptr ${exPtr} = nullptr;\n`;
352
+ code += `${this.indent()}try {\n`;
353
+ this.indentationLevel++;
354
+ code += this.visit(tryStmt.tryBlock, newContext);
355
+ this.indentationLevel--;
356
+ code +=
357
+ `${this.indent()}} catch (...) { ${exPtr} = std::current_exception(); }\n`;
358
+ if (tryStmt.catchClause) {
359
+ const caughtValVar = this.generateUniqueName("__caught_val");
360
+ const caughtFlagVar = this.generateUniqueName("__caught_flag");
361
+ code +=
362
+ `${this.indent()}jspp::AnyValue ${caughtValVar} = jspp::Constants::UNDEFINED;\n`;
363
+ code += `${this.indent()}bool ${caughtFlagVar} = false;\n`;
364
+ code += `${this.indent()}if (${exPtr}) {\n`;
365
+ this.indentationLevel++;
366
+ code +=
367
+ `${this.indent()}try { std::rethrow_exception(${exPtr}); } catch (const std::exception& ${exceptionName}) {\n`;
368
+ this.indentationLevel++;
369
+ code +=
370
+ `${this.indent()}${caughtValVar} = jspp::Exception::exception_to_any_value(${exceptionName});\n`;
371
+ code += `${this.indent()}${caughtFlagVar} = true;\n`;
372
+ this.indentationLevel--;
373
+ code += `${this.indent()}} catch (...) {\n`;
374
+ this.indentationLevel++;
375
+ code +=
376
+ `${this.indent()}${caughtValVar} = jspp::AnyValue::make_string("Unknown native exception");\n`;
377
+ code += `${this.indent()}${caughtFlagVar} = true;\n`;
378
+ this.indentationLevel--;
379
+ code += `${this.indent()}}\n`;
380
+ this.indentationLevel--;
381
+ code += `${this.indent()}}\n`;
382
+ code += `${this.indent()}if (${caughtFlagVar}) {\n`;
383
+ this.indentationLevel++;
384
+ code += `${this.indent()}{\n`; // Block scope
385
+ this.indentationLevel++;
386
+ if (tryStmt.catchClause.variableDeclaration) {
387
+ const varName = tryStmt.catchClause.variableDeclaration.name
388
+ .getText();
389
+ code +=
390
+ `${this.indent()}jspp::AnyValue ${varName} = ${caughtValVar};\n`;
391
+ }
392
+ code += this.visit(tryStmt.catchClause.block, newContext);
393
+ this.indentationLevel--;
394
+ code += `${this.indent()}}\n`;
395
+ this.indentationLevel--;
396
+ code += `${this.indent()}}\n`;
397
+ }
398
+ else {
399
+ code +=
400
+ `${this.indent()}if (${exPtr}) { std::rethrow_exception(${exPtr}); }\n`;
401
+ }
402
+ this.indentationLevel--;
403
+ code += `${this.indent()}}\n`;
404
+ return code;
405
+ }
406
+ }
235
407
  if (tryStmt.finallyBlock) {
236
- const declaredSymbols = new Set();
408
+ const declaredSymbols = new Set(context.topLevelScopeSymbols.toSet());
237
409
  this.getDeclaredSymbols(tryStmt.tryBlock).forEach((s) => declaredSymbols.add(s));
238
410
  if (tryStmt.catchClause) {
239
411
  this.getDeclaredSymbols(tryStmt.catchClause).forEach((s) => declaredSymbols.add(s));
240
412
  }
241
413
  this.getDeclaredSymbols(tryStmt.finallyBlock).forEach((s) => declaredSymbols.add(s));
242
- const finallyLambdaName = this.generateUniqueName("__finally_", declaredSymbols);
243
414
  const resultVarName = this.generateUniqueName("__try_result_", declaredSymbols);
244
415
  const hasReturnedFlagName = this.generateUniqueName("__try_has_returned_", declaredSymbols);
416
+ const catchAllExPtrName = this.generateUniqueName("__catch_all_exptr", declaredSymbols);
245
417
  let code = `${this.indent()}{\n`;
246
418
  this.indentationLevel++;
247
- code += `${this.indent()}jspp::AnyValue ${resultVarName};
248
- `;
249
- code += `${this.indent()}bool ${hasReturnedFlagName} = false;
250
- `;
251
- const finallyBlockCode = this.visit(tryStmt.finallyBlock, {
252
- ...context,
253
- isFunctionBody: false,
254
- });
419
+ code += `${this.indent()}jspp::AnyValue ${resultVarName};\n`;
255
420
  code +=
256
- `${this.indent()}auto ${finallyLambdaName} = [=]() ${finallyBlockCode.trim()};
257
- `;
258
- code += `${this.indent()}try {
259
- `;
421
+ `${this.indent()}std::exception_ptr ${catchAllExPtrName} = nullptr;\n`;
422
+ code += `${this.indent()}bool ${hasReturnedFlagName} = false;\n`;
423
+ const returnType = "jspp::AnyValue";
424
+ const returnCmd = "return";
425
+ const callPrefix = "";
426
+ code += `${this.indent()}try {\n`;
260
427
  this.indentationLevel++;
261
428
  code +=
262
- `${this.indent()}${resultVarName} = ([=, &${hasReturnedFlagName}]() -> jspp::AnyValue {
263
- `;
429
+ `${this.indent()}${resultVarName} = ${callPrefix}([=, &${hasReturnedFlagName}]() -> ${returnType} {\n`;
264
430
  this.indentationLevel++;
265
431
  const innerContext = {
266
432
  ...context,
@@ -268,19 +434,20 @@ export function visitTryStatement(node, context) {
268
434
  isInsideTryCatchLambda: true,
269
435
  hasReturnedFlag: hasReturnedFlagName,
270
436
  };
271
- code += `${this.indent()}try {
272
- `;
437
+ code += `${this.indent()}try {\n`;
273
438
  this.indentationLevel++;
274
439
  code += this.visit(tryStmt.tryBlock, innerContext);
275
440
  this.indentationLevel--;
276
441
  code += `${this.indent()}}\n`;
277
442
  if (tryStmt.catchClause) {
278
- const exceptionName = this.generateUniqueExceptionName(tryStmt.catchClause.variableDeclaration?.name.getText());
443
+ const exceptionName = this.generateUniqueExceptionName(tryStmt.catchClause.variableDeclaration?.name.getText(), context.topLevelScopeSymbols, context.localScopeSymbols);
279
444
  const catchContext = { ...innerContext, exceptionName };
280
445
  code +=
281
- `${this.indent()}catch (const std::exception& ${exceptionName}) {
282
- `;
446
+ `${this.indent()}catch (const std::exception& ${exceptionName}) {\n`;
283
447
  this.indentationLevel++;
448
+ // We cannot co_await here. For now, let's just visit the catch block.
449
+ // If the catch block contains await, it will fail to compile.
450
+ // TODO: properly handle async catch by moving it out of native C++ catch.
284
451
  code += this.visit(tryStmt.catchClause.block, catchContext);
285
452
  this.indentationLevel--;
286
453
  code += `${this.indent()}}\n`;
@@ -288,30 +455,28 @@ export function visitTryStatement(node, context) {
288
455
  else {
289
456
  code += `${this.indent()}catch (...) { throw; }\n`;
290
457
  }
291
- code += `${this.indent()}${this.getReturnCommand(context)} jspp::Constants::UNDEFINED;\n`;
458
+ code += `${this.indent()}${returnCmd} jspp::Constants::UNDEFINED;\n`;
292
459
  this.indentationLevel--;
293
460
  code += `${this.indent()}})();\n`;
294
461
  this.indentationLevel--;
295
- code += `${this.indent()}} catch (...) {
296
- `;
297
- this.indentationLevel++;
298
- code += `${this.indent()}${finallyLambdaName}();\n`;
299
- code += `${this.indent()}throw;\n`;
300
- this.indentationLevel--;
301
- code += `${this.indent()}}\n`;
302
- code += `${this.indent()}${finallyLambdaName}();\n`;
303
- code += `${this.indent()}if (${hasReturnedFlagName}) {
304
- `;
305
- this.indentationLevel++;
306
- code += `${this.indent()}return ${resultVarName};\n`;
307
- this.indentationLevel--;
308
- code += `${this.indent()}}\n`;
462
+ code +=
463
+ `${this.indent()}} catch (...) { ${catchAllExPtrName} = std::current_exception(); }\n`;
464
+ code += `${this.indent()}// finally block\n`;
465
+ code += this.visit(tryStmt.finallyBlock, {
466
+ ...context,
467
+ isFunctionBody: false,
468
+ });
469
+ code += `${this.indent()}// re-throw or return\n`;
470
+ code +=
471
+ `${this.indent()}if (${catchAllExPtrName}) { std::rethrow_exception(${catchAllExPtrName}); }\n`;
472
+ code +=
473
+ `${this.indent()}if (${hasReturnedFlagName}) { ${returnCmd} ${resultVarName}; }\n`;
309
474
  this.indentationLevel--;
310
475
  code += `${this.indent()}}\n`;
311
476
  return code;
312
477
  }
313
478
  else {
314
- const exceptionName = this.generateUniqueExceptionName(tryStmt.catchClause?.variableDeclaration?.name.getText());
479
+ const exceptionName = this.generateUniqueExceptionName(tryStmt.catchClause?.variableDeclaration?.name.getText(), context.topLevelScopeSymbols, context.localScopeSymbols);
315
480
  const newContext = {
316
481
  ...context,
317
482
  isFunctionBody: false,
@@ -320,7 +485,8 @@ export function visitTryStatement(node, context) {
320
485
  let code = `${this.indent()}try `;
321
486
  code += this.visit(tryStmt.tryBlock, newContext);
322
487
  if (tryStmt.catchClause) {
323
- code += ` catch (const std::exception& ${exceptionName}) `;
488
+ code +=
489
+ `${this.indent()}catch (const std::exception& ${exceptionName}) `;
324
490
  code += this.visit(tryStmt.catchClause, newContext);
325
491
  }
326
492
  return code;
@@ -337,21 +503,12 @@ export function visitCatchClause(node, context) {
337
503
  const varName = catchClause.variableDeclaration.name.getText();
338
504
  let code = `{\n`;
339
505
  this.indentationLevel++;
340
- code += `${this.indent()}{\n`;
341
- this.indentationLevel++;
342
506
  // The JS exception variable is always local to the catch block
343
507
  code +=
344
508
  `${this.indent()}jspp::AnyValue ${varName} = jspp::Exception::exception_to_any_value(${exceptionName});\n`;
345
- // Shadow the C++ exception variable *only if* the names don't clash.
346
- if (varName !== exceptionName) {
347
- code +=
348
- `${this.indent()}auto ${exceptionName} = std::make_shared<jspp::AnyValue>(jspp::Constants::UNDEFINED);\n`;
349
- }
350
509
  code += this.visit(catchClause.block, context);
351
510
  this.indentationLevel--;
352
511
  code += `${this.indent()}}\n`;
353
- this.indentationLevel--;
354
- code += `${this.indent()}}\n`;
355
512
  return code;
356
513
  }
357
514
  else {
@@ -383,32 +540,59 @@ export function visitYieldExpression(node, context) {
383
540
  let code = `${this.indent()}{\n`;
384
541
  this.indentationLevel++;
385
542
  const declaredSymbols = this.getDeclaredSymbols(expr);
543
+ context.topLevelScopeSymbols.toSet().forEach((s) => declaredSymbols.add(s));
386
544
  const iterableRef = this.generateUniqueName("__iter_ref", declaredSymbols);
387
545
  const iterator = this.generateUniqueName("__iter", declaredSymbols);
388
546
  const nextFunc = this.generateUniqueName("__next_func", declaredSymbols);
389
547
  const nextRes = this.generateUniqueName("__next_res", declaredSymbols);
390
548
  const varName = this.getJsVarName(expr);
391
549
  code += `${this.indent()}auto ${iterableRef} = ${exprText};\n`;
392
- code +=
393
- `${this.indent()}auto ${iterator} = jspp::Access::get_object_value_iterator(${iterableRef}, ${varName});\n`;
550
+ if (context.isInsideAsyncFunction) {
551
+ code +=
552
+ `${this.indent()}auto ${iterator} = jspp::Access::get_async_object_value_iterator(${iterableRef}, ${varName});\n`;
553
+ }
554
+ else {
555
+ code +=
556
+ `${this.indent()}auto ${iterator} = jspp::Access::get_object_value_iterator(${iterableRef}, ${varName});\n`;
557
+ }
394
558
  code +=
395
559
  `${this.indent()}auto ${nextFunc} = ${iterator}.get_own_property("next");\n`;
396
- code +=
397
- `${this.indent()}auto ${nextRes} = ${nextFunc}.call(${iterator}, {}, "next");\n`;
560
+ if (context.isInsideAsyncFunction) {
561
+ code +=
562
+ `${this.indent()}auto ${nextRes} = co_await ${nextFunc}.call(${iterator}, {}, "next");\n`;
563
+ }
564
+ else {
565
+ code +=
566
+ `${this.indent()}auto ${nextRes} = ${nextFunc}.call(${iterator}, {}, "next");\n`;
567
+ }
398
568
  code +=
399
569
  `${this.indent()}while (!is_truthy(${nextRes}.get_own_property("done"))) {\n`;
400
570
  this.indentationLevel++;
401
- code +=
402
- `${this.indent()}co_yield ${nextRes}.get_own_property("value");\n`;
403
- code +=
404
- `${this.indent()}${nextRes} = ${nextFunc}.call(${iterator}, {}, "next");\n`;
571
+ if (context.isInsideAsyncFunction) {
572
+ code +=
573
+ `${this.indent()}co_yield co_await ${nextRes}.get_own_property("value");\n`;
574
+ }
575
+ else {
576
+ code +=
577
+ `${this.indent()}co_yield ${nextRes}.get_own_property("value");\n`;
578
+ }
579
+ if (context.isInsideAsyncFunction) {
580
+ code +=
581
+ `${this.indent()}${nextRes} = co_await ${nextFunc}.call(${iterator}, {}, "next");\n`;
582
+ }
583
+ else {
584
+ code +=
585
+ `${this.indent()}${nextRes} = ${nextFunc}.call(${iterator}, {}, "next");\n`;
586
+ }
405
587
  this.indentationLevel--;
406
588
  code += `${this.indent()}}\n`;
407
589
  return code;
408
590
  }
409
- return `${this.indent()}co_yield ${exprText}`;
591
+ const awaitPart = context.isInsideAsyncFunction ? "co_await " : "";
592
+ return `${this.indent()}co_yield ${awaitPart}${exprText}`;
410
593
  }
411
- return `${this.indent()}co_yield jspp::Constants::UNDEFINED`;
594
+ const awaitPart = context.isInsideAsyncFunction ? "co_await " : "";
595
+ return `${this.indent()}co_yield ${awaitPart}jspp::Constants::UNDEFINED`;
412
596
  }
413
597
  export function visitReturnStatement(node, context) {
414
598
  if (context.isMainContext) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ugo-studio/jspp",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "A modern transpiler that converts JavaScript code into high-performance, standard C++23.",
5
5
  "main": "dist/index.js",
6
6
  "module": "src/index.ts",
@@ -13,6 +13,7 @@
13
13
  "src/prelude"
14
14
  ],
15
15
  "scripts": {
16
+ "postinstall": "bun run scripts/setup-compiler.ts",
16
17
  "dev": "bun run src/cli.ts",
17
18
  "typecheck": "tsc --noEmit",
18
19
  "precompile": "bun run scripts/precompile-headers.ts",
@@ -50,6 +50,7 @@ namespace jspp
50
50
  Promise = 11,
51
51
  DataDescriptor = 12,
52
52
  AccessorDescriptor = 13,
53
+ AsyncIterator = 14,
53
54
  };
54
55
 
55
56
  // The variant order MUST match JsType
@@ -67,7 +68,8 @@ namespace jspp
67
68
  std::shared_ptr<JsSymbol>,
68
69
  std::shared_ptr<JsPromise>,
69
70
  std::shared_ptr<DataDescriptor>,
70
- std::shared_ptr<AccessorDescriptor>>;
71
+ std::shared_ptr<AccessorDescriptor>,
72
+ std::shared_ptr<JsAsyncIterator<AnyValue>>>;
71
73
 
72
74
  class AnyValue
73
75
  {
@@ -329,6 +331,12 @@ namespace jspp
329
331
  v.storage = std::shared_ptr<JsIterator<AnyValue>>(iterator, [](JsIterator<AnyValue> *) {});
330
332
  return v;
331
333
  }
334
+ static AnyValue from_async_iterator(JsAsyncIterator<AnyValue> &&iterator) noexcept
335
+ {
336
+ AnyValue v;
337
+ v.storage = std::make_shared<JsAsyncIterator<AnyValue>>(std::move(iterator));
338
+ return v;
339
+ }
332
340
 
333
341
  // PROPERTY RESOLUTION HELPERS ---------------------------------------
334
342
  static AnyValue resolve_property_for_read(const AnyValue &val, const AnyValue &thisVal, const std::string &propName) noexcept
@@ -411,6 +419,7 @@ namespace jspp
411
419
  bool is_uninitialized() const noexcept { return storage.index() == 2; }
412
420
  bool is_data_descriptor() const noexcept { return storage.index() == 12; }
413
421
  bool is_accessor_descriptor() const noexcept { return storage.index() == 13; }
422
+ bool is_async_iterator() const noexcept { return storage.index() == 14; }
414
423
  bool is_generator() const noexcept { return is_function() && as_function()->is_generator; }
415
424
 
416
425
  // --- TYPE CASTERS
@@ -450,6 +459,10 @@ namespace jspp
450
459
  {
451
460
  return std::get<std::shared_ptr<JsIterator<AnyValue>>>(storage);
452
461
  }
462
+ std::shared_ptr<JsAsyncIterator<AnyValue>> as_async_iterator() const
463
+ {
464
+ return std::get<std::shared_ptr<JsAsyncIterator<AnyValue>>>(storage);
465
+ }
453
466
  DataDescriptor *as_data_descriptor() const noexcept
454
467
  {
455
468
  return std::get<std::shared_ptr<DataDescriptor>>(storage).get();
@@ -473,6 +486,10 @@ namespace jspp
473
486
  AnyValue set_own_property(const std::string &key, const AnyValue &value) const;
474
487
  AnyValue set_own_property(uint32_t idx, const AnyValue &value) const;
475
488
  AnyValue set_own_property(const AnyValue &key, const AnyValue &value) const;
489
+ // for calling the gotten the property
490
+ AnyValue call_own_property(const std::string &key, std::span<const AnyValue> args) const;
491
+ AnyValue call_own_property(uint32_t idx, std::span<const AnyValue> args) const;
492
+ AnyValue call_own_property(const AnyValue &key, std::span<const AnyValue> args) const;
476
493
 
477
494
  // --- DEFINERS (Object.defineProperty semantics)
478
495
  void define_data_property(const std::string &key, const AnyValue &value);
@@ -490,6 +507,15 @@ namespace jspp
490
507
  std::string to_std_string() const;
491
508
  };
492
509
 
510
+ // Awaiter for AnyValue
511
+ struct AnyValueAwaiter
512
+ {
513
+ AnyValue value; // Held by value
514
+ bool await_ready();
515
+ void await_suspend(std::coroutine_handle<> h);
516
+ AnyValue await_resume();
517
+ };
518
+
493
519
  // Inline implementation of operator co_await
494
520
  inline auto AnyValue::operator co_await() const
495
521
  {