@ugo-studio/jspp 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/dist/analysis/scope.js +17 -0
  2. package/dist/analysis/typeAnalyzer.js +7 -1
  3. package/dist/ast/symbols.js +32 -0
  4. package/dist/ast/types.js +0 -6
  5. package/dist/cli-utils/args.js +57 -0
  6. package/dist/cli-utils/colors.js +9 -0
  7. package/dist/cli-utils/file-utils.js +20 -0
  8. package/dist/cli-utils/spinner.js +55 -0
  9. package/dist/cli.js +105 -30
  10. package/dist/core/codegen/class-handlers.js +10 -6
  11. package/dist/core/codegen/control-flow-handlers.js +57 -28
  12. package/dist/core/codegen/declaration-handlers.js +10 -6
  13. package/dist/core/codegen/expression-handlers.js +206 -61
  14. package/dist/core/codegen/function-handlers.js +203 -76
  15. package/dist/core/codegen/helpers.js +125 -28
  16. package/dist/core/codegen/index.js +23 -15
  17. package/dist/core/codegen/literal-handlers.js +15 -6
  18. package/dist/core/codegen/statement-handlers.js +282 -84
  19. package/dist/core/codegen/visitor.js +3 -1
  20. package/package.json +1 -1
  21. package/src/prelude/any_value.hpp +221 -342
  22. package/src/prelude/any_value_access.hpp +168 -81
  23. package/src/prelude/any_value_defines.hpp +74 -35
  24. package/src/prelude/any_value_helpers.hpp +75 -180
  25. package/src/prelude/exception.hpp +1 -0
  26. package/src/prelude/exception_helpers.hpp +4 -4
  27. package/src/prelude/index.hpp +12 -2
  28. package/src/prelude/library/array.hpp +190 -0
  29. package/src/prelude/library/console.hpp +6 -5
  30. package/src/prelude/library/error.hpp +10 -8
  31. package/src/prelude/library/function.hpp +10 -0
  32. package/src/prelude/library/global.hpp +20 -0
  33. package/src/prelude/library/math.hpp +308 -0
  34. package/src/prelude/library/object.hpp +288 -0
  35. package/src/prelude/library/performance.hpp +1 -1
  36. package/src/prelude/library/process.hpp +39 -0
  37. package/src/prelude/library/promise.hpp +57 -55
  38. package/src/prelude/library/symbol.hpp +45 -57
  39. package/src/prelude/library/timer.hpp +6 -6
  40. package/src/prelude/types.hpp +54 -0
  41. package/src/prelude/utils/access.hpp +215 -11
  42. package/src/prelude/utils/assignment_operators.hpp +99 -0
  43. package/src/prelude/utils/log_any_value/array.hpp +8 -8
  44. package/src/prelude/utils/log_any_value/function.hpp +6 -4
  45. package/src/prelude/utils/log_any_value/object.hpp +41 -24
  46. package/src/prelude/utils/log_any_value/primitives.hpp +3 -1
  47. package/src/prelude/utils/operators.hpp +750 -274
  48. package/src/prelude/utils/well_known_symbols.hpp +12 -0
  49. package/src/prelude/values/array.hpp +8 -6
  50. package/src/prelude/values/async_iterator.hpp +79 -0
  51. package/src/prelude/values/descriptors.hpp +2 -2
  52. package/src/prelude/values/function.hpp +72 -62
  53. package/src/prelude/values/helpers/array.hpp +64 -28
  54. package/src/prelude/values/helpers/async_iterator.hpp +275 -0
  55. package/src/prelude/values/helpers/function.hpp +81 -92
  56. package/src/prelude/values/helpers/iterator.hpp +3 -3
  57. package/src/prelude/values/helpers/object.hpp +54 -9
  58. package/src/prelude/values/helpers/promise.hpp +13 -6
  59. package/src/prelude/values/iterator.hpp +1 -1
  60. package/src/prelude/values/object.hpp +10 -3
  61. package/src/prelude/values/promise.hpp +7 -11
  62. package/src/prelude/values/prototypes/array.hpp +851 -12
  63. package/src/prelude/values/prototypes/async_iterator.hpp +50 -0
  64. package/src/prelude/values/prototypes/function.hpp +2 -2
  65. package/src/prelude/values/prototypes/iterator.hpp +5 -5
  66. package/src/prelude/values/prototypes/number.hpp +153 -0
  67. package/src/prelude/values/prototypes/object.hpp +2 -2
  68. package/src/prelude/values/prototypes/promise.hpp +40 -30
  69. package/src/prelude/values/prototypes/string.hpp +28 -28
  70. package/src/prelude/values/prototypes/symbol.hpp +20 -3
  71. package/src/prelude/values/shape.hpp +52 -0
@@ -16,7 +16,7 @@ export function visitObjectPropertyName(node, context) {
16
16
  if (ts.isIdentifier(compExpr)) {
17
17
  const scope = this.getScopeForNode(compExpr);
18
18
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(compExpr.getText(), scope);
19
- propName = this.getDerefCode(propName, this.getJsVarName(compExpr), typeInfo);
19
+ propName = this.getDerefCode(propName, this.getJsVarName(compExpr), context, typeInfo);
20
20
  }
21
21
  return propName;
22
22
  }
