@ugo-studio/jspp 0.3.0 → 0.3.2

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 (127) hide show
  1. package/LICENSE +25 -25
  2. package/README.md +20 -12
  3. package/dist/cli/args.js +22 -0
  4. package/dist/cli/compiler.js +53 -0
  5. package/dist/cli/index.js +43 -107
  6. package/dist/cli/pch.js +71 -0
  7. package/dist/cli/runner.js +23 -0
  8. package/dist/cli/spinner.js +27 -11
  9. package/dist/cli/transpiler.js +20 -0
  10. package/dist/cli/utils.js +59 -0
  11. package/dist/cli/wasm.js +70 -0
  12. package/dist/index.js +17 -6
  13. package/dist/{analysis → interpreter/analysis}/scope.js +38 -3
  14. package/dist/{analysis → interpreter/analysis}/typeAnalyzer.js +563 -28
  15. package/dist/{core → interpreter/core}/codegen/class-handlers.js +1 -1
  16. package/dist/{core → interpreter/core}/codegen/control-flow-handlers.js +12 -11
  17. package/dist/{core → interpreter/core}/codegen/declaration-handlers.js +28 -9
  18. package/dist/{core → interpreter/core}/codegen/destructuring-handlers.js +9 -4
  19. package/dist/{core → interpreter/core}/codegen/expression-handlers.js +82 -88
  20. package/dist/{core → interpreter/core}/codegen/function-handlers.js +159 -46
  21. package/dist/{core → interpreter/core}/codegen/helpers.js +170 -25
  22. package/dist/interpreter/core/codegen/index.js +156 -0
  23. package/dist/{core → interpreter/core}/codegen/literal-handlers.js +9 -0
  24. package/dist/{core → interpreter/core}/codegen/statement-handlers.js +47 -7
  25. package/package.json +6 -4
  26. package/scripts/precompile-headers.ts +293 -50
  27. package/scripts/setup-compiler.ts +63 -63
  28. package/scripts/setup-emsdk.ts +114 -0
  29. package/src/prelude/any_value.cpp +888 -0
  30. package/src/prelude/any_value.hpp +29 -24
  31. package/src/prelude/{exception_helpers.hpp → exception.cpp} +53 -53
  32. package/src/prelude/exception.hpp +27 -27
  33. package/src/prelude/iterator_instantiations.hpp +10 -0
  34. package/src/prelude/{index.hpp → jspp.hpp} +13 -17
  35. package/src/prelude/library/array.cpp +191 -0
  36. package/src/prelude/library/array.hpp +5 -178
  37. package/src/prelude/library/boolean.cpp +30 -0
  38. package/src/prelude/library/boolean.hpp +14 -0
  39. package/src/prelude/library/console.cpp +125 -0
  40. package/src/prelude/library/console.hpp +9 -97
  41. package/src/prelude/library/error.cpp +100 -0
  42. package/src/prelude/library/error.hpp +8 -108
  43. package/src/prelude/library/function.cpp +69 -0
  44. package/src/prelude/library/function.hpp +6 -5
  45. package/src/prelude/library/global.cpp +98 -0
  46. package/src/prelude/library/global.hpp +12 -28
  47. package/src/prelude/library/global_usings.hpp +15 -0
  48. package/src/prelude/library/math.cpp +261 -0
  49. package/src/prelude/library/math.hpp +8 -288
  50. package/src/prelude/library/object.cpp +379 -0
  51. package/src/prelude/library/object.hpp +5 -267
  52. package/src/prelude/library/performance.cpp +21 -0
  53. package/src/prelude/library/performance.hpp +5 -20
  54. package/src/prelude/library/process.cpp +38 -0
  55. package/src/prelude/library/process.hpp +3 -31
  56. package/src/prelude/library/promise.cpp +131 -0
  57. package/src/prelude/library/promise.hpp +5 -116
  58. package/src/prelude/library/symbol.cpp +56 -0
  59. package/src/prelude/library/symbol.hpp +5 -46
  60. package/src/prelude/library/timer.cpp +88 -0
  61. package/src/prelude/library/timer.hpp +11 -87
  62. package/src/prelude/runtime.cpp +19 -0
  63. package/src/prelude/types.hpp +26 -20
  64. package/src/prelude/utils/access.hpp +123 -32
  65. package/src/prelude/utils/assignment_operators.hpp +119 -99
  66. package/src/prelude/utils/log_any_value/array.hpp +61 -40
  67. package/src/prelude/utils/log_any_value/function.hpp +39 -39
  68. package/src/prelude/utils/log_any_value/log_any_value.hpp +1 -1
  69. package/src/prelude/utils/log_any_value/object.hpp +60 -3
  70. package/src/prelude/utils/log_any_value/primitives.hpp +1 -1
  71. package/src/prelude/utils/operators.hpp +109 -94
  72. package/src/prelude/utils/operators_native.hpp +349 -0
  73. package/src/prelude/utils/well_known_symbols.hpp +24 -24
  74. package/src/prelude/values/array.cpp +1399 -0
  75. package/src/prelude/values/array.hpp +4 -0
  76. package/src/prelude/values/async_iterator.cpp +251 -0
  77. package/src/prelude/values/async_iterator.hpp +60 -32
  78. package/src/prelude/values/boolean.cpp +64 -0
  79. package/src/prelude/values/function.cpp +262 -0
  80. package/src/prelude/values/function.hpp +10 -30
  81. package/src/prelude/values/iterator.cpp +309 -0
  82. package/src/prelude/values/iterator.hpp +33 -64
  83. package/src/prelude/values/number.cpp +221 -0
  84. package/src/prelude/values/object.cpp +200 -0
  85. package/src/prelude/values/object.hpp +4 -0
  86. package/src/prelude/values/promise.cpp +479 -0
  87. package/src/prelude/values/promise.hpp +9 -2
  88. package/src/prelude/values/prototypes/array.hpp +46 -1348
  89. package/src/prelude/values/prototypes/async_iterator.hpp +19 -61
  90. package/src/prelude/values/prototypes/boolean.hpp +24 -0
  91. package/src/prelude/values/prototypes/function.hpp +7 -46
  92. package/src/prelude/values/prototypes/iterator.hpp +15 -191
  93. package/src/prelude/values/prototypes/number.hpp +30 -210
  94. package/src/prelude/values/prototypes/object.hpp +7 -23
  95. package/src/prelude/values/prototypes/promise.hpp +8 -186
  96. package/src/prelude/values/prototypes/string.hpp +28 -553
  97. package/src/prelude/values/prototypes/symbol.hpp +9 -70
  98. package/src/prelude/values/shape.hpp +52 -52
  99. package/src/prelude/values/string.cpp +485 -0
  100. package/src/prelude/values/symbol.cpp +89 -0
  101. package/src/prelude/values/symbol.hpp +101 -101
  102. package/dist/cli/file-utils.js +0 -20
  103. package/dist/cli-utils/args.js +0 -59
  104. package/dist/cli-utils/colors.js +0 -9
  105. package/dist/cli-utils/file-utils.js +0 -20
  106. package/dist/cli-utils/spinner.js +0 -55
  107. package/dist/cli.js +0 -153
  108. package/dist/core/codegen/index.js +0 -86
  109. package/src/prelude/any_value_access.hpp +0 -170
  110. package/src/prelude/any_value_defines.hpp +0 -190
  111. package/src/prelude/any_value_helpers.hpp +0 -374
  112. package/src/prelude/utils/operators_primitive.hpp +0 -337
  113. package/src/prelude/values/helpers/array.hpp +0 -199
  114. package/src/prelude/values/helpers/async_iterator.hpp +0 -275
  115. package/src/prelude/values/helpers/function.hpp +0 -109
  116. package/src/prelude/values/helpers/iterator.hpp +0 -145
  117. package/src/prelude/values/helpers/object.hpp +0 -104
  118. package/src/prelude/values/helpers/promise.hpp +0 -254
  119. package/src/prelude/values/helpers/string.hpp +0 -37
  120. package/src/prelude/values/helpers/symbol.hpp +0 -21
  121. /package/dist/{ast → interpreter/ast}/symbols.js +0 -0
  122. /package/dist/{ast → interpreter/ast}/types.js +0 -0
  123. /package/dist/{core → interpreter/core}/codegen/visitor.js +0 -0
  124. /package/dist/{core → interpreter/core}/constants.js +0 -0
  125. /package/dist/{core → interpreter/core}/error.js +0 -0
  126. /package/dist/{core → interpreter/core}/parser.js +0 -0
  127. /package/dist/{core → interpreter/core}/traverser.js +0 -0
