@ugo-studio/jspp 0.3.2 → 0.3.3

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.
@@ -247,6 +247,17 @@ export function visitPrefixUnaryExpression(node, context) {
247
247
  const operand = this.visit(prefixUnaryExpr.operand, context);
248
248
  const operator = ts.tokenToString(prefixUnaryExpr.operator);
249
249
  if (operator === "++" || operator === "--") {
250
+ const opFunc = operator === "++" ? "add" : "sub";
251
+ if (ts.isPropertyAccessExpression(prefixUnaryExpr.operand)) {
252
+ const obj = this.visit(prefixUnaryExpr.operand.expression, context);
253
+ const prop = visitObjectPropertyName.call(this, prefixUnaryExpr.operand.name, context);
254
+ return `${obj}.set_own_property(${prop}, jspp::${opFunc}(${obj}.get_own_property(${prop}), 1.0))`;
255
+ }
256
+ else if (ts.isElementAccessExpression(prefixUnaryExpr.operand)) {
257
+ const obj = this.visit(prefixUnaryExpr.operand.expression, context);
258
+ const prop = visitObjectPropertyName.call(this, prefixUnaryExpr.operand.argumentExpression, { ...context, isBracketNotationPropertyAccess: true });
259
+ return `${obj}.set_own_property(${prop}, jspp::${opFunc}(${obj}.get_own_property(${prop}), 1.0))`;
260
+ }
250
261
  let target = operand;
251
262
  if (ts.isIdentifier(prefixUnaryExpr.operand)) {
252
263
  const scope = this.getScopeForNode(prefixUnaryExpr.operand);
@@ -289,6 +300,20 @@ export function visitPostfixUnaryExpression(node, context) {
289
300
  const postfixUnaryExpr = node;
290
301
  const operand = this.visit(postfixUnaryExpr.operand, context);
291
302
  const operator = ts.tokenToString(postfixUnaryExpr.operator);
303
+ if (ts.isPropertyAccessExpression(postfixUnaryExpr.operand)) {
304
+ const obj = this.visit(postfixUnaryExpr.operand.expression, context);
305
+ const prop = visitObjectPropertyName.call(this, postfixUnaryExpr.operand.name, context);
306
+ const opFunc = operator === "++" ? "add" : "sub";
307
+ // Postfix needs IILE to return old value
308
+ return `([&]() { auto oldVal = ${obj}.get_own_property(${prop}); ${obj}.set_own_property(${prop}, jspp::${opFunc}(oldVal, 1.0)); return oldVal; })()`;
309
+ }
310
+ else if (ts.isElementAccessExpression(postfixUnaryExpr.operand)) {
311
+ const obj = this.visit(postfixUnaryExpr.operand.expression, context);
312
+ const prop = visitObjectPropertyName.call(this, postfixUnaryExpr.operand.argumentExpression, { ...context, isBracketNotationPropertyAccess: true });
313
+ const opFunc = operator === "++" ? "add" : "sub";
314
+ // Postfix needs IILE to return old value
315
+ return `([&]() { auto oldVal = ${obj}.get_own_property(${prop}); ${obj}.set_own_property(${prop}, jspp::${opFunc}(oldVal, 1.0)); return oldVal; })()`;
316
+ }
292
317
  let target = operand;
293
318
  if (ts.isIdentifier(postfixUnaryExpr.operand)) {
294
319
  const scope = this.getScopeForNode(postfixUnaryExpr.operand);
@@ -402,42 +427,36 @@ export function visitBinaryExpression(node, context) {
402
427
  if (assignmentOperators.includes(opToken.kind)) {
403
428
  if (opToken.kind ===
404
429
  ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken) {
405
- const leftText = this.visit(binExpr.left, visitContext);
406
430
  const rightText = this.visit(binExpr.right, visitContext);
407
- let target = leftText;
408
431
  if (ts.isIdentifier(binExpr.left)) {
432
+ const leftText = this.visit(binExpr.left, visitContext);
409
433
  const scope = this.getScopeForNode(binExpr.left);
410
434
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.left.text, scope);
411
- target = typeInfo?.needsHeapAllocation
435
+ const target = typeInfo?.needsHeapAllocation
412
436
  ? `*${leftText}`
413
437
  : leftText;
414
- return `${target} = jspp::unsigned_right_shift(${target}, ${rightText})`;
438
+ return `jspp::unsigned_right_shift_assign(${target}, ${rightText})`;
415
439
  }
416
- }
417
- if (opToken.kind === ts.SyntaxKind.AsteriskAsteriskEqualsToken) {
418
- const leftText = this.visit(binExpr.left, visitContext);
419
- const rightText = this.visit(binExpr.right, visitContext);
420
- let target = leftText;
421
- if (ts.isIdentifier(binExpr.left)) {
422
- const scope = this.getScopeForNode(binExpr.left);
423
- const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.left.text, scope);
424
- target = typeInfo?.needsHeapAllocation
425
- ? `*${leftText}`
426
- : leftText;
427
- return `${target} = jspp::pow(${target}, ${rightText})`;
440
+ else if (ts.isPropertyAccessExpression(binExpr.left)) {
441
+ const obj = this.visit(binExpr.left.expression, context);
442
+ const prop = visitObjectPropertyName.call(this, binExpr.left.name, context);
443
+ return `${obj}.set_own_property(${prop}, jspp::unsigned_right_shift(${obj}.get_own_property(${prop}), ${rightText}))`;
444
+ }
445
+ else if (ts.isElementAccessExpression(binExpr.left)) {
446
+ const obj = this.visit(binExpr.left.expression, context);
447
+ const prop = visitObjectPropertyName.call(this, binExpr.left.argumentExpression, { ...context, isBracketNotationPropertyAccess: true });
448
+ return `${obj}.set_own_property(${prop}, jspp::unsigned_right_shift(${obj}.get_own_property(${prop}), ${rightText}))`;
428
449
  }
429
- // For complex LHS, we need a different approach, but this is a start.
430
450
  }
431
451
  if (opToken.kind === ts.SyntaxKind.AmpersandAmpersandEqualsToken ||
432
452
  opToken.kind === ts.SyntaxKind.BarBarEqualsToken ||
433
453
  opToken.kind === ts.SyntaxKind.QuestionQuestionEqualsToken) {
434
- const leftText = this.visit(binExpr.left, visitContext);
435
454
  const rightText = this.visit(binExpr.right, visitContext);
436
- let target = leftText;
437
455
  if (ts.isIdentifier(binExpr.left)) {
456
+ const leftText = this.visit(binExpr.left, visitContext);
438
457
  const scope = this.getScopeForNode(binExpr.left);
439
458
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.left.text, scope);
440
- target = typeInfo?.needsHeapAllocation
459
+ const target = typeInfo?.needsHeapAllocation
441
460
  ? `*${leftText}`
442
461
  : leftText;
443
462
  if (opToken.kind === ts.SyntaxKind.AmpersandAmpersandEqualsToken) {
@@ -450,6 +469,29 @@ export function visitBinaryExpression(node, context) {
450
469
  return `jspp::nullish_coalesce_assign(${target}, ${rightText})`;
451
470
  }
452
471
  }
472
+ else if (ts.isPropertyAccessExpression(binExpr.left)) {
473
+ const obj = this.visit(binExpr.left.expression, context);
474
+ const prop = visitObjectPropertyName.call(this, binExpr.left.name, context);
475
+ const func = opToken.kind ===
476
+ ts.SyntaxKind.AmpersandAmpersandEqualsToken
477
+ ? "logical_and"
478
+ : (opToken.kind === ts.SyntaxKind.BarBarEqualsToken
479
+ ? "logical_or"
480
+ : "nullish_coalesce");
481
+ // Logical assignments return newVal, set_own_property returns newVal.
482
+ return `${obj}.set_own_property(${prop}, jspp::${func}(${obj}.get_own_property(${prop}), ${rightText}))`;
483
+ }
484
+ else if (ts.isElementAccessExpression(binExpr.left)) {
485
+ const obj = this.visit(binExpr.left.expression, context);
486
+ const prop = visitObjectPropertyName.call(this, binExpr.left.argumentExpression, { ...context, isBracketNotationPropertyAccess: true });
487
+ const func = opToken.kind ===
488
+ ts.SyntaxKind.AmpersandAmpersandEqualsToken
489
+ ? "logical_and"
490
+ : (opToken.kind === ts.SyntaxKind.BarBarEqualsToken
491
+ ? "logical_or"
492
+ : "nullish_coalesce");
493
+ return `${obj}.set_own_property(${prop}, jspp::${func}(${obj}.get_own_property(${prop}), ${rightText}))`;
494
+ }
453
495
  }
454
496
  const leftText = this.visit(binExpr.left, visitContext);
455
497
  let rightText = ts.isNumericLiteral(binExpr.right)
@@ -460,6 +502,35 @@ export function visitBinaryExpression(node, context) {
460
502
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.right.getText(), scope);
461
503
  rightText = this.getDerefCode(rightText, this.getJsVarName(binExpr.right), visitContext, typeInfo);
462
504
  }
505
+ if (ts.isPropertyAccessExpression(binExpr.left)) {
506
+ const obj = this.visit(binExpr.left.expression, context);
507
+ const prop = visitObjectPropertyName.call(this, binExpr.left.name, context);
508
+ const opBase = op.slice(0, -1); // "+=", "-=" -> "+", "-"
509
+ const opFunc = opBase === "+" ? "add" : (opBase === "-" ? "sub" : (opBase === "*" ? "mul" : (opBase === "/" ? "div" : (opBase === "%" ? "mod" : (opBase === "&" ? "bitwise_and" : (opBase === "|" ? "bitwise_or" : (opBase === "^" ? "bitwise_xor" : (opBase === "<<" ? "left_shift" : (opBase === ">>"
510
+ ? "right_shift"
511
+ : "")))))))));
512
+ if (opFunc) {
513
+ return `${obj}.set_own_property(${prop}, jspp::${opFunc}(${obj}.get_own_property(${prop}), ${rightText}))`;
514
+ }
515
+ else {
516
+ // Fallback to IILE if we don't have an opFunc mapping
517
+ return `([&]() { auto val = ${obj}.get_own_property(${prop}); val ${op} ${rightText}; ${obj}.set_own_property(${prop}, val); return val; })()`;
518
+ }
519
+ }
520
+ else if (ts.isElementAccessExpression(binExpr.left)) {
521
+ const obj = this.visit(binExpr.left.expression, context);
522
+ const prop = visitObjectPropertyName.call(this, binExpr.left.argumentExpression, { ...context, isBracketNotationPropertyAccess: true });
523
+ const opBase = op.slice(0, -1);
524
+ const opFunc = opBase === "+" ? "add" : (opBase === "-" ? "sub" : (opBase === "*" ? "mul" : (opBase === "/" ? "div" : (opBase === "%" ? "mod" : (opBase === "&" ? "bitwise_and" : (opBase === "|" ? "bitwise_or" : (opBase === "^" ? "bitwise_xor" : (opBase === "<<" ? "left_shift" : (opBase === ">>"
525
+ ? "right_shift"
526
+ : "")))))))));
527
+ if (opFunc) {
528
+ return `${obj}.set_own_property(${prop}, jspp::${opFunc}(${obj}.get_own_property(${prop}), ${rightText}))`;
529
+ }
530
+ else {
531
+ return `([&]() { auto val = ${obj}.get_own_property(${prop}); val ${op} ${rightText}; ${obj}.set_own_property(${prop}, val); return val; })()`;
532
+ }
533
+ }
463
534
  let target = leftText;
464
535
  if (ts.isIdentifier(binExpr.left)) {
465
536
  const scope = this.getScopeForNode(binExpr.left);
@@ -1266,7 +1337,7 @@ export function visitTypeOfExpression(node, context) {
1266
1337
  derefExpr = this.getDerefCode(exprText, this.getJsVarName(typeOfExpr.expression), context, typeInfo);
1267
1338
  }
1268
1339
  }
1269
- return `jspp::Access::type_of(${derefExpr})`;
1340
+ return `jspp::type_of(${derefExpr})`;
1270
1341
  }
1271
1342
  export function visitAwaitExpression(node, context) {
1272
1343
  const awaitExpr = node;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ugo-studio/jspp",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
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",
@@ -14,7 +14,7 @@
14
14
  "scripts"
15
15
  ],
16
16
  "scripts": {
17
- "postinstall": "bun run scripts/setup-compiler.ts && bun run scripts/setup-emsdk.ts && bun run scripts/precompile-headers.ts",
17
+ "postinstall": "bun run scripts/setup-compiler.ts && bun run scripts/setup-emsdk.ts && bun run scripts/precompile-headers.ts --mode debug",
18
18
  "dev": "bun run src/cli/index.ts",
19
19
  "typecheck": "tsc --noEmit",
20
20
  "test": "bun test",
@@ -112,6 +112,14 @@ if (process.platform === "win32") {
112
112
  MODES[1].flags.push("-Wa,-mbig-obj");
113
113
  }
114
114
 
115
+ const pkgDir = path.dirname(import.meta.dirname);
116
+ const emsdkEnv = {
117
+ ...process.env,
118
+ PATH: `${path.join(pkgDir, ".emsdk")}${path.delimiter}${
119
+ path.join(pkgDir, ".emsdk", "upstream", "emscripten")
120
+ }${path.delimiter}${process.env.PATH}`,
121
+ };
122
+
115
123
  async function getLatestMtime(
116
124
  dirPath: string,
117
125
  filter?: (name: string) => boolean,
@@ -146,11 +154,11 @@ async function findCppFiles(dir: string): Promise<string[]> {
146
154
  }
147
155
 
148
156
  async function runCommand(cmd: string, args: string[]): Promise<boolean> {
149
- // console.log(`${COLORS.dim}> ${cmd} ${args.join(" ")}${COLORS.reset}`);
150
157
  return new Promise((resolve) => {
151
158
  const proc = spawn(cmd, args, {
152
159
  stdio: "inherit",
153
160
  shell: process.platform === "win32",
161
+ env: emsdkEnv,
154
162
  });
155
163
  proc.on("close", (code) => resolve(code === 0));
156
164
  });
@@ -48,40 +48,6 @@ namespace jspp
48
48
  return var;
49
49
  }
50
50
 
51
- inline AnyValue type_of(const std::optional<AnyValue> &val = std::nullopt)
52
- {
53
- if (!val.has_value())
54
- return AnyValue::make_string("undefined");
55
-
56
- switch (val.value().get_type())
57
- {
58
- case JsType::Undefined:
59
- return AnyValue::make_string("undefined");
60
- case JsType::Null:
61
- return AnyValue::make_string("object");
62
- case JsType::Boolean:
63
- return AnyValue::make_string("boolean");
64
- case JsType::Number:
65
- return AnyValue::make_string("number");
66
- case JsType::String:
67
- return AnyValue::make_string("string");
68
- case JsType::Symbol:
69
- return AnyValue::make_string("symbol");
70
- case JsType::Function:
71
- return AnyValue::make_string("function");
72
- case JsType::Object:
73
- return AnyValue::make_string("object");
74
- case JsType::Array:
75
- return AnyValue::make_string("object");
76
- case JsType::Iterator:
77
- return AnyValue::make_string("object");
78
- case JsType::AsyncIterator:
79
- return AnyValue::make_string("object");
80
- default:
81
- return AnyValue::make_string("undefined");
82
- }
83
- }
84
-
85
51
  // Helper function to get enumerable own property keys/values of an object
86
52
  inline std::vector<AnyValue> get_object_keys(const AnyValue &obj, bool include_symbols = false)
87
53
  {
@@ -39,6 +39,42 @@ namespace jspp
39
39
  return lhs;
40
40
  }
41
41
 
42
+ inline AnyValue &operator&=(AnyValue &lhs, const AnyValue &rhs)
43
+ {
44
+ lhs = jspp::bitwise_and(lhs, rhs);
45
+ return lhs;
46
+ }
47
+
48
+ inline AnyValue &operator|=(AnyValue &lhs, const AnyValue &rhs)
49
+ {
50
+ lhs = jspp::bitwise_or(lhs, rhs);
51
+ return lhs;
52
+ }
53
+
54
+ inline AnyValue &operator^=(AnyValue &lhs, const AnyValue &rhs)
55
+ {
56
+ lhs = jspp::bitwise_xor(lhs, rhs);
57
+ return lhs;
58
+ }
59
+
60
+ inline AnyValue &operator<<=(AnyValue &lhs, const AnyValue &rhs)
61
+ {
62
+ lhs = jspp::left_shift(lhs, rhs);
63
+ return lhs;
64
+ }
65
+
66
+ inline AnyValue &operator>>=(AnyValue &lhs, const AnyValue &rhs)
67
+ {
68
+ lhs = jspp::right_shift(lhs, rhs);
69
+ return lhs;
70
+ }
71
+
72
+ inline AnyValue &unsigned_right_shift_assign(AnyValue &lhs, const AnyValue &rhs)
73
+ {
74
+ lhs = jspp::unsigned_right_shift(lhs, rhs);
75
+ return lhs;
76
+ }
77
+
42
78
  inline AnyValue &operator++(AnyValue &val)
43
79
  {
44
80
  val = jspp::add(val, 1.0);
@@ -116,4 +152,64 @@ namespace jspp
116
152
  {
117
153
  return lhs %= static_cast<double>(rhs);
118
154
  }
155
+
156
+ inline AnyValue &operator&=(AnyValue &lhs, const double &rhs)
157
+ {
158
+ lhs = jspp::bitwise_and(lhs, rhs);
159
+ return lhs;
160
+ }
161
+ inline AnyValue &operator&=(AnyValue &lhs, const int &rhs)
162
+ {
163
+ return lhs &= static_cast<double>(rhs);
164
+ }
165
+
166
+ inline AnyValue &operator|=(AnyValue &lhs, const double &rhs)
167
+ {
168
+ lhs = jspp::bitwise_or(lhs, rhs);
169
+ return lhs;
170
+ }
171
+ inline AnyValue &operator|=(AnyValue &lhs, const int &rhs)
172
+ {
173
+ return lhs |= static_cast<double>(rhs);
174
+ }
175
+
176
+ inline AnyValue &operator^=(AnyValue &lhs, const double &rhs)
177
+ {
178
+ lhs = jspp::bitwise_xor(lhs, rhs);
179
+ return lhs;
180
+ }
181
+ inline AnyValue &operator^=(AnyValue &lhs, const int &rhs)
182
+ {
183
+ return lhs ^= static_cast<double>(rhs);
184
+ }
185
+
186
+ inline AnyValue &operator<<=(AnyValue &lhs, const double &rhs)
187
+ {
188
+ lhs = jspp::left_shift(lhs, rhs);
189
+ return lhs;
190
+ }
191
+ inline AnyValue &operator<<=(AnyValue &lhs, const int &rhs)
192
+ {
193
+ return lhs <<= static_cast<double>(rhs);
194
+ }
195
+
196
+ inline AnyValue &operator>>=(AnyValue &lhs, const double &rhs)
197
+ {
198
+ lhs = jspp::right_shift(lhs, rhs);
199
+ return lhs;
200
+ }
201
+ inline AnyValue &operator>>=(AnyValue &lhs, const int &rhs)
202
+ {
203
+ return lhs >>= static_cast<double>(rhs);
204
+ }
205
+
206
+ inline AnyValue &unsigned_right_shift_assign(AnyValue &lhs, const double &rhs)
207
+ {
208
+ lhs = jspp::unsigned_right_shift(lhs, rhs);
209
+ return lhs;
210
+ }
211
+ inline AnyValue &unsigned_right_shift_assign(AnyValue &lhs, const int &rhs)
212
+ {
213
+ return unsigned_right_shift_assign(lhs, static_cast<double>(rhs));
214
+ }
119
215
  }
@@ -11,6 +11,41 @@
11
11
 
12
12
  namespace jspp
13
13
  {
14
+ // --- TYPE OF ---
15
+ inline AnyValue type_of(const std::optional<AnyValue> &val = std::nullopt)
16
+ {
17
+ if (!val.has_value())
18
+ return AnyValue::make_string("undefined");
19
+
20
+ switch (val.value().get_type())
21
+ {
22
+ case JsType::Undefined:
23
+ return AnyValue::make_string("undefined");
24
+ case JsType::Null:
25
+ return AnyValue::make_string("object");
26
+ case JsType::Boolean:
27
+ return AnyValue::make_string("boolean");
28
+ case JsType::Number:
29
+ return AnyValue::make_string("number");
30
+ case JsType::String:
31
+ return AnyValue::make_string("string");
32
+ case JsType::Symbol:
33
+ return AnyValue::make_string("symbol");
34
+ case JsType::Function:
35
+ return AnyValue::make_string("function");
36
+ case JsType::Object:
37
+ return AnyValue::make_string("object");
38
+ case JsType::Array:
39
+ return AnyValue::make_string("object");
40
+ case JsType::Iterator:
41
+ return AnyValue::make_string("object");
42
+ case JsType::AsyncIterator:
43
+ return AnyValue::make_string("object");
44
+ default:
45
+ return AnyValue::make_string("undefined");
46
+ }
47
+ }
48
+
14
49
  // Operator === (returns boolean wrapped in AnyValue)
15
50
  inline const AnyValue is_strictly_equal_to(const AnyValue &lhs, const double &rhs) noexcept
16
51
  {
@@ -349,4 +384,4 @@ namespace jspp
349
384
  return lhs;
350
385
  return rhs;
351
386
  }
352
- }
387
+ }
@@ -2,6 +2,7 @@
2
2
 
3
3
  #include "types.hpp"
4
4
  #include "any_value.hpp"
5
+ #include "exception.hpp"
5
6
  #include <cstdint> // For int32_t
6
7
  #include <cmath> // For fmod, isnan, isinf, floor, abs, pow
7
8
  #include <string> // For std::to_string, std::stod
@@ -109,6 +110,16 @@ namespace jspp
109
110
  }
110
111
  }
111
112
 
113
+ // --- UNARY NATIVE ---
114
+ inline double plus_native(const AnyValue &val) { return Operators_Private::ToNumber(val); }
115
+ inline double plus_native(double val) { return val; }
116
+ inline double negate_native(const AnyValue &val) { return -Operators_Private::ToNumber(val); }
117
+ inline double negate_native(double val) { return -val; }
118
+ inline double bitwise_not_native(const AnyValue &val) { return static_cast<double>(~Operators_Private::ToInt32(val)); }
119
+ inline double bitwise_not_native(double val) { return static_cast<double>(~static_cast<int32_t>(val)); }
120
+ inline bool logical_not_native(const AnyValue &val) { return !is_truthy(val); }
121
+ inline bool logical_not_native(double val) { return !is_truthy(val); }
122
+
112
123
  // --- BASIC EQUALITY ---
113
124
 
114
125
  // Operator === (returns primitive boolean)
@@ -346,4 +357,4 @@ namespace jspp
346
357
  inline double unsigned_right_shift_native(const AnyValue &lhs, const double &rhs) { return unsigned_right_shift_native(Operators_Private::ToUint32(lhs), rhs); }
347
358
  inline double unsigned_right_shift_native(const double &lhs, const AnyValue &rhs) { return unsigned_right_shift_native(lhs, Operators_Private::ToInt32(rhs)); }
348
359
 
349
- }
360
+ }
@@ -2,210 +2,251 @@
2
2
  #include "values/iterator.hpp"
3
3
  #include "values/prototypes/iterator.hpp"
4
4
 
5
- namespace jspp {
6
-
7
- template <typename T>
8
- JsIterator<T>::JsIterator(handle_type h) : handle(h) {}
9
-
10
- template <typename T>
11
- JsIterator<T>::JsIterator(JsIterator &&other) noexcept
12
- : handle(std::exchange(other.handle, nullptr)),
13
- props(std::move(other.props)),
14
- symbol_props(std::move(other.symbol_props)) {}
15
-
16
- template <typename T>
17
- JsIterator<T>::~JsIterator() { if (handle) handle.destroy(); }
18
-
19
- template <typename T>
20
- JsIterator<T> JsIterator<T>::promise_type::get_return_object() {
21
- return JsIterator{std::coroutine_handle<promise_type>::from_promise(*this)};
22
- }
23
-
24
- template <typename T>
25
- std::suspend_always JsIterator<T>::promise_type::initial_suspend() noexcept { return {}; }
26
-
27
- template <typename T>
28
- std::suspend_always JsIterator<T>::promise_type::final_suspend() noexcept { return {}; }
29
-
30
- template <typename T>
31
- void JsIterator<T>::promise_type::unhandled_exception() {
32
- try {
33
- throw;
34
- } catch (const GeneratorReturnException&) {
35
- } catch (...) {
36
- exception_ = std::current_exception();
5
+ namespace jspp
6
+ {
7
+
8
+ template <typename T>
9
+ JsIterator<T>::JsIterator(handle_type h) : handle(h) {}
10
+
11
+ template <typename T>
12
+ JsIterator<T>::JsIterator(JsIterator &&other) noexcept
13
+ : handle(std::exchange(other.handle, nullptr)),
14
+ props(std::move(other.props)),
15
+ symbol_props(std::move(other.symbol_props)) {}
16
+
17
+ template <typename T>
18
+ JsIterator<T>::~JsIterator()
19
+ {
20
+ if (handle)
21
+ handle.destroy();
37
22
  }
38
- }
39
23
 
40
- template <typename T>
41
- std::string JsIterator<T>::to_std_string() const { return "[object Generator]"; }
24
+ template <typename T>
25
+ JsIterator<T> JsIterator<T>::promise_type::get_return_object()
26
+ {
27
+ return JsIterator{std::coroutine_handle<promise_type>::from_promise(*this)};
28
+ }
42
29
 
43
- template <typename T>
44
- typename JsIterator<T>::NextResult JsIterator<T>::next(const T &val)
45
- {
46
- if (!handle || handle.done()) return {std::nullopt, true};
47
- handle.promise().input_value = val;
48
- handle.resume();
49
- if (handle.promise().exception_) std::rethrow_exception(handle.promise().exception_);
50
- bool is_done = handle.done();
51
- return {handle.promise().current_value, is_done};
52
- }
53
-
54
- template <typename T>
55
- typename JsIterator<T>::NextResult JsIterator<T>::return_(const T &val)
56
- {
57
- if (!handle || handle.done()) return {val, true};
58
- handle.promise().pending_return = true;
59
- handle.promise().current_value = val;
60
- handle.resume();
61
- if (handle.promise().exception_) std::rethrow_exception(handle.promise().exception_);
62
- return {handle.promise().current_value, true};
63
- }
64
-
65
- template <typename T>
66
- typename JsIterator<T>::NextResult JsIterator<T>::throw_(const AnyValue &err)
67
- {
68
- if (!handle || handle.done()) throw Exception(err);
69
- handle.promise().pending_exception = std::make_exception_ptr(Exception(err));
70
- handle.resume();
71
- if (handle.promise().exception_) std::rethrow_exception(handle.promise().exception_);
72
- bool is_done = handle.done();
73
- return {handle.promise().current_value, is_done};
74
- }
75
-
76
- template <typename T>
77
- std::vector<T> JsIterator<T>::to_vector()
78
- {
79
- std::vector<T> result;
80
- while (true) {
81
- auto next_res = this->next();
82
- if (next_res.done) break;
83
- result.push_back(next_res.value.value_or(Constants::UNDEFINED));
30
+ template <typename T>
31
+ std::suspend_always JsIterator<T>::promise_type::initial_suspend() noexcept { return {}; }
32
+
33
+ template <typename T>
34
+ std::suspend_always JsIterator<T>::promise_type::final_suspend() noexcept { return {}; }
35
+
36
+ template <typename T>
37
+ void JsIterator<T>::promise_type::unhandled_exception()
38
+ {
39
+ try
40
+ {
41
+ throw;
42
+ }
43
+ catch (const GeneratorReturnException &)
44
+ {
45
+ }
46
+ catch (...)
47
+ {
48
+ exception_ = std::current_exception();
49
+ }
84
50
  }
85
- return result;
86
- }
87
51
 
88
- template <typename T>
89
- bool JsIterator<T>::has_symbol_property(const AnyValue &key) const { return symbol_props.count(key) > 0; }
52
+ template <typename T>
53
+ std::string JsIterator<T>::to_std_string() const { return "[object Generator]"; }
54
+
55
+ template <typename T>
56
+ typename JsIterator<T>::NextResult JsIterator<T>::next(const T &val)
57
+ {
58
+ if (!handle || handle.done())
59
+ return {std::nullopt, true};
60
+ handle.promise().input_value = val;
61
+ handle.resume();
62
+ if (handle.promise().exception_)
63
+ std::rethrow_exception(handle.promise().exception_);
64
+ bool is_done = handle.done();
65
+ return {handle.promise().current_value, is_done};
66
+ }
90
67
 
91
- template <typename T>
92
- AnyValue JsIterator<T>::get_property(const std::string &key, AnyValue thisVal)
93
- {
94
- auto it = props.find(key);
95
- if (it == props.end()) {
96
- if constexpr (std::is_same_v<T, AnyValue>) {
97
- auto proto_it = IteratorPrototypes::get(key);
98
- if (proto_it.has_value()) return AnyValue::resolve_property_for_read(proto_it.value(), thisVal, key);
68
+ template <typename T>
69
+ typename JsIterator<T>::NextResult JsIterator<T>::return_(const T &val)
70
+ {
71
+ if (!handle || handle.done())
72
+ return {val, true};
73
+ handle.promise().pending_return = true;
74
+ handle.promise().current_value = val;
75
+ handle.resume();
76
+ if (handle.promise().exception_)
77
+ std::rethrow_exception(handle.promise().exception_);
78
+ return {handle.promise().current_value, true};
79
+ }
80
+
81
+ template <typename T>
82
+ typename JsIterator<T>::NextResult JsIterator<T>::throw_(const AnyValue &err)
83
+ {
84
+ if (!handle || handle.done())
85
+ throw Exception(err);
86
+ handle.promise().pending_exception = std::make_exception_ptr(Exception(err));
87
+ handle.resume();
88
+ if (handle.promise().exception_)
89
+ std::rethrow_exception(handle.promise().exception_);
90
+ bool is_done = handle.done();
91
+ return {handle.promise().current_value, is_done};
92
+ }
93
+
94
+ template <typename T>
95
+ std::vector<T> JsIterator<T>::to_vector()
96
+ {
97
+ std::vector<T> result;
98
+ while (true)
99
+ {
100
+ auto next_res = this->next();
101
+ if (next_res.done)
102
+ break;
103
+ result.push_back(next_res.value.value_or(Constants::UNDEFINED));
99
104
  }
100
- return Constants::UNDEFINED;
105
+ return result;
101
106
  }
102
- return AnyValue::resolve_property_for_read(it->second, thisVal, key);
103
- }
104
107
 
105
- template <typename T>
106
- AnyValue JsIterator<T>::get_symbol_property(const AnyValue &key, AnyValue thisVal)
107
- {
108
- auto it = symbol_props.find(key);
109
- if (it == symbol_props.end()) {
110
- if constexpr (std::is_same_v<T, AnyValue>) {
111
- auto proto_it = IteratorPrototypes::get(key);
112
- if (proto_it.has_value()) return AnyValue::resolve_property_for_read(proto_it.value(), thisVal, key.to_std_string());
108
+ template <typename T>
109
+ bool JsIterator<T>::has_symbol_property(const AnyValue &key) const { return symbol_props.count(key) > 0; }
110
+
111
+ template <typename T>
112
+ AnyValue JsIterator<T>::get_property(const std::string &key, AnyValue thisVal)
113
+ {
114
+ auto it = props.find(key);
115
+ if (it == props.end())
116
+ {
117
+ if constexpr (std::is_same_v<T, AnyValue>)
118
+ {
119
+ auto proto_it = IteratorPrototypes::get(key);
120
+ if (proto_it.has_value())
121
+ return AnyValue::resolve_property_for_read(proto_it.value(), thisVal, key);
122
+ }
123
+ return Constants::UNDEFINED;
113
124
  }
114
- return Constants::UNDEFINED;
125
+ return AnyValue::resolve_property_for_read(it->second, thisVal, key);
115
126
  }
116
- return AnyValue::resolve_property_for_read(it->second, thisVal, key.to_std_string());
117
- }
118
127
 
119
- template <typename T>
120
- AnyValue JsIterator<T>::set_property(const std::string &key, AnyValue value, AnyValue thisVal)
121
- {
122
- if constexpr (std::is_same_v<T, AnyValue>) {
123
- auto proto_it = IteratorPrototypes::get(key);
124
- if (proto_it.has_value()) {
125
- auto proto_value = proto_it.value();
126
- if (proto_value.is_accessor_descriptor()) return AnyValue::resolve_property_for_write(proto_value, thisVal, value, key);
127
- if (proto_value.is_data_descriptor() && !proto_value.as_data_descriptor()->writable) return AnyValue::resolve_property_for_write(proto_value, thisVal, value, key);
128
+ template <typename T>
129
+ AnyValue JsIterator<T>::get_symbol_property(const AnyValue &key, AnyValue thisVal)
130
+ {
131
+ auto it = symbol_props.find(key);
132
+ if (it == symbol_props.end())
133
+ {
134
+ if constexpr (std::is_same_v<T, AnyValue>)
135
+ {
136
+ auto proto_it = IteratorPrototypes::get(key);
137
+ if (proto_it.has_value())
138
+ return AnyValue::resolve_property_for_read(proto_it.value(), thisVal, key.to_std_string());
139
+ }
140
+ return Constants::UNDEFINED;
128
141
  }
142
+ return AnyValue::resolve_property_for_read(it->second, thisVal, key.to_std_string());
129
143
  }
130
- auto it = props.find(key);
131
- if (it != props.end()) return jspp::AnyValue::resolve_property_for_write(it->second, thisVal, value, key);
132
- else { props[key] = value; return value; }
133
- }
134
144
 
135
- template <typename T>
136
- AnyValue JsIterator<T>::set_symbol_property(const AnyValue &key, AnyValue value, AnyValue thisVal)
137
- {
138
- auto it = symbol_props.find(key);
139
- if (it != symbol_props.end()) return AnyValue::resolve_property_for_write(it->second, thisVal, value, key.to_std_string());
140
- else { symbol_props[key] = value; return value; }
141
- }
145
+ template <typename T>
146
+ AnyValue JsIterator<T>::set_property(const std::string &key, AnyValue value, AnyValue thisVal)
147
+ {
148
+ if constexpr (std::is_same_v<T, AnyValue>)
149
+ {
150
+ auto proto_it = IteratorPrototypes::get(key);
151
+ if (proto_it.has_value())
152
+ {
153
+ auto proto_value = proto_it.value();
154
+ if (proto_value.is_accessor_descriptor())
155
+ return AnyValue::resolve_property_for_write(proto_value, thisVal, value, key);
156
+ if (proto_value.is_data_descriptor() && !proto_value.as_data_descriptor()->writable)
157
+ return AnyValue::resolve_property_for_write(proto_value, thisVal, value, key);
158
+ }
159
+ }
160
+ auto it = props.find(key);
161
+ if (it != props.end())
162
+ return jspp::AnyValue::resolve_property_for_write(it->second, thisVal, value, key);
163
+ else
164
+ {
165
+ props[key] = value;
166
+ return value;
167
+ }
168
+ }
142
169
 
143
- // Explicit template instantiation
144
- template class JsIterator<AnyValue>;
170
+ template <typename T>
171
+ AnyValue JsIterator<T>::set_symbol_property(const AnyValue &key, AnyValue value, AnyValue thisVal)
172
+ {
173
+ auto it = symbol_props.find(key);
174
+ if (it != symbol_props.end())
175
+ return AnyValue::resolve_property_for_write(it->second, thisVal, value, key.to_std_string());
176
+ else
177
+ {
178
+ symbol_props[key] = value;
179
+ return value;
180
+ }
181
+ }
145
182
 
146
- namespace IteratorPrototypes {
183
+ // Explicit template instantiation
184
+ template class JsIterator<AnyValue>;
147
185
 
148
- AnyValue &get_toString_fn()
149
- {
150
- static AnyValue fn = AnyValue::make_function([](const AnyValue &thisVal, std::span<const AnyValue>) -> AnyValue
151
- { return AnyValue::make_string(thisVal.as_iterator()->to_std_string()); },
152
- "toString");
153
- return fn;
154
- }
186
+ namespace IteratorPrototypes
187
+ {
155
188
 
156
- AnyValue &get_iterator_fn()
157
- {
158
- static AnyValue fn = AnyValue::make_generator([](const AnyValue &thisVal, std::span<const AnyValue>) -> AnyValue
159
- { return thisVal; },
160
- "Symbol.iterator");
161
- return fn;
162
- }
189
+ AnyValue &get_toString_fn()
190
+ {
191
+ static AnyValue fn = AnyValue::make_function([](const AnyValue &thisVal, std::span<const AnyValue>) -> AnyValue
192
+ { return AnyValue::make_string(thisVal.as_iterator()->to_std_string()); },
193
+ "toString");
194
+ return fn;
195
+ }
163
196
 
164
- AnyValue &get_next_fn()
165
- {
166
- static AnyValue fn = AnyValue::make_function([](const AnyValue &thisVal, std::span<const AnyValue> args) -> AnyValue
167
- {
197
+ AnyValue &get_iterator_fn()
198
+ {
199
+ static AnyValue fn = AnyValue::make_generator([](const AnyValue &thisVal, std::span<const AnyValue>) -> AnyValue
200
+ { return thisVal; },
201
+ "Symbol.iterator");
202
+ return fn;
203
+ }
204
+
205
+ AnyValue &get_next_fn()
206
+ {
207
+ static AnyValue fn = AnyValue::make_function([](const AnyValue &thisVal, std::span<const AnyValue> args) -> AnyValue
208
+ {
168
209
  AnyValue val = args.empty() ? Constants::UNDEFINED : args[0];
169
210
  auto res = thisVal.as_iterator()->next(val);
170
211
  return AnyValue::make_object({{"value", res.value.value_or(Constants::UNDEFINED)}, {"done", AnyValue::make_boolean(res.done)}}); },
171
- "next");
172
- return fn;
173
- }
212
+ "next");
213
+ return fn;
214
+ }
174
215
 
175
- AnyValue &get_return_fn()
176
- {
177
- static AnyValue fn = AnyValue::make_function([](const AnyValue &thisVal, std::span<const AnyValue> args) -> AnyValue
178
- {
216
+ AnyValue &get_return_fn()
217
+ {
218
+ static AnyValue fn = AnyValue::make_function([](const AnyValue &thisVal, std::span<const AnyValue> args) -> AnyValue
219
+ {
179
220
  AnyValue val = args.empty() ? Constants::UNDEFINED : args[0];
180
221
  auto res = thisVal.as_iterator()->return_(val);
181
222
  return AnyValue::make_object({{"value", res.value.value_or(Constants::UNDEFINED)}, {"done", AnyValue::make_boolean(res.done)}}); },
182
- "return");
183
- return fn;
184
- }
223
+ "return");
224
+ return fn;
225
+ }
185
226
 
186
- AnyValue &get_throw_fn()
187
- {
188
- static AnyValue fn = AnyValue::make_function([](const AnyValue &thisVal, std::span<const AnyValue> args) -> AnyValue
189
- {
227
+ AnyValue &get_throw_fn()
228
+ {
229
+ static AnyValue fn = AnyValue::make_function([](const AnyValue &thisVal, std::span<const AnyValue> args) -> AnyValue
230
+ {
190
231
  AnyValue err = args.empty() ? Constants::UNDEFINED : args[0];
191
232
  auto res = thisVal.as_iterator()->throw_(err);
192
233
  return AnyValue::make_object({{"value", res.value.value_or(Constants::UNDEFINED)}, {"done", AnyValue::make_boolean(res.done)}}); },
193
- "throw");
194
- return fn;
195
- }
234
+ "throw");
235
+ return fn;
236
+ }
196
237
 
197
- AnyValue &get_toArray_fn()
198
- {
199
- static AnyValue fn = AnyValue::make_function([](const AnyValue &thisVal, std::span<const AnyValue>) -> AnyValue
200
- { return AnyValue::make_array(thisVal.as_iterator()->to_vector()); },
201
- "toArray");
202
- return fn;
203
- }
238
+ AnyValue &get_toArray_fn()
239
+ {
240
+ static AnyValue fn = AnyValue::make_function([](const AnyValue &thisVal, std::span<const AnyValue>) -> AnyValue
241
+ { return AnyValue::make_array(thisVal.as_iterator()->to_vector()); },
242
+ "toArray");
243
+ return fn;
244
+ }
204
245
 
205
- AnyValue &get_drop_fn()
206
- {
207
- static AnyValue fn = AnyValue::make_generator([](AnyValue thisVal, std::vector<AnyValue> args) -> JsIterator<AnyValue>
208
- {
246
+ AnyValue &get_drop_fn()
247
+ {
248
+ static AnyValue fn = AnyValue::make_generator([](AnyValue thisVal, std::vector<AnyValue> args) -> JsIterator<AnyValue>
249
+ {
209
250
  auto self = thisVal.as_iterator();
210
251
  size_t skip_count = 0;
211
252
  if (!args.empty()) {
@@ -224,14 +265,14 @@ AnyValue &get_drop_fn()
224
265
  co_yield next_res.value.value_or(Constants::UNDEFINED);
225
266
  }
226
267
  co_return jspp::Constants::UNDEFINED; },
227
- "drop");
228
- return fn;
229
- }
268
+ "drop");
269
+ return fn;
270
+ }
230
271
 
231
- AnyValue &get_take_fn()
232
- {
233
- static AnyValue fn = AnyValue::make_generator([](AnyValue thisVal, std::vector<AnyValue> args) -> JsIterator<AnyValue>
234
- {
272
+ AnyValue &get_take_fn()
273
+ {
274
+ static AnyValue fn = AnyValue::make_generator([](AnyValue thisVal, std::vector<AnyValue> args) -> JsIterator<AnyValue>
275
+ {
235
276
  auto self = thisVal.as_iterator();
236
277
  size_t take_count = 0;
237
278
  if (!args.empty()) {
@@ -254,14 +295,14 @@ AnyValue &get_take_fn()
254
295
  }
255
296
  }
256
297
  co_return jspp::Constants::UNDEFINED; },
257
- "take");
258
- return fn;
259
- }
298
+ "take");
299
+ return fn;
300
+ }
260
301
 
261
- AnyValue &get_some_fn()
262
- {
263
- static AnyValue fn = AnyValue::make_function([](const AnyValue &thisVal, std::span<const AnyValue> args) -> AnyValue
264
- {
302
+ AnyValue &get_some_fn()
303
+ {
304
+ static AnyValue fn = AnyValue::make_function([](const AnyValue &thisVal, std::span<const AnyValue> args) -> AnyValue
305
+ {
265
306
  auto self = thisVal.as_iterator();
266
307
  if (args.empty() || !args[0].is_function()) throw Exception::make_exception("callback is not a function", "TypeError");
267
308
  auto callback = args[0].as_function();
@@ -269,7 +310,8 @@ AnyValue &get_some_fn()
269
310
  {
270
311
  auto next_res = self->next();
271
312
  if (next_res.done) { break; }
272
- if (is_truthy(callback->call(thisVal, std::span<const AnyValue>((const jspp::AnyValue[]){next_res.value.value_or(Constants::UNDEFINED)}, 1))))
313
+ AnyValue cb_arg = next_res.value.value_or(Constants::UNDEFINED);
314
+ if (is_truthy(callback->call(thisVal, std::span<const AnyValue>(&cb_arg, 1))))
273
315
  {
274
316
  self->return_();
275
317
  return Constants::TRUE;
@@ -277,33 +319,43 @@ AnyValue &get_some_fn()
277
319
  }
278
320
  return jspp::Constants::FALSE; },
279
321
  "some");
280
- return fn;
281
- }
322
+ return fn;
323
+ }
282
324
 
283
- std::optional<AnyValue> get(const std::string &key)
284
- {
285
- if (key == "toString") return get_toString_fn();
286
- if (key == "next") return get_next_fn();
287
- if (key == "return") return get_return_fn();
288
- if (key == "throw") return get_throw_fn();
289
- if (key == "toArray") return get_toArray_fn();
290
- if (key == "drop") return get_drop_fn();
291
- if (key == "take") return get_take_fn();
292
- if (key == "some") return get_some_fn();
293
- return std::nullopt;
294
- }
295
-
296
- std::optional<AnyValue> get(const AnyValue &key)
297
- {
298
- if (key.is_string())
299
- return get(key.as_string()->value);
325
+ std::optional<AnyValue> get(const std::string &key)
326
+ {
327
+ if (key == "toString")
328
+ return get_toString_fn();
329
+ if (key == "next")
330
+ return get_next_fn();
331
+ if (key == "return")
332
+ return get_return_fn();
333
+ if (key == "throw")
334
+ return get_throw_fn();
335
+ if (key == "toArray")
336
+ return get_toArray_fn();
337
+ if (key == "drop")
338
+ return get_drop_fn();
339
+ if (key == "take")
340
+ return get_take_fn();
341
+ if (key == "some")
342
+ return get_some_fn();
343
+ return std::nullopt;
344
+ }
345
+
346
+ std::optional<AnyValue> get(const AnyValue &key)
347
+ {
348
+ if (key.is_string())
349
+ return get(key.as_string()->value);
300
350
 
301
- if (key == AnyValue::from_symbol(WellKnownSymbols::toStringTag)) return get_toString_fn();
302
- if (key == AnyValue::from_symbol(WellKnownSymbols::iterator)) return get_iterator_fn();
351
+ if (key == AnyValue::from_symbol(WellKnownSymbols::toStringTag))
352
+ return get_toString_fn();
353
+ if (key == AnyValue::from_symbol(WellKnownSymbols::iterator))
354
+ return get_iterator_fn();
303
355
 
304
- return std::nullopt;
305
- }
356
+ return std::nullopt;
357
+ }
306
358
 
307
- } // namespace IteratorPrototypes
359
+ } // namespace IteratorPrototypes
308
360
 
309
361
  } // namespace jspp