@@ -28,8 +28,9 @@ export function visitObjectPropertyName(node, context) {
28
28
  export function visitObjectLiteralExpression(node, context) {
29
29
  const obj = node;
30
30
  const objVar = this.generateUniqueName("__obj_", this.getDeclaredSymbols(node));
31
- let code = `([&]() {
32
- ${this.indent()} auto ${objVar} = jspp::AnyValue::make_object({});\n`;
31
+ let code = `([&]() {\n`;
32
+ code +=
33
+ `${this.indent()} auto ${objVar} = jspp::AnyValue::make_object_with_proto({}, ::Object.get_own_property("prototype"));\n`;
33
34
  this.indentationLevel++;
34
35
  for (const prop of obj.properties) {
35
36
  if (ts.isPropertyAssignment(prop)) {
@@ -43,7 +44,7 @@ ${this.indent()} auto ${objVar} = jspp::AnyValue::make_object({});\n`;
43
44
  const scope = this.getScopeForNode(initializer);
44
45
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(initializer.text, scope);
45
46
  if (typeInfo && !typeInfo.isBuiltin && !typeInfo.isParameter) {
46
- value = this.getDerefCode(value, this.getJsVarName(initializer), typeInfo);
47
+ value = this.getDerefCode(value, this.getJsVarName(initializer), context, typeInfo);
47
48
  }
48
49
  }
49
50
  code +=
@@ -58,7 +59,7 @@ ${this.indent()} auto ${objVar} = jspp::AnyValue::make_object({});\n`;
58
59
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(prop.name.text, scope);
59
60
  let value = this.visit(prop.name, context);
60
61
  if (typeInfo && !typeInfo.isBuiltin && !typeInfo.isParameter) {
61
- value = this.getDerefCode(value, this.getJsVarName(prop.name), typeInfo);
62
+ value = this.getDerefCode(value, this.getJsVarName(prop.name), context, typeInfo);
62
63
  }
63
64
  code +=
64
65
  `${this.indent()}${objVar}.define_data_property(${key}, ${value});\n`;
@@ -101,6 +102,8 @@ ${this.indent()} auto ${objVar} = jspp::AnyValue::make_object({});\n`;
101
102
  }
102
103
  }
103
104
  this.indentationLevel--;
105
+ // code +=
106
+ // `${this.indent()} ${returnCmd} ${objVar};\n${this.indent()}} )() ))`;
104
107
  code += `${this.indent()} return ${objVar};\n${this.indent()}})()`;
105
108
  return code;
106
109
  }
@@ -112,13 +115,16 @@ export function visitArrayLiteralExpression(node, context) {
112
115
  const scope = this.getScopeForNode(elem);
113
116
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(elem.text, scope);
114
117
  if (typeInfo && !typeInfo.isBuiltin && !typeInfo.isParameter) {
115
- elemText = this.getDerefCode(elemText, this.getJsVarName(elem), typeInfo);
118
+ elemText = this.getDerefCode(elemText, this.getJsVarName(elem), context, typeInfo);
116
119
  }
117
120
  }
118
121
  return elemText;
119
- })
120
- .join(", ");
121
- return `jspp::AnyValue::make_array({${elements}})`;
122
+ });
123
+ const elementsJoined = elements.join(", ");
124
+ const elementsSpan = elements.length > 0
125
+ ? `std::span<const jspp::AnyValue>((const jspp::AnyValue[]){${elementsJoined}}, ${elements.length})`
126
+ : "std::span<const jspp::AnyValue>{}";
127
+ return `jspp::AnyValue::make_array_with_proto(${elementsSpan}, ::Array.get_own_property("prototype"))`;
122
128
  }
123
129
  export function visitPrefixUnaryExpression(node, context) {
124
130
  const prefixUnaryExpr = node;
@@ -130,7 +136,7 @@ export function visitPrefixUnaryExpression(node, context) {
130
136
  const scope = this.getScopeForNode(prefixUnaryExpr.operand);
131
137
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(prefixUnaryExpr.operand.getText(), scope);
132
138
  if (context.derefBeforeAssignment) {
133
- target = this.getDerefCode(operand, operand, typeInfo);
139
+ target = this.getDerefCode(operand, operand, context, typeInfo);
134
140
  }
135
141
  else if (typeInfo.needsHeapAllocation) {
136
142
  target = `*${operand}`;
@@ -144,7 +150,7 @@ export function visitPrefixUnaryExpression(node, context) {
144
150
  const scope = this.getScopeForNode(prefixUnaryExpr.operand);
145
151
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(prefixUnaryExpr.operand.getText(), scope);
146
152
  if (context.derefBeforeAssignment) {
147
- target = this.getDerefCode(operand, operand, typeInfo);
153
+ target = this.getDerefCode(operand, operand, context, typeInfo);
148
154
  }
149
155
  else if (typeInfo.needsHeapAllocation) {
150
156
  target = `*${operand}`;
@@ -163,7 +169,7 @@ export function visitPostfixUnaryExpression(node, context) {
163
169
  const scope = this.getScopeForNode(postfixUnaryExpr.operand);
164
170
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(postfixUnaryExpr.operand.getText(), scope);
165
171
  if (context.derefBeforeAssignment) {
166
- target = this.getDerefCode(operand, operand, typeInfo);
172
+ target = this.getDerefCode(operand, operand, context, typeInfo);
167
173
  }
168
174
  else if (typeInfo.needsHeapAllocation) {
169
175
  target = `*${operand}`;
@@ -199,7 +205,10 @@ export function visitPropertyAccessExpression(node, context) {
199
205
  !typeInfo.isParameter &&
200
206
  !typeInfo.isBuiltin &&
201
207
  ts.isIdentifier(propAccess.expression)) {
202
- finalExpr = this.getDerefCode(exprText, this.getJsVarName(propAccess.expression), typeInfo);
208
+ finalExpr = this.getDerefCode(exprText, this.getJsVarName(propAccess.expression), context, typeInfo);
209
+ }
210
+ if (propAccess.questionDotToken) {
211
+ return `jspp::Access::optional_get_property(${finalExpr}, "${propName}")`;
203
212
  }
204
213
  return `${finalExpr}.get_own_property("${propName}")`;
205
214
  }
@@ -221,7 +230,7 @@ export function visitElementAccessExpression(node, context) {
221
230
  !exprTypeInfo.isParameter &&
222
231
  !exprTypeInfo.isBuiltin &&
223
232
  ts.isIdentifier(elemAccess.expression)) {
224
- finalExpr = this.getDerefCode(exprText, this.getJsVarName(elemAccess.expression), exprTypeInfo);
233
+ finalExpr = this.getDerefCode(exprText, this.getJsVarName(elemAccess.expression), context, exprTypeInfo);
225
234
  }
226
235
  // Dereference the argument expression if it's an identifier
227
236
  if (ts.isIdentifier(elemAccess.argumentExpression)) {
@@ -233,9 +242,12 @@ export function visitElementAccessExpression(node, context) {
233
242
  if (argTypeInfo &&
234
243
  !argTypeInfo.isParameter &&
235
244
  !argTypeInfo.isBuiltin) {
236
- argText = this.getDerefCode(argText, this.getJsVarName(elemAccess.argumentExpression), argTypeInfo);
245
+ argText = this.getDerefCode(argText, this.getJsVarName(elemAccess.argumentExpression), context, argTypeInfo);
237
246
  }
238
247
  }
248
+ if (elemAccess.questionDotToken) {
249
+ return `jspp::Access::optional_get_element(${finalExpr}, ${argText})`;
250
+ }
239
251
  return `${finalExpr}.get_own_property(${argText})`;
240
252
  }
241
253
  export function visitBinaryExpression(node, context) {
@@ -248,21 +260,84 @@ export function visitBinaryExpression(node, context) {
248
260
  ts.SyntaxKind.AsteriskEqualsToken,
249
261
  ts.SyntaxKind.SlashEqualsToken,
250
262
  ts.SyntaxKind.PercentEqualsToken,
263
+ ts.SyntaxKind.AsteriskAsteriskEqualsToken,
264
+ ts.SyntaxKind.LessThanLessThanEqualsToken,
265
+ ts.SyntaxKind.GreaterThanGreaterThanEqualsToken,
266
+ ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken,
267
+ ts.SyntaxKind.AmpersandEqualsToken,
268
+ ts.SyntaxKind.BarEqualsToken,
269
+ ts.SyntaxKind.CaretEqualsToken,
270
+ ts.SyntaxKind.AmpersandAmpersandEqualsToken,
271
+ ts.SyntaxKind.BarBarEqualsToken,
272
+ ts.SyntaxKind.QuestionQuestionEqualsToken,
251
273
  ];
252
274
  if (assignmentOperators.includes(opToken.kind)) {
275
+ if (opToken.kind ===
276
+ ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken) {
277
+ const leftText = this.visit(binExpr.left, context);
278
+ const rightText = this.visit(binExpr.right, context);
279
+ let target = leftText;
280
+ if (ts.isIdentifier(binExpr.left)) {
281
+ const scope = this.getScopeForNode(binExpr.left);
282
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.left.text, scope);
283
+ target = typeInfo.needsHeapAllocation
284
+ ? `*${leftText}`
285
+ : leftText;
286
+ return `${target} = jspp::unsigned_right_shift(${target}, ${rightText})`;
287
+ }
288
+ }
289
+ if (opToken.kind === ts.SyntaxKind.AsteriskAsteriskEqualsToken) {
290
+ const leftText = this.visit(binExpr.left, context);
291
+ const rightText = this.visit(binExpr.right, context);
292
+ let target = leftText;
293
+ if (ts.isIdentifier(binExpr.left)) {
294
+ const scope = this.getScopeForNode(binExpr.left);
295
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.left.text, scope);
296
+ target = typeInfo.needsHeapAllocation
297
+ ? `*${leftText}`
298
+ : leftText;
299
+ return `${target} = jspp::pow(${target}, ${rightText})`;
300
+ }
301
+ // For complex LHS, we need a different approach, but this is a start.
302
+ }
303
+ if (opToken.kind === ts.SyntaxKind.AmpersandAmpersandEqualsToken ||
304
+ opToken.kind === ts.SyntaxKind.BarBarEqualsToken ||
305
+ opToken.kind === ts.SyntaxKind.QuestionQuestionEqualsToken) {
306
+ const leftText = this.visit(binExpr.left, context);
307
+ const rightText = this.visit(binExpr.right, context);
308
+ let target = leftText;
309
+ if (ts.isIdentifier(binExpr.left)) {
310
+ const scope = this.getScopeForNode(binExpr.left);
311
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.left.text, scope);
312
+ target = typeInfo.needsHeapAllocation
313
+ ? `*${leftText}`
314
+ : leftText;
315
+ if (opToken.kind === ts.SyntaxKind.AmpersandAmpersandEqualsToken) {
316
+ return `jspp::logical_and_assign(${target}, ${rightText})`;
317
+ }
318
+ else if (opToken.kind === ts.SyntaxKind.BarBarEqualsToken) {
319
+ return `jspp::logical_or_assign(${target}, ${rightText})`;
320
+ }
321
+ else {
322
+ return `jspp::nullish_coalesce_assign(${target}, ${rightText})`;
323
+ }
324
+ }
325
+ }
253
326
  const leftText = this.visit(binExpr.left, context);
254
- let rightText = this.visit(binExpr.right, context);
327
+ let rightText = ts.isNumericLiteral(binExpr.right)
328
+ ? binExpr.right.getText()
329
+ : this.visit(binExpr.right, context);
255
330
  if (ts.isIdentifier(binExpr.right)) {
256
331
  const scope = this.getScopeForNode(binExpr.right);
257
332
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.right.getText(), scope);
258
- rightText = this.getDerefCode(rightText, this.getJsVarName(binExpr.right), typeInfo);
333
+ rightText = this.getDerefCode(rightText, this.getJsVarName(binExpr.right), context, typeInfo);
259
334
  }
260
335
  let target = leftText;
261
336
  if (ts.isIdentifier(binExpr.left)) {
262
337
  const scope = this.getScopeForNode(binExpr.left);
263
338
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.left.getText(), scope);
264
339
  if (context.derefBeforeAssignment) {
265
- target = this.getDerefCode(leftText, leftText, typeInfo);
340
+ target = this.getDerefCode(leftText, leftText, context, typeInfo);
266
341
  }
267
342
  else if (typeInfo.needsHeapAllocation) {
268
343
  target = `*${leftText}`;
@@ -271,7 +346,7 @@ export function visitBinaryExpression(node, context) {
271
346
  return `${target} ${op} ${rightText}`;
272
347
  }
273
348
  if (opToken.kind === ts.SyntaxKind.EqualsToken) {
274
- const rightText = this.visit(binExpr.right, context);
349
+ let rightText = this.visit(binExpr.right, context);
275
350
  if (ts.isPropertyAccessExpression(binExpr.left)) {
276
351
  const propAccess = binExpr.left;
277
352
  if (propAccess.expression.kind === ts.SyntaxKind.SuperKeyword) {
@@ -285,7 +360,7 @@ export function visitBinaryExpression(node, context) {
285
360
  if (rightTypeInfo &&
286
361
  !rightTypeInfo.isParameter &&
287
362
  !rightTypeInfo.isBuiltin) {
288
- finalRightText = this.getDerefCode(rightText, this.getJsVarName(binExpr.right), rightTypeInfo);
363
+ finalRightText = this.getDerefCode(rightText, this.getJsVarName(binExpr.right), context, rightTypeInfo);
289
364
  }
290
365
  }
291
366
  // Approximate super assignment as setting property on 'this'
@@ -298,7 +373,7 @@ export function visitBinaryExpression(node, context) {
298
373
  const scope = this.getScopeForNode(propAccess.expression);
299
374
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(propAccess.expression.getText(), scope);
300
375
  if (typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin) {
301
- finalObjExpr = this.getDerefCode(objExprText, this.getJsVarName(propAccess.expression), typeInfo);
376
+ finalObjExpr = this.getDerefCode(objExprText, this.getJsVarName(propAccess.expression), context, typeInfo);
302
377
  }
303
378
  }
304
379
  let finalRightText = rightText;
@@ -309,7 +384,7 @@ export function visitBinaryExpression(node, context) {
309
384
  if (rightTypeInfo &&
310
385
  !rightTypeInfo.isParameter &&
311
386
  !rightTypeInfo.isBuiltin) {
312
- finalRightText = this.getDerefCode(rightText, this.getJsVarName(binExpr.right), rightTypeInfo);
387
+ finalRightText = this.getDerefCode(rightText, this.getJsVarName(binExpr.right), context, rightTypeInfo);
313
388
  }
314
389
  }
315
390
  return `${finalObjExpr}.set_own_property("${propName}", ${finalRightText})`;
@@ -323,7 +398,7 @@ export function visitBinaryExpression(node, context) {
323
398
  const scope = this.getScopeForNode(elemAccess.expression);
324
399
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(elemAccess.expression.getText(), scope);
325
400
  if (typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin) {
326
- finalObjExpr = this.getDerefCode(objExprText, this.getJsVarName(elemAccess.expression), typeInfo);
401
+ finalObjExpr = this.getDerefCode(objExprText, this.getJsVarName(elemAccess.expression), context, typeInfo);
327
402
  }
328
403
  }
329
404
  if (ts.isIdentifier(elemAccess.argumentExpression)) {
@@ -333,7 +408,7 @@ export function visitBinaryExpression(node, context) {
333
408
  if (argTypeInfo &&
334
409
  !argTypeInfo.isParameter &&
335
410
  !argTypeInfo.isBuiltin) {
336
- argText = this.getDerefCode(argText, this.getJsVarName(elemAccess.argumentExpression), argTypeInfo);
411
+ argText = this.getDerefCode(argText, this.getJsVarName(elemAccess.argumentExpression), context, argTypeInfo);
337
412
  }
338
413
  }
339
414
  let finalRightText = rightText;
@@ -344,7 +419,7 @@ export function visitBinaryExpression(node, context) {
344
419
  if (rightTypeInfo &&
345
420
  !rightTypeInfo.isParameter &&
346
421
  !rightTypeInfo.isBuiltin) {
347
- finalRightText = this.getDerefCode(rightText, this.getJsVarName(binExpr.right), rightTypeInfo);
422
+ finalRightText = this.getDerefCode(rightText, this.getJsVarName(binExpr.right), context, rightTypeInfo);
348
423
  }
349
424
  }
350
425
  return `${finalObjExpr}.set_own_property(${argText}, ${finalRightText})`;
@@ -358,8 +433,11 @@ export function visitBinaryExpression(node, context) {
358
433
  if (typeInfo?.isConst) {
359
434
  return `jspp::Exception::throw_immutable_assignment()`;
360
435
  }
436
+ if (ts.isNumericLiteral(binExpr.right)) {
437
+ rightText = binExpr.right.getText();
438
+ }
361
439
  const target = context.derefBeforeAssignment
362
- ? this.getDerefCode(leftText, leftText, typeInfo)
440
+ ? this.getDerefCode(leftText, leftText, context, typeInfo)
363
441
  : (typeInfo.needsHeapAllocation ? `*${leftText}` : leftText);
364
442
  return `${target} ${op} ${rightText}`;
365
443
  }
@@ -373,7 +451,7 @@ export function visitBinaryExpression(node, context) {
373
451
  return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(binExpr.left)})`;