@@ -35,7 +35,7 @@ export function visitClassDeclaration(node, context) {
35
35
  constructorLambda = this.generateWrappedLambda(this.generateLambdaComponents(constructor, {
36
36
  ...classContext,
37
37
  isInsideFunction: true,
38
- lambdaName: className,
38
+ functionName: className,
39
39
  }, { isClass: true }));
40
40
  }
41
41
  else {
@@ -50,7 +50,7 @@ export function visitForStatement(node, context) {
50
50
  type: declType,
51
51
  checks: { initialized: true },
52
52
  });
53
- if (typeInfo.needsHeapAllocation) {
53
+ if (typeInfo?.needsHeapAllocation) {
54
54
  initializerCode =
55
55
  `auto ${name} = std::make_shared<jspp::AnyValue>(${initValue})`;
56
56
  }
@@ -124,7 +124,7 @@ export function visitForInStatement(node, context) {
124
124
  varName = decl.name.getText();
125
125
  const scope = this.getScopeForNode(decl);
126
126
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(varName, scope);
127
- if (typeInfo.needsHeapAllocation) {
127
+ if (typeInfo?.needsHeapAllocation) {
128
128
  code +=
129
129
  `${this.indent()}auto ${varName} = std::make_shared<jspp::AnyValue>(jspp::Constants::UNDEFINED);\n`;
130
130
  assignmentTarget = `*${varName}`;
@@ -140,7 +140,7 @@ export function visitForInStatement(node, context) {
140
140
  varName = forIn.initializer.getText();
141
141
  const scope = this.getScopeForNode(forIn.initializer);
142
142
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(varName, scope);
143
- assignmentTarget = typeInfo.needsHeapAllocation
143
+ assignmentTarget = typeInfo?.needsHeapAllocation
144
144
  ? `*${varName}`
145
145
  : varName;
146
146
  }
@@ -154,11 +154,10 @@ export function visitForInStatement(node, context) {
154
154
  }
155
155
  const keysVar = this.generateUniqueName("__keys_", new Set([varName]));
156
156
  code +=
157
- `${this.indent()}std::vector<std::string> ${keysVar} = jspp::Access::get_object_keys(${derefExpr});\n`;
158
- code += `${this.indent()}for (const auto& ${varName}_str : ${keysVar}) {\n`;
157
+ `${this.indent()}std::vector<jspp::AnyValue> ${keysVar} = jspp::Access::get_object_keys(${derefExpr});\n`;
158
+ code += `${this.indent()}for (const auto& ${varName}_val : ${keysVar}) {\n`;
159
159
  this.indentationLevel++;
160
- code +=
161
- `${this.indent()}${assignmentTarget} = jspp::AnyValue::make_string(${varName}_str);\n`;
160
+ code += `${this.indent()}${assignmentTarget} = ${varName}_val;\n`;
162
161
  code += this.visit(forIn.statement, {
163
162
  ...context,
164
163
  currentLabel: undefined,
@@ -196,7 +195,7 @@ export function visitForOfStatement(node, context) {
196
195
  elemName = decl.name.getText();
197
196
  const scope = this.getScopeForNode(decl);
198
197
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(elemName, scope);
199
- if (typeInfo.needsHeapAllocation) {
198
+ if (typeInfo?.needsHeapAllocation) {
200
199
  code +=
201
200
  `${this.indent()}auto ${elemName} = std::make_shared<jspp::AnyValue>(jspp::Constants::UNDEFINED);\n`;
202
201
  assignmentTarget = `*${elemName}`;
@@ -212,7 +211,7 @@ export function visitForOfStatement(node, context) {
212
211
  elemName = forOf.initializer.getText();
213
212
  const scope = this.getScopeForNode(forOf.initializer);
214
213
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(elemName, scope);
215
- assignmentTarget = typeInfo.needsHeapAllocation
214
+ assignmentTarget = typeInfo?.needsHeapAllocation
216
215
  ? `*${elemName}`
217
216
  : elemName;
218
217
  }
@@ -440,12 +439,14 @@ export function visitSwitchStatement(node, context) {
440
439
  this.markSymbolAsInitialized(funcName, globalScopeSymbols, localScopeSymbols);
441
440
  // Generate native name
442
441
  const nativeName = this.generateUniqueName(`__${funcName}_native_`, hoistedSymbols);
442
+ const argumentKeywordIsUsed = this.isVariableUsedWithoutDeclaration("arguments", stmt.body);
443
443
  hoistedSymbols.update(funcName, {
444
444
  features: {
445
445
  native: {
446
446
  type: "lambda",
447
447
  name: nativeName,
448
448
  parameters: this.validateFunctionParams(stmt.parameters),
449
+ argumentKeywordIsUsed,
449
450
  },
450
451
  },
451
452
  });
@@ -475,13 +476,13 @@ export function visitSwitchStatement(node, context) {
475
476
  let condition = "";
476
477
  if (firstIf) {
477
478
  condition =
478
- `(${fallthroughVar} || jspp::is_strictly_equal_to_primitive(${switchValueVar}, ${caseExprCode}))`;
479
+ `(${fallthroughVar} || jspp::is_strictly_equal_to_native(${switchValueVar}, ${caseExprCode}))`;
479
480
  code += `${this.indent()}if ${condition} {\n`;
480
481
  firstIf = false;
481
482
  }
482
483
  else {
483
484
  condition =
484
- `(${fallthroughVar} || jspp::is_strictly_equal_to_primitive(${switchValueVar}, ${caseExprCode}))`;
485
+ `(${fallthroughVar} || jspp::is_strictly_equal_to_native(${switchValueVar}, ${caseExprCode}))`;
485
486
  code += `${this.indent()}if ${condition} {\n`;
486
487
  }
487
488
  this.indentationLevel++;
@@ -1,4 +1,5 @@
1
1
  import ts from "typescript";
2
+ import { RESERVED_VAR_NAMES } from "../../analysis/scope.js";
2
3
  import { CompilerError } from "../error.js";
3
4
  import { CodeGenerator } from "./index.js";
4
5
  export function visitVariableDeclarationList(node, context) {
@@ -17,6 +18,9 @@ export function visitVariableDeclaration(node, context) {
17
18
  return this.generateDestructuring(varDecl.name, rhsCode, context);
18
19
  }
19
20
  const name = varDecl.name.getText();
21
+ if (RESERVED_VAR_NAMES.has(name)) {
22
+ throw new CompilerError(`Cannot declare a variable named '${name}'.`, varDecl.name, "SyntaxError");
23
+ }
20
24
  const scope = this.getScopeForNode(varDecl);
21
25
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(name, scope);
22
26
  // Mark the symbol as checked
@@ -34,7 +38,7 @@ export function visitVariableDeclaration(node, context) {
34
38
  const initTypeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(initExpr.text, initScope);
35
39
  const varName = this.getJsVarName(initExpr);
36
40
  // Check if both target and initializer are heap allocated
37
- if (typeInfo.needsHeapAllocation &&
41
+ if (typeInfo?.needsHeapAllocation &&
38
42
  initTypeInfo?.needsHeapAllocation) {
39
43
  shouldSkipDeref = true;
40
44
  }
@@ -49,12 +53,14 @@ export function visitVariableDeclaration(node, context) {
49
53
  (ts.isFunctionExpression(initExpr) && !initExpr.name)) {
50
54
  const initContext = {
51
55
  ...context,
52
- lambdaName: name, // Use the variable name as function name
56
+ functionName: name, // Use the variable name as function name
53
57
  };
54
58
  const nativeName = this.generateUniqueName(`__${name}_native_`, context.localScopeSymbols, context.globalScopeSymbols);
55
59
  const scopeNode = ts.isVariableDeclarationList(varDecl.parent)
56
60
  ? varDecl.parent.parent.parent
57
61
  : varDecl.parent;
62
+ const argumentKeywordIsUsed = ts.isFunctionExpression(initExpr) &&
63
+ this.isVariableUsedWithoutDeclaration("arguments", initExpr.body);
58
64
  // Mark before further visits
59
65
  context.localScopeSymbols.update(name, {
60
66
  features: {
@@ -62,6 +68,7 @@ export function visitVariableDeclaration(node, context) {
62
68
  type: "lambda",
63
69
  name: nativeName,
64
70
  parameters: this.validateFunctionParams(initExpr.parameters),
71
+ argumentKeywordIsUsed,
65
72
  },
66
73
  },
67
74
  });
@@ -85,16 +92,28 @@ export function visitVariableDeclaration(node, context) {
85
92
  return nativeLambdaCode;
86
93
  }
87
94
  }
88
- initializer = " = " + initText;
95
+ initializer = initText;
89
96
  }
90
97
  const isLetOrConst = (varDecl.parent.flags & (ts.NodeFlags.Let | ts.NodeFlags.Const)) !== 0;
91
98
  const shouldDeref = context.derefBeforeAssignment &&
92
99
  (!context.localScopeSymbols.has(name));
93
- const assignmentTarget = shouldDeref
100
+ let assignmentTarget = shouldDeref
94
101
  ? this.getDerefCode(name, name, context, typeInfo)
95
- : (typeInfo.needsHeapAllocation && !shouldSkipDeref
102
+ : (typeInfo?.needsHeapAllocation && !shouldSkipDeref
96
103
  ? `*${name}`
97
104
  : name);
105
+ const sym = context.localScopeSymbols.get(name) ||
106
+ context.globalScopeSymbols.get(name);
107
+ if (sym?.checks.skippedHoisting) {
108
+ assignmentTarget = typeInfo?.needsHeapAllocation && !shouldSkipDeref
109
+ ? `auto ${name}`
110
+ : `jspp::AnyValue ${name}`;
111
+ if (initializer) {
112
+ initializer = typeInfo?.needsHeapAllocation && !shouldSkipDeref
113
+ ? `std::make_shared<jspp::AnyValue>(${initializer})`
114
+ : initializer;
115
+ }
116
+ }
98
117
  if (nativeLambdaCode)
99
118
  nativeLambdaCode += `;\n${this.indent()}`;
100
119
  if (isLetOrConst) {
@@ -109,21 +128,21 @@ export function visitVariableDeclaration(node, context) {
109
128
  return `${nativeLambdaCode}${assignmentTarget} = jspp::Constants::UNDEFINED`;
110
129
  }
111
130
  }
112
- return `${nativeLambdaCode}${assignmentTarget}${initializer}`;
131
+ return `${nativeLambdaCode}${assignmentTarget} = ${initializer}`;
113
132
  }
114
133
  // For 'var', it's a bit more complex.
115
134
  if (context.isAssignmentOnly) {
116
135
  if (!initializer)
117
136
  return "";
118
- return `${nativeLambdaCode}${assignmentTarget}${initializer}`;
137
+ return `${nativeLambdaCode}${assignmentTarget} = ${initializer}`;
119
138
  }
120
139
  else {
121
140
  // This case should not be hit with the new hoisting logic,
122
141
  // but is kept for safety.
123
142
  const initValue = initializer
124
- ? initializer.substring(3)
143
+ ? initializer
125
144
  : "jspp::Constants::UNDEFINED";
126
- if (typeInfo.needsHeapAllocation) {
145
+ if (typeInfo?.needsHeapAllocation) {
127
146
  return `auto ${name} = std::make_shared<jspp::AnyValue>(${initValue})`;
128
147
  }
129
148
  else {
@@ -1,4 +1,6 @@
1
1
  import ts from "typescript";
2
+ import { RESERVED_VAR_NAMES } from "../../analysis/scope.js";
3
+ import { CompilerError } from "../error.js";
2
4
  import { visitObjectPropertyName } from "./expression-handlers.js";
3
5
  import { CodeGenerator } from "./index.js";
4
6
  import {} from "./visitor.js";
@@ -11,6 +13,9 @@ export function generateDestructuring(lhs, rhsCode, context) {
11
13
  const genAssignment = (pattern, valueCode) => {
12
14
  if (ts.isIdentifier(pattern)) {
13
15
  const name = pattern.text;
16
+ if (RESERVED_VAR_NAMES.has(name)) {
17
+ throw new CompilerError(`Cannot destructure to a variable named '${name}'.`, pattern, "SyntaxError");
18
+ }
14
19
  const scope = this.getScopeForNode(pattern);
15
20
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(name, scope);
16
21
  // Mark initialized if it's a declaration
@@ -103,7 +108,7 @@ export function generateDestructuring(lhs, rhsCode, context) {
103
108
  });
104
109
  // Call the return method to clean resources
105
110
  innerCode +=
106
- `${this.indent()}jspp::Access::call_optional_property_with_optional_call(${iterVar}, "return", {}, "return");\n`;
111
+ `${this.indent()}jspp::Access::call_optional_property_with_optional_call(${iterVar}, "return", {});\n`;
107
112
  return innerCode;
108
113
  }
109
114
  else if (ts.isObjectBindingPattern(pattern) ||
@@ -150,9 +155,9 @@ export function generateDestructuring(lhs, rhsCode, context) {
150
155
  }
151
156
  if (isRest) {
152
157
  const keysArray = `{${seenKeys.map((k) => k.startsWith('"') && k.endsWith('"')
153
- ? k
154
- : `(${k}).to_property_key()`).join(", ")}}`;
155
- const restValueCode = `jspp::Access::get_rest_object(${valueCode}, std::vector<std::string>${keysArray})`;
158
+ ? `jspp::AnyValue::make_string(${k})`
159
+ : `(${k})`).join(", ")}}`;
160
+ const restValueCode = `jspp::Access::get_rest_object(${valueCode}, std::vector<jspp::AnyValue>${keysArray})`;
156
161
  innerCode += genAssignment(target, restValueCode);
157
162
  }
158
163
  else {
@@ -84,11 +84,11 @@ export function visitObjectLiteralExpression(node, context) {
84
84
  ts.isMethodDeclaration(prop) || ts.isGetAccessor(prop) ||
85
85
  ts.isSetAccessor(prop) || ts.isSpreadAssignment(prop))) {
86
86
  // Empty object
87
- return `jspp::AnyValue::make_object_with_proto({}, ::Object.get_own_property("prototype"))`;
87
+ return `jspp::AnyValue::make_object({}).set_prototype(::Object.get_own_property("prototype"))`;
88
88
  }
89
89
  let code = `([&]() {\n`;
90
90
  code +=
91
- `${this.indent()} auto ${objVar} = jspp::AnyValue::make_object_with_proto({}, ::Object.get_own_property("prototype"));\n`;
91
+ `${this.indent()} auto ${objVar} = jspp::AnyValue::make_object({}).set_prototype(::Object.get_own_property("prototype"));\n`;
92
92
  this.indentationLevel++;
93
93
  for (const prop of properties) {
94
94
  if (ts.isPropertyAssignment(prop)) {
@@ -200,7 +200,7 @@ export function visitArrayLiteralExpression(node, context) {
200
200
  const elementsSpan = elements.length > 0
201
201
  ? `std::span<const jspp::AnyValue>((const jspp::AnyValue[]){${elementsJoined}}, ${elements.length})`
202
202
  : "std::span<const jspp::AnyValue>{}";
203
- return `jspp::AnyValue::make_array_with_proto(${elementsSpan}, ::Array.get_own_property("prototype"))`;
203
+ return `jspp::AnyValue::make_array(${elementsSpan}).set_prototype(::Array.get_own_property("prototype"))`;
204
204
  }
205
205
  const arrVar = this.generateUniqueName("__arr_", this.getDeclaredSymbols(node));
206
206
  let code = `([&]() {\n`;
@@ -238,7 +238,7 @@ export function visitArrayLiteralExpression(node, context) {
238
238
  }
239
239
  this.indentationLevel--;
240
240
  code +=
241
- `${this.indent()} return jspp::AnyValue::make_array_with_proto(std::move(${arrVar}), ::Array.get_own_property("prototype"));\n`;
241
+ `${this.indent()} return jspp::AnyValue::make_array(std::move(${arrVar})).set_prototype(::Array.get_own_property("prototype"));\n`;
242
242
  code += `${this.indent()}})()`;
243
243
  return code;
244
244
  }
@@ -254,7 +254,7 @@ export function visitPrefixUnaryExpression(node, context) {
254
254
  if (context.derefBeforeAssignment) {
255
255
  target = this.getDerefCode(operand, operand, context, typeInfo);
256
256
  }
257
- else if (typeInfo.needsHeapAllocation) {
257
+ else if (typeInfo?.needsHeapAllocation) {
258
258
  target = `*${operand}`;
259
259
  }
260
260
  }
@@ -277,7 +277,7 @@ export function visitPrefixUnaryExpression(node, context) {
277
277
  if (context.derefBeforeAssignment) {
278
278
  target = this.getDerefCode(operand, operand, context, typeInfo);
279
279
  }
280
- else if (typeInfo.needsHeapAllocation) {
280
+ else if (typeInfo?.needsHeapAllocation) {
281
281
  target = `*${operand}`;
282
282
  }
283
283
  }
@@ -296,7 +296,7 @@ export function visitPostfixUnaryExpression(node, context) {
296
296
  if (context.derefBeforeAssignment) {
297
297
  target = this.getDerefCode(operand, operand, context, typeInfo);
298
298
  }
299
- else if (typeInfo.needsHeapAllocation) {
299
+ else if (typeInfo?.needsHeapAllocation) {
300
300
  target = `*${operand}`;
301
301
  }
302
302
  }
@@ -381,7 +381,6 @@ export function visitBinaryExpression(node, context) {
381
381
  const op = opToken.getText();
382
382
  const visitContext = {
383
383
  ...context,
384
- supportedNativeLiterals: undefined,
385
384
  };
386
385
  const assignmentOperators = [
387
386
  ts.SyntaxKind.PlusEqualsToken,
@@ -409,7 +408,7 @@ export function visitBinaryExpression(node, context) {
409
408
  if (ts.isIdentifier(binExpr.left)) {
410
409
  const scope = this.getScopeForNode(binExpr.left);
411
410
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.left.text, scope);
412
- target = typeInfo.needsHeapAllocation
411
+ target = typeInfo?.needsHeapAllocation
413
412
  ? `*${leftText}`
414
413
  : leftText;
415
414
  return `${target} = jspp::unsigned_right_shift(${target}, ${rightText})`;
@@ -422,7 +421,7 @@ export function visitBinaryExpression(node, context) {
422
421
  if (ts.isIdentifier(binExpr.left)) {
423
422
  const scope = this.getScopeForNode(binExpr.left);
424
423
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.left.text, scope);
425
- target = typeInfo.needsHeapAllocation
424
+ target = typeInfo?.needsHeapAllocation
426
425
  ? `*${leftText}`
427
426
  : leftText;
428
427
  return `${target} = jspp::pow(${target}, ${rightText})`;
@@ -438,7 +437,7 @@ export function visitBinaryExpression(node, context) {
438
437
  if (ts.isIdentifier(binExpr.left)) {
439
438
  const scope = this.getScopeForNode(binExpr.left);
440
439
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.left.text, scope);
441
- target = typeInfo.needsHeapAllocation
440
+ target = typeInfo?.needsHeapAllocation
442
441
  ? `*${leftText}`
443
442
  : leftText;
444
443
  if (opToken.kind === ts.SyntaxKind.AmpersandAmpersandEqualsToken) {
@@ -468,7 +467,7 @@ export function visitBinaryExpression(node, context) {
468
467
  if (context.derefBeforeAssignment) {
469
468
  target = this.getDerefCode(leftText, leftText, visitContext, typeInfo);
470
469
  }
471
- else if (typeInfo.needsHeapAllocation) {
470
+ else if (typeInfo?.needsHeapAllocation) {
472
471
  target = `*${leftText}`;
473
472
  }
474
473
  }
@@ -575,7 +574,7 @@ export function visitBinaryExpression(node, context) {
575
574
  }
576
575
  const target = context.derefBeforeAssignment
577
576
  ? this.getDerefCode(leftText, leftText, visitContext, typeInfo)
578
- : (typeInfo.needsHeapAllocation ? `*${leftText}` : leftText);
577
+ : (typeInfo?.needsHeapAllocation ? `*${leftText}` : leftText);
579
578
  // Update scope symbols on variable re-assignment
580
579
  // Reset features
581
580
  if (ts.isIdentifier(binExpr.left)) {
@@ -608,7 +607,8 @@ export function visitBinaryExpression(node, context) {
608
607
  finalLeft = this.getDerefCode(leftText, this.getJsVarName(binExpr.left), visitContext, typeInfo);
609
608
  }
610
609
  // Number optimizations
611
- if (typeInfo && typeInfo.type === "number") {
610
+ const nodeType = this.typeAnalyzer.inferNodeReturnType(binExpr.left);
611
+ if (nodeType === "number") {
612
612
  finalLeft = `${finalLeft}.as_double()`;
613
613
  }
614
614
  }
@@ -624,7 +624,8 @@ export function visitBinaryExpression(node, context) {
624
624
  finalRight = this.getDerefCode(rightText, this.getJsVarName(binExpr.right), visitContext, typeInfo);
625
625
  }
626
626
  // Number optimizations
627
- if (typeInfo && typeInfo.type === "number") {
627
+ const nodeType = this.typeAnalyzer.inferNodeReturnType(binExpr.right);
628
+ if (nodeType === "number") {
628
629
  finalRight = `${finalRight}.as_double()`;
629
630
  }
630
631
  }
@@ -643,100 +644,82 @@ export function visitBinaryExpression(node, context) {
643
644
  if (opToken.kind === ts.SyntaxKind.QuestionQuestionToken) {
644
645
  return `jspp::nullish_coalesce(${finalLeft}, ${finalRight})`;
645
646
  }
646
- const isLiteral = (n) => ts.isNumericLiteral(n);
647
- const supportsNativeBoolean = context.supportedNativeLiterals?.includes("boolean") || false;
648
647
  // Native values for lhs and rhs
649
- const literalLeft = isLiteral(binExpr.left)
648
+ const literalLeft = ts.isNumericLiteral(binExpr.left)
650
649
  ? binExpr.left.getText()
651
650
  : finalLeft;
652
- const literalRight = isLiteral(binExpr.right)
651
+ const literalRight = ts.isNumericLiteral(binExpr.right)
653
652
  ? binExpr.right.getText()
654
653
  : finalRight;
655
- // Operations that returns boolean should return the native boolean if supported
656
- if (constants.booleanOperators.includes(opToken.kind) &&
657
- supportsNativeBoolean) {
658
- if (opToken.kind === ts.SyntaxKind.EqualsEqualsEqualsToken) {
659
- return `jspp::is_strictly_equal_to_primitive(${literalLeft}, ${literalRight})`;
660
- }
661
- if (opToken.kind === ts.SyntaxKind.EqualsEqualsToken) {
662
- return `jspp::is_equal_to_primitive(${literalLeft}, ${literalRight})`;
663
- }
664
- if (opToken.kind === ts.SyntaxKind.ExclamationEqualsEqualsToken) {
665
- return `!jspp::is_strictly_equal_to_primitive(${literalLeft}, ${literalRight})`;
666
- }
667
- if (opToken.kind === ts.SyntaxKind.ExclamationEqualsToken) {
668
- return `!jspp::is_equal_to_primitive(${literalLeft}, ${literalRight})`;
669
- }
670
- let funcName = "";
671
- if (opToken.kind === ts.SyntaxKind.LessThanToken) {
672
- funcName = "jspp::less_than_primitive";
673
- }
674
- if (opToken.kind === ts.SyntaxKind.LessThanEqualsToken) {
675
- funcName = "jspp::less_than_or_equal_primitive";
676
- }
677
- if (opToken.kind === ts.SyntaxKind.GreaterThanToken) {
678
- funcName = "jspp::greater_than_primitive";
679
- }
680
- if (opToken.kind === ts.SyntaxKind.GreaterThanEqualsToken) {
681
- funcName = "jspp::greater_than_or_equal_primitive";
654
+ let supportsNativeValue = false;
655
+ const exprReturnType = this.typeAnalyzer.inferNodeReturnType(node);
656
+ if (exprReturnType === "boolean" &&
657
+ (ts.isIfStatement(node.parent) ||
658
+ ts.isConditionalExpression(node.parent))) {
659
+ supportsNativeValue = true;
660
+ }
661
+ else if (exprReturnType === "number" &&
662
+ context.isInsideNativeLambda &&
663
+ context.isInsideFunction) {
664
+ const funcDecl = this
665
+ .findEnclosingFunctionDeclarationFromReturnStatement(node);
666
+ if (funcDecl) {
667
+ const funcReturnType = this.typeAnalyzer.inferFunctionReturnType(funcDecl);
668
+ if (funcReturnType === "number") {
669
+ supportsNativeValue = true;
670
+ }
682
671
  }
683
- // For C++ primitive literals, standard operators are fine if they map directly,
684
- // but we are safe using our functions (which handle doubles correctly).
685
- // Actually, for pure numeric literals like "1 < 2", we can leave it as is if we want optimization,
686
- // but consistency is safer.
687
- // Let's stick to valid C++ syntax for literals if possible to avoid overhead?
688
- // jspp::less_than(1, 2) works.
689
- return `${funcName}(${literalLeft}, ${literalRight})`;
690
672
  }
673
+ const method = supportsNativeValue ? "_native" : "";
691
674
  // Return boxed value
692
675
  if (opToken.kind === ts.SyntaxKind.EqualsEqualsEqualsToken) {
693
- return `jspp::is_strictly_equal_to(${literalLeft}, ${literalRight})`;
676
+ return `jspp::is_strictly_equal_to${method}(${literalLeft}, ${literalRight})`;
694
677
  }
695
678
  if (opToken.kind === ts.SyntaxKind.EqualsEqualsToken) {
696
- return `jspp::is_equal_to(${literalLeft}, ${literalRight})`;
679
+ return `jspp::is_equal_to${method}(${literalLeft}, ${literalRight})`;
697
680
  }
698
681
  if (opToken.kind === ts.SyntaxKind.ExclamationEqualsEqualsToken) {
699
- return `jspp::not_strictly_equal_to(${literalLeft}, ${literalRight})`;
682
+ return `jspp::not_strictly_equal_to${method}(${literalLeft}, ${literalRight})`;
700
683
  }
701
684
  if (opToken.kind === ts.SyntaxKind.ExclamationEqualsToken) {
702
- return `jspp::not_equal_to(${literalLeft}, ${literalRight})`;
685
+ return `jspp::not_equal_to${method}(${literalLeft}, ${literalRight})`;
703
686
  }
704
687
  if (opToken.kind === ts.SyntaxKind.AsteriskAsteriskToken) {
705
- return `jspp::pow(${literalLeft}, ${literalRight})`;
688
+ return `jspp::pow${method}(${literalLeft}, ${literalRight})`;
706
689
  }
707
690
  if (opToken.kind === ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken) {
708
- return `jspp::unsigned_right_shift(${literalLeft}, ${literalRight})`;
691
+ return `jspp::unsigned_right_shift${method}(${literalLeft}, ${literalRight})`;
709
692
  }
710
693
  // For other arithmetic and bitwise operations, use native operations if possible
711
694
  switch (op) {
712
695
  case "+":
713
- return `jspp::add(${literalLeft}, ${literalRight})`;
696
+ return `jspp::add${method}(${literalLeft}, ${literalRight})`;
714
697
  case "-":
715
- return `jspp::sub(${literalLeft}, ${literalRight})`;
698
+ return `jspp::sub${method}(${literalLeft}, ${literalRight})`;
716
699
  case "*":
717
- return `jspp::mul(${literalLeft}, ${literalRight})`;
700
+ return `jspp::mul${method}(${literalLeft}, ${literalRight})`;
718
701
  case "/":
719
- return `jspp::div(${literalLeft}, ${literalRight})`;
702
+ return `jspp::div${method}(${literalLeft}, ${literalRight})`;
720
703
  case "%":
721
- return `jspp::mod(${literalLeft}, ${literalRight})`;
704
+ return `jspp::mod${method}(${literalLeft}, ${literalRight})`;
722
705
  case "^":
723
- return `jspp::bitwise_xor(${literalLeft}, ${literalRight})`;
706
+ return `jspp::bitwise_xor${method}(${literalLeft}, ${literalRight})`;
724
707
  case "&":
725
- return `jspp::bitwise_and(${literalLeft}, ${literalRight})`;
708
+ return `jspp::bitwise_and${method}(${literalLeft}, ${literalRight})`;
726
709
  case "|":
727
- return `jspp::bitwise_or(${literalLeft}, ${literalRight})`;
710
+ return `jspp::bitwise_or${method}(${literalLeft}, ${literalRight})`;
728
711
  case "<<":
729
- return `jspp::left_shift(${literalLeft}, ${literalRight})`;
712
+ return `jspp::left_shift${method}(${literalLeft}, ${literalRight})`;
730
713
  case ">>":
731
- return `jspp::right_shift(${literalLeft}, ${literalRight})`;
714
+ return `jspp::right_shift${method}(${literalLeft}, ${literalRight})`;
732
715
  case "<":
733
- return `jspp::less_than(${literalLeft}, ${literalRight})`;
716
+ return `jspp::less_than${method}(${literalLeft}, ${literalRight})`;
734
717
  case ">":
735
- return `jspp::greater_than(${literalLeft}, ${literalRight})`;
718
+ return `jspp::greater_than${method}(${literalLeft}, ${literalRight})`;
736
719
  case "<=":
737
- return `jspp::less_than_or_equal(${literalLeft}, ${literalRight})`;
720
+ return `jspp::less_than_or_equal${method}(${literalLeft}, ${literalRight})`;
738
721
  case ">=":
739
- return `jspp::greater_than_or_equal(${literalLeft}, ${literalRight})`;
722
+ return `jspp::greater_than_or_equal${method}(${literalLeft}, ${literalRight})`;
740
723
  }
741
724
  return `/* Unhandled Operator: ${finalLeft} ${op} ${finalRight} */`; // Default fallback
742
725
  }
@@ -744,10 +727,7 @@ export function visitConditionalExpression(node, context) {
744
727
  const condExpr = node;
745
728
  const isBinaryExpression = ts.isBinaryExpression(condExpr.condition) &&
746
729
  constants.booleanOperators.includes(condExpr.condition.operatorToken.kind);
747
- const condition = this.visit(condExpr.condition, {
748
- ...context,
749
- supportedNativeLiterals: isBinaryExpression ? ["boolean"] : undefined,
750
- });
730
+ const condition = this.visit(condExpr.condition, context);
751
731
  const whenTrueStmt = this.visit(condExpr.whenTrue, {
752
732
  ...context,
753
733
  isFunctionBody: false,
@@ -1023,6 +1003,7 @@ export function visitCallExpression(node, context) {
1023
1003
  if (nativeFeature && nativeFeature.type === "lambda") {
1024
1004
  const nativeName = nativeFeature.name;
1025
1005
  const parameters = nativeFeature.parameters || [];
1006
+ const argumentKeywordIsUsed = nativeFeature.argumentKeywordIsUsed;
1026
1007
  if (!hasSpread) {
1027
1008
  let argsPart = "";
1028
1009
  if (parameters) {
@@ -1041,10 +1022,17 @@ export function visitCallExpression(node, context) {
1041
1022
  .filter((_, i) => !parameters[i]?.dotDotDotToken).join(", ");
1042
1023
  if (argsText)
1043
1024
  argsPart += `, ${argsText}`;
1044
- if (argsArray.length > parameters.length &&
1025
+ if (argsArray.length >= parameters.length &&
1045
1026
  !!parameters[parameters.length - 1]?.dotDotDotToken) {
1046
1027
  const restArgsText = `jspp::AnyValue::make_array(std::vector<jspp::AnyValue>{${argsArray.slice(parameters.length - 1).join(", ")}})`;
1047
- argsPart += `, ${restArgsText}`;
1028
+ argsPart += `, ${restArgsText}${argumentKeywordIsUsed ? `, ${argsArray.length}` : ""}`;
1029
+ }
1030
+ else if (argsArray.length > parameters.length &&
1031
+ argumentKeywordIsUsed) {
1032
+ const restArgsArray = argsArray.slice(parameters.length);
1033
+ const nativeRestArgsText = `std::span<const jspp::AnyValue>((const jspp::AnyValue[]){${restArgsArray.join(", ")}}, ${restArgsArray.length})`;
1034
+ argsPart +=
1035
+ `, ${argsArray.length}, ${nativeRestArgsText}`;
1048
1036
  }
1049
1037
  }
1050
1038
  const callImplementation = `${nativeName}(jspp::Constants::UNDEFINED${argsPart})`;
@@ -1070,28 +1058,34 @@ export function visitCallExpression(node, context) {
1070
1058
  if (!p)
1071
1059
  continue;
1072
1060
  if (p.dotDotDotToken) {
1073
- callArgs.push(`jspp::AnyValue::make_array(std::vector<jspp::AnyValue>(${argsVar}.begin() + std::min((size_t)${i}, ${argsVar}.size()), ${argsVar}.end()))`);
1061
+ callArgs.push(`jspp::AnyValue::make_array(std::vector<jspp::AnyValue>(${argsVar}.begin() + std::min((std::size_t)${i}, ${argsVar}.size()), ${argsVar}.end()))`);
1074
1062
  }
1075
1063
  else {
1076
1064
  callArgs.push(`(${argsVar}.size() > ${i} ? ${argsVar}[${i}] : jspp::Constants::UNDEFINED)`);
1077
1065
  }
1078
1066
  }
1079
- let callExprStr = `${nativeName}(jspp::Constants::UNDEFINED${callArgs.length > 0 ? ", " + callArgs.join(", ") : ""})`;
1067
+ const callArgsPart = callArgs.length > 0
1068
+ ? `, ${callArgs.join(", ")}`
1069
+ : "";
1070
+ const totalArgsSizePart = argumentKeywordIsUsed
1071
+ ? `, ${argsVar}.size()`
1072
+ : "";
1073
+ let callImplementation = `${nativeName}(jspp::Constants::UNDEFINED${callArgsPart}${totalArgsSizePart})`;
1080
1074
  if (symbol.features.isGenerator) {
1081
1075
  if (symbol.features.isAsync) {
1082
- callExprStr =
1083
- `jspp::AnyValue::from_async_iterator(${callExprStr})`;
1076
+ callImplementation =
1077
+ `jspp::AnyValue::from_async_iterator(${callImplementation})`;
1084
1078
  }
1085
1079
  else {
1086
- callExprStr =
1087
- `jspp::AnyValue::from_iterator(${callExprStr})`;
1080
+ callImplementation =
1081
+ `jspp::AnyValue::from_iterator(${callImplementation})`;
1088
1082
  }
1089
1083
  }
1090
1084
  else if (symbol.features.isAsync) {
1091
- callExprStr =
1092
- `jspp::AnyValue::from_promise(${callExprStr})`;
1085
+ callImplementation =
1086
+ `jspp::AnyValue::from_promise(${callImplementation})`;
1093
1087
  }
1094
- code += `${this.indent()}return ${callExprStr};\n`;
1088
+ code += `${this.indent()}return ${callImplementation};\n`;
1095
1089
  this.indentationLevel--;
1096
1090
  code += `${this.indent()}})()`;
1097
1091
  return code;