@ugo-studio/jspp 0.1.4 → 0.1.5

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