374
452
  }
375
453
  if (typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin) {
376
- finalLeft = this.getDerefCode(leftText, this.getJsVarName(binExpr.left), typeInfo);
454
+ finalLeft = this.getDerefCode(leftText, this.getJsVarName(binExpr.left), context, typeInfo);
377
455
  }
378
456
  }
379
457
  let finalRight = rightText;
@@ -385,24 +463,53 @@ export function visitBinaryExpression(node, context) {
385
463
  return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(binExpr.right)})`;
386
464
  }
387
465
  if (typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin) {
388
- finalRight = this.getDerefCode(rightText, this.getJsVarName(binExpr.right), typeInfo);
466
+ finalRight = this.getDerefCode(rightText, this.getJsVarName(binExpr.right), context, typeInfo);
389
467
  }
390
468
  }
469
+ if (opToken.kind === ts.SyntaxKind.InKeyword) {
470
+ return `jspp::Access::in(${finalLeft}, ${finalRight})`;
471
+ }
472
+ if (opToken.kind === ts.SyntaxKind.InstanceOfKeyword) {
473
+ return `jspp::Access::instance_of(${finalLeft}, ${finalRight})`;
474
+ }
475
+ if (opToken.kind === ts.SyntaxKind.AmpersandAmpersandToken) {
476
+ return `jspp::logical_and(${finalLeft}, ${finalRight})`;
477
+ }
478
+ if (opToken.kind === ts.SyntaxKind.BarBarToken) {
479
+ return `jspp::logical_or(${finalLeft}, ${finalRight})`;
480
+ }
481
+ if (opToken.kind === ts.SyntaxKind.QuestionQuestionToken) {
482
+ return `jspp::nullish_coalesce(${finalLeft}, ${finalRight})`;
483
+ }
484
+ // Optimizations to prevent calling make_number multiple times
485
+ if (ts.isNumericLiteral(binExpr.left)) {
486
+ finalLeft = binExpr.left.getText();
487
+ }
488
+ if (ts.isNumericLiteral(binExpr.right)) {
489
+ finalRight = binExpr.right.getText();
490
+ }
391
491
  if (opToken.kind === ts.SyntaxKind.EqualsEqualsEqualsToken) {
392
- return `${finalLeft}.is_strictly_equal_to(${finalRight})`;
492
+ return `jspp::is_strictly_equal_to(${finalLeft}, ${finalRight})`;
393
493
  }
394
494
  if (opToken.kind === ts.SyntaxKind.EqualsEqualsToken) {
395
- return `${finalLeft}.is_equal_to(${finalRight})`;
495
+ return `jspp::is_equal_to(${finalLeft}, ${finalRight})`;
396
496
  }
397
497
  if (opToken.kind === ts.SyntaxKind.ExclamationEqualsEqualsToken) {
398
- return `${finalLeft}.not_strictly_equal_to(${finalRight})`;
498
+ return `jspp::not_strictly_equal_to(${finalLeft}, ${finalRight})`;
399
499
  }
400
500
  if (opToken.kind === ts.SyntaxKind.ExclamationEqualsToken) {
401
- return `${finalLeft}.not_equal_to(${finalRight})`;
501
+ return `jspp::not_equal_to(${finalLeft}, ${finalRight})`;
402
502
  }
403
503
  if (opToken.kind === ts.SyntaxKind.AsteriskAsteriskToken) {
404
504
  return `jspp::pow(${finalLeft}, ${finalRight})`;
405
505
  }
506
+ if (opToken.kind === ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken) {
507
+ return `jspp::unsigned_right_shift(${finalLeft}, ${finalRight})`;
508
+ }
509
+ // Optimizations to prevent calling make_number multiple times
510
+ if (ts.isNumericLiteral(binExpr.left) && ts.isNumericLiteral(binExpr.right)) {
511
+ return `jspp::AnyValue::make_number(${finalLeft} ${op} ${finalRight})`;
512
+ }
406
513
  if (op === "+" ||
407
514
  op === "-" ||
408
515
  op === "*" ||
@@ -410,7 +517,9 @@ export function visitBinaryExpression(node, context) {
410
517
  op === "%" ||
411
518
  op === "^" ||
412
519
  op === "&" ||
413
- op === "|") {
520
+ op === "|" ||
521
+ op === "<<" ||
522
+ op === ">>") {
414
523
  return `(${finalLeft} ${op} ${finalRight})`;
415
524
  }
416
525
  return `${finalLeft} ${op} ${finalRight}`;
@@ -426,7 +535,7 @@ export function visitConditionalExpression(node, context) {
426
535
  ...context,
427
536
  isFunctionBody: false,
428
537
  });
429
- return `(${condition}).is_truthy() ? ${whenTrueStmt} : ${whenFalseStmt}`;
538
+ return `is_truthy(${condition}) ? ${whenTrueStmt} : ${whenFalseStmt}`;
430
539
  }
431
540
  export function visitCallExpression(node, context) {
432
541
  const callExpr = node;
@@ -437,9 +546,9 @@ export function visitCallExpression(node, context) {
437
546
  }
438
547
  const args = callExpr.arguments.map((arg) => this.visit(arg, context))
439
548
  .join(", ");
440
- return `(${context.superClassVar}).as_function("super")->call(${this.globalThisVar}, {${args}})`;
549
+ return `(${context.superClassVar}).call(${this.globalThisVar}, (const jspp::AnyValue[]){${args}}, "super")`;
441
550
  }
442
- const args = callExpr.arguments
551
+ const argsArray = callExpr.arguments
443
552
  .map((arg) => {
444
553
  const argText = this.visit(arg, context);
445
554
  if (ts.isIdentifier(arg)) {
@@ -448,13 +557,17 @@ export function visitCallExpression(node, context) {
448
557
  if (!typeInfo) {
449
558
  return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(arg)})`;
450
559
  }
451
- if (typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin) {
452
- return this.getDerefCode(argText, this.getJsVarName(arg), typeInfo);
560
+ if (typeInfo && !typeInfo.isBuiltin) {
561
+ return this.getDerefCode(argText, this.getJsVarName(arg), context, typeInfo);
453
562
  }
454
563
  }
455
564
  return argText;
456
- })
457
- .join(", ");
565
+ });
566
+ const args = argsArray.join(", ");
567
+ const argsCount = argsArray.length;
568
+ const argsSpan = argsCount > 0
569
+ ? `std::span<const jspp::AnyValue>((const jspp::AnyValue[]){${args}}, ${argsCount})`
570
+ : "std::span<const jspp::AnyValue>{}";
458
571
  // Handle obj.method() -> pass obj as 'this'
459
572
  if (ts.isPropertyAccessExpression(callee)) {
460
573
  const propAccess = callee;
@@ -463,7 +576,7 @@ export function visitCallExpression(node, context) {
463
576
  throw new Error("super.method() called but no super class variable found in context");
464
577
  }
465
578
  const propName = propAccess.name.getText();
466
- return `(${context.superClassVar}).get_own_property("prototype").get_own_property("${propName}").as_function("${this.escapeString(propName)}")->call(${this.globalThisVar}, {${args}})`;
579
+ return `(${context.superClassVar}).get_own_property("prototype").get_own_property("${propName}").call(${this.globalThisVar}, ${argsSpan}, "${this.escapeString(propName)}")`;
467
580
  }
468
581
  const objExpr = propAccess.expression;
469
582
  const propName = propAccess.name.getText();
@@ -477,10 +590,13 @@ export function visitCallExpression(node, context) {
477
590
  return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(objExpr)})`;
478
591
  }
479
592
  if (typeInfo && !typeInfo.isBuiltin && !typeInfo.isParameter) {
480
- derefObj = this.getDerefCode(objCode, this.getJsVarName(objExpr), typeInfo);
593
+ derefObj = this.getDerefCode(objCode, this.getJsVarName(objExpr), context, typeInfo);
481
594
  }
482
595
  }
483
- return `([&](){ auto __obj = ${derefObj}; return __obj.get_own_property("${propName}").as_function("${this.escapeString(propName)}")->call(__obj, {${args}}); })()`;
596
+ if (callExpr.questionDotToken) {
597
+ return `jspp::Access::optional_call(${derefObj}.get_own_property("${propName}"), ${derefObj}, ${argsSpan}, "${this.escapeString(propName)}")`;
598
+ }
599
+ return `${derefObj}.call_own_property("${propName}", ${argsSpan})`;
484
600
  }
485
601
  // Handle obj[method]() -> pass obj as 'this'
486
602
  if (ts.isElementAccessExpression(callee)) {
@@ -495,7 +611,7 @@ export function visitCallExpression(node, context) {
495
611
  return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(objExpr)})`;
496
612
  }
497
613
  if (typeInfo && !typeInfo.isBuiltin && !typeInfo.isParameter) {
498
- derefObj = this.getDerefCode(objCode, this.getJsVarName(objExpr), typeInfo);
614
+ derefObj = this.getDerefCode(objCode, this.getJsVarName(objExpr), context, typeInfo);
499
615
  }
500
616
  }
501
617
  let argText = visitObjectPropertyName.call(this, elemAccess.argumentExpression, { ...context, isBracketNotationPropertyAccess: true });
@@ -509,10 +625,13 @@ export function visitCallExpression(node, context) {
509
625
  }
510
626
  if (argTypeInfo && !argTypeInfo.isParameter &&
511
627
  !argTypeInfo.isBuiltin) {
512
- argText = this.getDerefCode(argText, this.getJsVarName(elemAccess.argumentExpression), argTypeInfo);
628
+ argText = this.getDerefCode(argText, this.getJsVarName(elemAccess.argumentExpression), context, argTypeInfo);
513
629
  }
514
630
  }
515
- return `([&](){ auto __obj = ${derefObj}; return __obj.get_own_property(${argText}).as_function()->call(__obj, {${args}}); })()`;
631
+ if (callExpr.questionDotToken) {
632
+ return `jspp::Access::optional_call(${derefObj}.get_own_property(${argText}), ${derefObj}, ${argsSpan})`;
633
+ }
634
+ return `${derefObj}.call_own_property(${argText}, ${argsSpan})`;
516
635
  }
517
636
  const calleeCode = this.visit(callee, context);
518
637
  let derefCallee = calleeCode;
@@ -526,7 +645,7 @@ export function visitCallExpression(node, context) {
526
645
  derefCallee = calleeCode;
527
646
  }
528
647
  else if (typeInfo) {
529
- derefCallee = this.getDerefCode(calleeCode, this.getJsVarName(callee), typeInfo);
648
+ derefCallee = this.getDerefCode(calleeCode, this.getJsVarName(callee), context, typeInfo);
530
649
  }
531
650
  }
532
651
  let calleeName = "";
@@ -539,12 +658,18 @@ export function visitCallExpression(node, context) {
539
658
  calleeName = this.escapeString(funcExpr.name?.getText() || "");
540
659
  }
541
660
  // Pass undefined as 'this' for normal function calls
542
- return `${derefCallee}.as_function("${calleeName}")->call(jspp::AnyValue::make_undefined(), {${args}})`;
661
+ const calleeNamePart = calleeName && calleeName.length > 0
662
+ ? `, "${calleeName}"`
663
+ : "";
664
+ if (callExpr.questionDotToken) {
665
+ return `jspp::Access::optional_call(${derefCallee}, jspp::Constants::UNDEFINED, ${argsSpan}${calleeNamePart})`;
666
+ }
667
+ return `${derefCallee}.call(jspp::Constants::UNDEFINED, ${argsSpan}${calleeNamePart})`;
543
668
  }
544
669
  export function visitVoidExpression(node, context) {
545
670
  const voidExpr = node;
546
671
  const exprText = this.visit(voidExpr.expression, context);
547
- return `(${exprText}, jspp::AnyValue::make_undefined())`;
672
+ return `(${exprText}, jspp::Constants::UNDEFINED)`;
548
673
  }
549
674
  export function visitTemplateExpression(node, context) {
550
675
  const templateExpr = node;
@@ -562,7 +687,7 @@ export function visitTemplateExpression(node, context) {
562
687
  else if (typeInfo &&
563
688
  !typeInfo.isParameter &&
564
689
  !typeInfo.isBuiltin) {
565
- finalExpr = this.getDerefCode(exprText, this.getJsVarName(expr), typeInfo);
690
+ finalExpr = this.getDerefCode(exprText, this.getJsVarName(expr), context, typeInfo);
566
691
  }
567
692
  }
568
693
  result += ` + (${finalExpr})`;
@@ -576,18 +701,20 @@ export function visitNewExpression(node, context) {
576
701
  const newExpr = node;
577
702
  const exprText = this.visit(newExpr.expression, context);
578
703
  let derefExpr = exprText;
704
+ let name = `"${exprText}"`;
579
705
  if (ts.isIdentifier(newExpr.expression)) {
706
+ name = this.getJsVarName(newExpr.expression);
580
707
  const scope = this.getScopeForNode(newExpr.expression);
581
708
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(newExpr.expression.getText(), scope);
582
709
  if (!typeInfo &&
583
710
  !this.isBuiltinObject(newExpr.expression)) {
584
- return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(newExpr.expression)})`;
711
+ return `jspp::Exception::throw_unresolved_reference(${name})`;
585
712
  }
586
713
  if (typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin) {
587
- derefExpr = this.getDerefCode(exprText, this.getJsVarName(newExpr.expression), typeInfo);
714
+ derefExpr = this.getDerefCode(exprText, name, context, typeInfo);
588
715
  }
589
716
  }
590
- const args = newExpr.arguments
717
+ const argsArray = newExpr.arguments
591
718
  ? newExpr.arguments
592
719
  .map((arg) => {
593
720
  const argText = this.visit(arg, context);
@@ -599,14 +726,18 @@ export function visitNewExpression(node, context) {
599
726
  return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(arg)})`;
600
727
  }
601
728
  if (typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin) {
602
- return this.getDerefCode(argText, this.getJsVarName(arg), typeInfo);
729
+ return this.getDerefCode(argText, this.getJsVarName(arg), context, typeInfo);
603
730
  }
604
731
  }
605
732
  return argText;
606
733
  })
607
- .join(", ")
608
- : "";
609
- return `${derefExpr}.construct({${args}})`;
734
+ : [];
735
+ const args = argsArray.join(", ");
736
+ const argsCount = argsArray.length;
737
+ const argsSpan = argsCount > 0
738
+ ? `std::span<const jspp::AnyValue>((const jspp::AnyValue[]){${args}}, ${argsCount})`
739
+ : "std::span<const jspp::AnyValue>{}";
740
+ return `${derefExpr}.construct(${argsSpan}, ${name})`;
610
741
  }
611
742
  export function visitTypeOfExpression(node, context) {
612
743
  const typeOfExpr = node;
@@ -620,10 +751,10 @@ export function visitTypeOfExpression(node, context) {
620
751
  derefExpr = `/* undeclared variable: ${this.getJsVarName(typeOfExpr.expression)} */`; // typeof undeclared variable is 'undefined'
621
752
  }
622
753
  if (typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin) {
623
- derefExpr = this.getDerefCode(exprText, this.getJsVarName(typeOfExpr.expression), typeInfo);
754
+ derefExpr = this.getDerefCode(exprText, this.getJsVarName(typeOfExpr.expression), context, typeInfo);
624
755
  }
625
756
  }
626
- return `jspp::Access::typeof(${derefExpr})`;
757
+ return `jspp::Access::type_of(${derefExpr})`;
627
758
  }
628
759
  export function visitAwaitExpression(node, context) {
629
760
  const awaitExpr = node;
@@ -639,11 +770,25 @@ export function visitAwaitExpression(node, context) {
639
770
  // We can just throw before co_await.
640
771
  // But we need to return a string expression.
641
772
  // Using comma operator: (throw..., AnyValue())
642
- return `(jspp::Exception::throw_unresolved_reference(${this.getJsVarName(awaitExpr.expression)}), co_await jspp::AnyValue::make_undefined())`;
773
+ return `(jspp::Exception::throw_unresolved_reference(${this.getJsVarName(awaitExpr.expression)}), co_await jspp::Constants::UNDEFINED)`;
643
774
  }
644
775
  if (typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin) {
645
- derefExpr = this.getDerefCode(exprText, this.getJsVarName(awaitExpr.expression), typeInfo);
776
+ derefExpr = this.getDerefCode(exprText, this.getJsVarName(awaitExpr.expression), context, typeInfo);
646
777
  }
647
778
  }
648
779
  return `co_await ${derefExpr}`;
649
780
  }
781
+ export function visitDeleteExpression(node, context) {
782
+ const expr = node.expression;
783
+ if (ts.isPropertyAccessExpression(expr)) {
784
+ const obj = this.visit(expr.expression, context);
785
+ const prop = `jspp::AnyValue::make_string("${expr.name.getText()}")`;
786
+ return `jspp::Access::delete_property(${obj}, ${prop})`;
787
+ }
788
+ else if (ts.isElementAccessExpression(expr)) {
789
+ const obj = this.visit(expr.expression, context);
790
+ const prop = this.visit(expr.argumentExpression, context);
791
+ return `jspp::Access::delete_property(${obj}, ${prop})`;
792
+ }
793
+ return "jspp::Constants::TRUE"; // delete on non-property is true in JS
794
+ }