@ugo-studio/jspp 0.2.9 → 0.3.1

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 (97) hide show
  1. package/LICENSE +25 -25
  2. package/README.md +20 -12
  3. package/dist/analysis/scope.js +5 -3
  4. package/dist/analysis/typeAnalyzer.js +21 -25
  5. package/dist/cli/index.js +14 -4
  6. package/dist/cli/utils.js +61 -0
  7. package/dist/core/codegen/class-handlers.js +6 -6
  8. package/dist/core/codegen/control-flow-handlers.js +10 -9
  9. package/dist/core/codegen/declaration-handlers.js +10 -3
  10. package/dist/core/codegen/destructuring-handlers.js +9 -4
  11. package/dist/core/codegen/expression-handlers.js +40 -29
  12. package/dist/core/codegen/function-handlers.js +78 -12
  13. package/dist/core/codegen/helpers.js +91 -14
  14. package/dist/core/codegen/index.js +4 -2
  15. package/dist/core/codegen/statement-handlers.js +9 -7
  16. package/package.json +2 -2
  17. package/scripts/precompile-headers.ts +249 -50
  18. package/scripts/setup-compiler.ts +63 -63
  19. package/src/prelude/any_value.cpp +636 -0
  20. package/src/prelude/any_value.hpp +369 -362
  21. package/src/prelude/{exception_helpers.hpp → exception.cpp} +53 -53
  22. package/src/prelude/exception.hpp +27 -27
  23. package/src/prelude/iterator_instantiations.hpp +10 -0
  24. package/src/prelude/{index.hpp → jspp.hpp} +10 -16
  25. package/src/prelude/library/array.cpp +191 -0
  26. package/src/prelude/library/array.hpp +13 -186
  27. package/src/prelude/library/console.cpp +125 -0
  28. package/src/prelude/library/console.hpp +24 -112
  29. package/src/prelude/library/error.cpp +100 -0
  30. package/src/prelude/library/error.hpp +13 -113
  31. package/src/prelude/library/function.cpp +69 -0
  32. package/src/prelude/library/function.hpp +11 -10
  33. package/src/prelude/library/global.cpp +96 -0
  34. package/src/prelude/library/global.hpp +12 -28
  35. package/src/prelude/library/global_usings.hpp +15 -0
  36. package/src/prelude/library/math.cpp +258 -0
  37. package/src/prelude/library/math.hpp +26 -308
  38. package/src/prelude/library/object.cpp +379 -0
  39. package/src/prelude/library/object.hpp +14 -276
  40. package/src/prelude/library/performance.cpp +21 -0
  41. package/src/prelude/library/performance.hpp +5 -20
  42. package/src/prelude/library/process.cpp +38 -0
  43. package/src/prelude/library/process.hpp +11 -39
  44. package/src/prelude/library/promise.cpp +131 -0
  45. package/src/prelude/library/promise.hpp +12 -123
  46. package/src/prelude/library/symbol.cpp +56 -0
  47. package/src/prelude/library/symbol.hpp +11 -52
  48. package/src/prelude/library/timer.cpp +88 -0
  49. package/src/prelude/library/timer.hpp +16 -92
  50. package/src/prelude/runtime.cpp +19 -0
  51. package/src/prelude/types.hpp +184 -179
  52. package/src/prelude/utils/access.hpp +502 -411
  53. package/src/prelude/utils/assignment_operators.hpp +99 -99
  54. package/src/prelude/utils/log_any_value/array.hpp +61 -40
  55. package/src/prelude/utils/log_any_value/function.hpp +39 -39
  56. package/src/prelude/utils/log_any_value/object.hpp +60 -3
  57. package/src/prelude/utils/operators.hpp +351 -336
  58. package/src/prelude/utils/operators_primitive.hpp +336 -336
  59. package/src/prelude/utils/well_known_symbols.hpp +24 -24
  60. package/src/prelude/values/array.cpp +1399 -0
  61. package/src/prelude/values/array.hpp +4 -1
  62. package/src/prelude/values/async_iterator.cpp +251 -0
  63. package/src/prelude/values/async_iterator.hpp +111 -83
  64. package/src/prelude/values/function.cpp +262 -0
  65. package/src/prelude/values/function.hpp +62 -82
  66. package/src/prelude/values/iterator.cpp +309 -0
  67. package/src/prelude/values/iterator.hpp +33 -64
  68. package/src/prelude/values/number.cpp +176 -0
  69. package/src/prelude/values/object.cpp +159 -0
  70. package/src/prelude/values/object.hpp +4 -0
  71. package/src/prelude/values/promise.cpp +479 -0
  72. package/src/prelude/values/promise.hpp +79 -72
  73. package/src/prelude/values/prototypes/array.hpp +46 -1336
  74. package/src/prelude/values/prototypes/async_iterator.hpp +19 -61
  75. package/src/prelude/values/prototypes/function.hpp +7 -46
  76. package/src/prelude/values/prototypes/iterator.hpp +25 -201
  77. package/src/prelude/values/prototypes/number.hpp +23 -210
  78. package/src/prelude/values/prototypes/object.hpp +7 -23
  79. package/src/prelude/values/prototypes/promise.hpp +18 -196
  80. package/src/prelude/values/prototypes/string.hpp +39 -542
  81. package/src/prelude/values/prototypes/symbol.hpp +9 -70
  82. package/src/prelude/values/shape.hpp +52 -52
  83. package/src/prelude/values/string.cpp +485 -0
  84. package/src/prelude/values/string.hpp +25 -26
  85. package/src/prelude/values/symbol.cpp +89 -0
  86. package/src/prelude/values/symbol.hpp +101 -101
  87. package/src/prelude/any_value_access.hpp +0 -170
  88. package/src/prelude/any_value_defines.hpp +0 -190
  89. package/src/prelude/any_value_helpers.hpp +0 -374
  90. package/src/prelude/values/helpers/array.hpp +0 -209
  91. package/src/prelude/values/helpers/async_iterator.hpp +0 -275
  92. package/src/prelude/values/helpers/function.hpp +0 -109
  93. package/src/prelude/values/helpers/iterator.hpp +0 -145
  94. package/src/prelude/values/helpers/object.hpp +0 -104
  95. package/src/prelude/values/helpers/promise.hpp +0 -254
  96. package/src/prelude/values/helpers/string.hpp +0 -61
  97. package/src/prelude/values/helpers/symbol.hpp +0 -21
@@ -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)) {
@@ -644,7 +643,8 @@ export function visitBinaryExpression(node, context) {
644
643
  return `jspp::nullish_coalesce(${finalLeft}, ${finalRight})`;
645
644
  }
646
645
  const isLiteral = (n) => ts.isNumericLiteral(n);
647
- const supportsNativeBoolean = context.supportedNativeLiterals?.includes("boolean") || false;
646
+ const supportsNativeBoolean = ts.isIfStatement(node.parent) ||
647
+ ts.isConditionalExpression(node.parent);
648
648
  // Native values for lhs and rhs
649
649
  const literalLeft = isLiteral(binExpr.left)
650
650
  ? binExpr.left.getText()
@@ -744,10 +744,7 @@ export function visitConditionalExpression(node, context) {
744
744
  const condExpr = node;
745
745
  const isBinaryExpression = ts.isBinaryExpression(condExpr.condition) &&
746
746
  constants.booleanOperators.includes(condExpr.condition.operatorToken.kind);
747
- const condition = this.visit(condExpr.condition, {
748
- ...context,
749
- supportedNativeLiterals: isBinaryExpression ? ["boolean"] : undefined,
750
- });
747
+ const condition = this.visit(condExpr.condition, context);
751
748
  const whenTrueStmt = this.visit(condExpr.whenTrue, {
752
749
  ...context,
753
750
  isFunctionBody: false,
@@ -1023,6 +1020,7 @@ export function visitCallExpression(node, context) {
1023
1020
  if (nativeFeature && nativeFeature.type === "lambda") {
1024
1021
  const nativeName = nativeFeature.name;
1025
1022
  const parameters = nativeFeature.parameters || [];
1023
+ const argumentKeywordIsUsed = nativeFeature.argumentKeywordIsUsed;
1026
1024
  if (!hasSpread) {
1027
1025
  let argsPart = "";
1028
1026
  if (parameters) {
@@ -1041,10 +1039,17 @@ export function visitCallExpression(node, context) {
1041
1039
  .filter((_, i) => !parameters[i]?.dotDotDotToken).join(", ");
1042
1040
  if (argsText)
1043
1041
  argsPart += `, ${argsText}`;
1044
- if (argsArray.length > parameters.length &&
1042
+ if (argsArray.length >= parameters.length &&
1045
1043
  !!parameters[parameters.length - 1]?.dotDotDotToken) {
1046
1044
  const restArgsText = `jspp::AnyValue::make_array(std::vector<jspp::AnyValue>{${argsArray.slice(parameters.length - 1).join(", ")}})`;
1047
- argsPart += `, ${restArgsText}`;
1045
+ argsPart += `, ${restArgsText}${argumentKeywordIsUsed ? `, ${argsArray.length}` : ""}`;
1046
+ }
1047
+ else if (argsArray.length > parameters.length &&
1048
+ argumentKeywordIsUsed) {
1049
+ const restArgsArray = argsArray.slice(parameters.length);
1050
+ const nativeRestArgsText = `std::span<const jspp::AnyValue>((const jspp::AnyValue[]){${restArgsArray.join(", ")}}, ${restArgsArray.length})`;
1051
+ argsPart +=
1052
+ `, ${argsArray.length}, ${nativeRestArgsText}`;
1048
1053
  }
1049
1054
  }
1050
1055
  const callImplementation = `${nativeName}(jspp::Constants::UNDEFINED${argsPart})`;
@@ -1070,28 +1075,34 @@ export function visitCallExpression(node, context) {
1070
1075
  if (!p)
1071
1076
  continue;
1072
1077
  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()))`);
1078
+ callArgs.push(`jspp::AnyValue::make_array(std::vector<jspp::AnyValue>(${argsVar}.begin() + std::min((std::size_t)${i}, ${argsVar}.size()), ${argsVar}.end()))`);
1074
1079
  }
1075
1080
  else {
1076
1081
  callArgs.push(`(${argsVar}.size() > ${i} ? ${argsVar}[${i}] : jspp::Constants::UNDEFINED)`);
1077
1082
  }
1078
1083
  }
1079
- let callExprStr = `${nativeName}(jspp::Constants::UNDEFINED${callArgs.length > 0 ? ", " + callArgs.join(", ") : ""})`;
1084
+ const callArgsPart = callArgs.length > 0
1085
+ ? `, ${callArgs.join(", ")}`
1086
+ : "";
1087
+ const totalArgsSizePart = argumentKeywordIsUsed
1088
+ ? `, ${argsVar}.size()`
1089
+ : "";
1090
+ let callImplementation = `${nativeName}(jspp::Constants::UNDEFINED${callArgsPart}${totalArgsSizePart})`;
1080
1091
  if (symbol.features.isGenerator) {
1081
1092
  if (symbol.features.isAsync) {
1082
- callExprStr =
1083
- `jspp::AnyValue::from_async_iterator(${callExprStr})`;
1093
+ callImplementation =
1094
+ `jspp::AnyValue::from_async_iterator(${callImplementation})`;
1084
1095
  }
1085
1096
  else {
1086
- callExprStr =
1087
- `jspp::AnyValue::from_iterator(${callExprStr})`;
1097
+ callImplementation =
1098
+ `jspp::AnyValue::from_iterator(${callImplementation})`;
1088
1099
  }
1089
1100
  }
1090
1101
  else if (symbol.features.isAsync) {
1091
- callExprStr =
1092
- `jspp::AnyValue::from_promise(${callExprStr})`;
1102
+ callImplementation =
1103
+ `jspp::AnyValue::from_promise(${callImplementation})`;
1093
1104
  }
1094
- code += `${this.indent()}return ${callExprStr};\n`;
1105
+ code += `${this.indent()}return ${callImplementation};\n`;
1095
1106
  this.indentationLevel--;
1096
1107
  code += `${this.indent()}})()`;
1097
1108
  return code;
@@ -5,6 +5,8 @@ import { CodeGenerator } from "./index.js";
5
5
  export function generateLambdaComponents(node, context, options) {
6
6
  const declaredSymbols = this.getDeclaredSymbols(node);
7
7
  const argsName = this.generateUniqueName("__args_", declaredSymbols, context.globalScopeSymbols, context.localScopeSymbols);
8
+ const nativeExcessArgsName = this.generateUniqueName("__excess_args", declaredSymbols, context.globalScopeSymbols, context.localScopeSymbols);
9
+ const nativeTotalArgsSizeName = this.generateUniqueName("__total_args_size", declaredSymbols, context.globalScopeSymbols, context.localScopeSymbols);
8
10
  const isInsideGeneratorFunction = this.isGeneratorFunction(node);
9
11
  const isInsideAsyncFunction = this.isAsyncFunction(node);
10
12
  const isArrow = ts.isArrowFunction(node);
@@ -59,7 +61,7 @@ export function generateLambdaComponents(node, context, options) {
59
61
  nativePreamble +=
60
62
  `${this.indent()}jspp::AnyValue ${this.globalThisVar} = ${finalThisParamName};\n`;
61
63
  }
62
- // Note: Arguments are now automatically copied into the coroutine frame because
64
+ // Note: Arguments are now automatically copied into the coroutine frame because
63
65
  // the lambda parameter is passed by value (std::vector).
64
66
  // We just need to define argsName as a reference to the parameter.
65
67
  preamble +=
@@ -69,17 +71,32 @@ export function generateLambdaComponents(node, context, options) {
69
71
  let nativeFuncArgs = "";
70
72
  let nativeParamsContent = "";
71
73
  this.validateFunctionParams(node.parameters).forEach((p, i) => {
72
- const name = ts.isIdentifier(p.name)
73
- ? p.name.text
74
- : this.generateUniqueName(`__param_native_${i}_`, visitContext.localScopeSymbols);
74
+ const isIdentifier = ts.isIdentifier(p.name);
75
+ const name = isIdentifier ? p.name.text : this.generateUniqueName(`__param_native_${i}_`, visitContext.localScopeSymbols);
75
76
  const defaultValue = p.initializer
76
77
  ? this.visit(p.initializer, visitContext)
77
78
  : !!p.dotDotDotToken
78
79
  ? "jspp::AnyValue::make_array(std::vector<jspp::AnyValue>{})"
79
80
  : "jspp::Constants::UNDEFINED";
80
- nativeFuncArgs += `, jspp::AnyValue ${name} = ${defaultValue}`;
81
- if (!ts.isIdentifier(p.name)) {
82
- nativeParamsContent += this.generateDestructuring(p.name, name, visitContext) + ";\n";
81
+ let signatureName = name;
82
+ let needsTemp = !isIdentifier;
83
+ if (isIdentifier) {
84
+ const scope = this.getScopeForNode(p);
85
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(name, scope);
86
+ if (typeInfo?.needsHeapAllocation) {
87
+ needsTemp = true;
88
+ signatureName = this.generateUniqueName(`__${name}_param_`, visitContext.localScopeSymbols);
89
+ }
90
+ }
91
+ nativeFuncArgs += `, jspp::AnyValue ${signatureName} = ${defaultValue}`;
92
+ if (needsTemp) {
93
+ if (isIdentifier) {
94
+ nativeParamsContent +=
95
+ `${this.indent()}auto ${name} = std::make_shared<jspp::AnyValue>(${signatureName});\n`;
96
+ }
97
+ else {
98
+ nativeParamsContent += this.generateDestructuring(p.name, signatureName, visitContext) + ";\n";
99
+ }
83
100
  }
84
101
  });
85
102
  // Extract lambda parameters from arguments span/vector
@@ -102,7 +119,7 @@ export function generateLambdaComponents(node, context, options) {
102
119
  // Handle rest parameters
103
120
  if (!!p.dotDotDotToken) {
104
121
  const initValue = `jspp::AnyValue::make_array(${argsName}.subspan(${i}))`;
105
- if (typeInfo.needsHeapAllocation) {
122
+ if (typeInfo?.needsHeapAllocation) {
106
123
  paramsCode +=
107
124
  `${this.indent()}auto ${name} = std::make_shared<jspp::AnyValue>(${initValue});\n`;
108
125
  }
@@ -114,7 +131,7 @@ export function generateLambdaComponents(node, context, options) {
114
131
  }
115
132
  // Normal parameter
116
133
  const initValue = `${argsName}.size() > ${i} ? ${argsName}[${i}] : ${defaultValue}`;
117
- if (typeInfo && typeInfo.needsHeapAllocation) {
134
+ if (typeInfo?.needsHeapAllocation) {
118
135
  paramsCode +=
119
136
  `${this.indent()}auto ${name} = std::make_shared<jspp::AnyValue>(${initValue});\n`;
120
137
  }
@@ -139,6 +156,31 @@ export function generateLambdaComponents(node, context, options) {
139
156
  // Generate params and function body
140
157
  let paramsContent = "";
141
158
  let blockContentWithoutOpeningBrace = "";
159
+ // Generate `arguments` variable if it used in the function body
160
+ if (this.isVariableUsedWithoutDeclaration("arguments", node.body)) {
161
+ // Add span parameter for holding the excess arguments from the caller of the native lambda
162
+ nativeFuncArgs +=
163
+ `, const std::size_t ${nativeTotalArgsSizeName}, std::span<const jspp::AnyValue> ${nativeExcessArgsName} = {}`;
164
+ // TODO: compile arguments array from parameters
165
+ // this.indentationLevel++;
166
+ // nativeParamsContent +=
167
+ // `${this.indent()}jspp::AnyValue arguments = jspp::Constants::UNDEFINED;\n`;
168
+ // nativeParamsContent += `${this.indent()}{\n`;
169
+ // this.indentationLevel++;
170
+ // nativeParamsContent +=
171
+ // `${this.indent()}std::vector<jspp::AnyValue> ${argsName};\n`;
172
+ // this.validateFunctionParams(node.parameters).forEach(
173
+ // (p, i) => {
174
+ // },
175
+ // );
176
+ // this.indentationLevel--;
177
+ // nativeParamsContent += `${this.indent()}}\n`;
178
+ // this.indentationLevel--;
179
+ // this.indentationLevel++;
180
+ // paramsContent =
181
+ // `${this.indent()}jspp::AnyValue arguments = jspp::AnyValue::make_array(${argsName});\n`;
182
+ // this.indentationLevel--;
183
+ }
142
184
  if (node.body) {
143
185
  if (ts.isBlock(node.body)) {
144
186
  // Hoist var declarations in the function body
@@ -148,8 +190,16 @@ export function generateLambdaComponents(node, context, options) {
148
190
  preamble += hoistCode;
149
191
  nativePreamble += hoistCode;
150
192
  });
193
+ // Hoist parameters (especially for destructuring)
194
+ node.parameters.forEach((p) => {
195
+ if (!ts.isIdentifier(p.name)) {
196
+ const hoistCode = this.hoistDeclaration(p, visitContext.localScopeSymbols, node.body);
197
+ preamble += hoistCode;
198
+ nativePreamble += hoistCode;
199
+ }
200
+ });
151
201
  this.indentationLevel++;
152
- paramsContent = generateParamsBuilder();
202
+ paramsContent += generateParamsBuilder();
153
203
  this.indentationLevel--;
154
204
  // The block visitor already adds braces, so we need to remove the opening brace to inject the preamble and param extraction.
155
205
  blockContentWithoutOpeningBrace = this.visit(node.body, {
@@ -163,7 +213,7 @@ export function generateLambdaComponents(node, context, options) {
163
213
  }
164
214
  else {
165
215
  this.indentationLevel++;
166
- paramsContent = generateParamsBuilder();
216
+ paramsContent += generateParamsBuilder();
167
217
  blockContentWithoutOpeningBrace =
168
218
  `${this.indent()}${returnCommand} ${this.visit(node.body, {
169
219
  ...visitContext,
@@ -283,7 +333,23 @@ export function generateWrappedLambda(comps) {
283
333
  args += `, "${funcName}"`;
284
334
  }
285
335
  }
286
- const fullExpression = `${method}(${args})`;
336
+ let prototypeExpr = "";
337
+ if (isInsideGeneratorFunction) {
338
+ if (isInsideAsyncFunction) {
339
+ prototypeExpr =
340
+ '::AsyncGeneratorFunction.get_own_property("prototype")';
341
+ }
342
+ else {
343
+ prototypeExpr = '::GeneratorFunction.get_own_property("prototype")';
344
+ }
345
+ }
346
+ else if (isInsideAsyncFunction) {
347
+ prototypeExpr = '::AsyncFunction.get_own_property("prototype")';
348
+ }
349
+ else {
350
+ prototypeExpr = '::Function.get_own_property("prototype")';
351
+ }
352
+ const fullExpression = `${method}(${args}).set_prototype(${prototypeExpr})`;
287
353
  if (ts.isFunctionDeclaration(node) && !isAssignment && node.name) {
288
354
  const funcName = node.name?.getText();
289
355
  return `${this.indent()}auto ${funcName} = ${fullExpression};\n`;
@@ -161,6 +161,9 @@ export function getJsVarName(node) {
161
161
  * @returns The C++ code for accessing the variable's value.
162
162
  */
163
163
  export function getDerefCode(nodeText, varName, context, typeInfo) {
164
+ if (typeInfo?.isBuiltin) {
165
+ return nodeText;
166
+ }
164
167
  // Make sure varName is incased in quotes
165
168
  if (!varName.startsWith('"'))
166
169
  varName = '"' + varName;
@@ -230,21 +233,23 @@ export function getReturnCommand(context) {
230
233
  * @throws CompilerError if a duplicate declaration is found.
231
234
  */
232
235
  export function hoistDeclaration(decl, hoistedSymbols, scopeNode) {
233
- const isLet = ts.isVariableDeclaration(decl) &&
234
- (decl.parent.flags & (ts.NodeFlags.Let)) !== 0;
236
+ const isLet = ts.isParameter(decl) || (ts.isVariableDeclaration(decl) &&
237
+ (decl.parent.flags & (ts.NodeFlags.Let)) !== 0);
235
238
  const isConst = ts.isVariableDeclaration(decl) &&
236
239
  (decl.parent.flags & (ts.NodeFlags.Const)) !== 0;
237
- const declType = isLet
240
+ const declType = ts.isParameter(decl)
238
241
  ? DeclarationType.let
239
- : isConst
240
- ? DeclarationType.const
241
- : ts.isFunctionDeclaration(decl)
242
- ? DeclarationType.function
243
- : ts.isClassDeclaration(decl)
244
- ? DeclarationType.class
245
- : ts.isEnumDeclaration(decl)
246
- ? DeclarationType.enum
247
- : DeclarationType.var;
242
+ : isLet
243
+ ? DeclarationType.let
244
+ : isConst
245
+ ? DeclarationType.const
246
+ : ts.isFunctionDeclaration(decl)
247
+ ? DeclarationType.function
248
+ : ts.isClassDeclaration(decl)
249
+ ? DeclarationType.class
250
+ : ts.isEnumDeclaration(decl)
251
+ ? DeclarationType.enum
252
+ : DeclarationType.var;
248
253
  const hoistName = (nameNode) => {
249
254
  if (ts.isIdentifier(nameNode)) {
250
255
  const name = nameNode.text;
@@ -268,7 +273,8 @@ export function hoistDeclaration(decl, hoistedSymbols, scopeNode) {
268
273
  (ts.isVariableDeclaration(decl) && decl.initializer &&
269
274
  nameNode === decl.name &&
270
275
  (ts.isArrowFunction(decl.initializer) ||
271
- ts.isFunctionExpression(decl.initializer)))) {
276
+ (ts.isFunctionExpression(decl.initializer) &&
277
+ !decl.initializer.name)))) {
272
278
  const funcExpr = ts.isVariableDeclaration(decl)
273
279
  ? decl.initializer
274
280
  : decl;
@@ -294,7 +300,7 @@ export function hoistDeclaration(decl, hoistedSymbols, scopeNode) {
294
300
  const initializer = isLet || isConst || ts.isClassDeclaration(decl)
295
301
  ? "jspp::Constants::UNINITIALIZED"
296
302
  : "jspp::Constants::UNDEFINED";
297
- if (typeInfo.needsHeapAllocation) {
303
+ if (typeInfo?.needsHeapAllocation) {
298
304
  return `${this.indent()}auto ${name} = std::make_shared<jspp::AnyValue>(${initializer});\n`;
299
305
  }
300
306
  else {
@@ -638,6 +644,77 @@ export function isDeclarationUsedBeforeInitialization(name, root) {
638
644
  visit(root);
639
645
  return isUsedBefore;
640
646
  }
647
+ /**
648
+ * Checks if a variable name is used within a node subtree without a preceding declaration.
649
+ * Returns true if:
650
+ * 1. The variable is used but never declared within the provided root node.
651
+ * 2. The variable is used lexically before its declaration within the root node.
652
+ *
653
+ * @param name The variable name to check.
654
+ * @param root The root node to search within.
655
+ * @returns True if used without/before declaration.
656
+ */
657
+ export function isVariableUsedWithoutDeclaration(name, root) {
658
+ let declPos = -1;
659
+ let foundDecl = false;
660
+ // 1. Find the declaration position within this root
661
+ const findDecl = (node) => {
662
+ if (foundDecl)
663
+ return;
664
+ if ((ts.isFunctionDeclaration(node) ||
665
+ ts.isClassDeclaration(node) ||
666
+ ts.isVariableDeclaration(node) ||
667
+ ts.isEnumDeclaration(node) ||
668
+ ts.isParameter(node)) &&
669
+ node.name && ts.isIdentifier(node.name) && node.name.text === name) {
670
+ declPos = node.getStart();
671
+ foundDecl = true;
672
+ }
673
+ else {
674
+ ts.forEachChild(node, findDecl);
675
+ }
676
+ };
677
+ findDecl(root);
678
+ let isUsedIllegally = false;
679
+ // 2. Traverse for usages
680
+ const visit = (node) => {
681
+ if (isUsedIllegally)
682
+ return;
683
+ if (ts.isIdentifier(node) && node.text === name) {
684
+ const parent = node.parent;
685
+ // Skip if this identifier IS the declaration site
686
+ const isDeclarationSite = (ts.isFunctionDeclaration(parent) ||
687
+ ts.isVariableDeclaration(parent) ||
688
+ ts.isClassDeclaration(parent) ||
689
+ ts.isMethodDeclaration(parent) ||
690
+ ts.isEnumDeclaration(parent) ||
691
+ ts.isEnumMember(parent) ||
692
+ ts.isParameter(parent) ||
693
+ ts.isImportSpecifier(parent)) &&
694
+ parent.name === node;
695
+ // Skip if it's a property access (e.g., obj.name)
696
+ const isProperty = (ts.isPropertyAccessExpression(parent) &&
697
+ parent.name === node) ||
698
+ (ts.isPropertyAssignment(parent) && parent.name === node);
699
+ if (!isDeclarationSite && !isProperty) {
700
+ const usagePos = node.getStart();
701
+ // Case 1: Used but not declared in this node
702
+ if (!foundDecl) {
703
+ isUsedIllegally = true;
704
+ return;
705
+ }
706
+ // Case 2: Used lexically before declaration
707
+ if (usagePos < declPos) {
708
+ isUsedIllegally = true;
709
+ return;
710
+ }
711
+ }
712
+ }
713
+ ts.forEachChild(node, visit);
714
+ };
715
+ visit(root);
716
+ return isUsedIllegally;
717
+ }
641
718
  /**
642
719
  * Validates and filters function parameters, checking for illegal "this"
643
720
  * and correctly positioned rest parameters.
@@ -1,6 +1,6 @@
1
1
  import { DeclaredSymbols } from "../../ast/symbols.js";
2
2
  import { generateLambdaComponents, generateNativeLambda, generateWrappedLambda, } from "./function-handlers.js";
3
- import { escapeString, generateUniqueExceptionName, generateUniqueName, getDeclaredSymbols, getDerefCode, getJsVarName, getReturnCommand, getScopeForNode, hoistDeclaration, indent, isAsyncFunction, isBuiltinObject, isDeclarationCalledAsFunction, isDeclarationUsedAsValue, isDeclarationUsedBeforeInitialization, isGeneratorFunction, markSymbolAsInitialized, prepareScopeSymbolsForVisit, validateFunctionParams, } from "./helpers.js";
3
+ import { escapeString, generateUniqueExceptionName, generateUniqueName, getDeclaredSymbols, getDerefCode, getJsVarName, getReturnCommand, getScopeForNode, hoistDeclaration, indent, isAsyncFunction, isBuiltinObject, isDeclarationCalledAsFunction, isDeclarationUsedAsValue, isDeclarationUsedBeforeInitialization, isVariableUsedWithoutDeclaration, isGeneratorFunction, markSymbolAsInitialized, prepareScopeSymbolsForVisit, validateFunctionParams, } from "./helpers.js";
4
4
  import { generateDestructuring } from "./destructuring-handlers.js";
5
5
  import { visit } from "./visitor.js";
6
6
  const MODULE_NAME = "__entry_point__";
@@ -31,6 +31,7 @@ export class CodeGenerator {
31
31
  isDeclarationCalledAsFunction = isDeclarationCalledAsFunction;
32
32
  isDeclarationUsedAsValue = isDeclarationUsedAsValue;
33
33
  isDeclarationUsedBeforeInitialization = isDeclarationUsedBeforeInitialization;
34
+ isVariableUsedWithoutDeclaration = isVariableUsedWithoutDeclaration;
34
35
  validateFunctionParams = validateFunctionParams;
35
36
  generateDestructuring = generateDestructuring;
36
37
  // function handlers
@@ -44,7 +45,7 @@ export class CodeGenerator {
44
45
  this.typeAnalyzer = analyzer;
45
46
  this.isTypescript = isTypescript;
46
47
  this.globalThisVar = this.generateUniqueName("__this_val__", this.getDeclaredSymbols(ast));
47
- const declarations = `#include "index.hpp"\n\n`;
48
+ const declarations = `#include "jspp.hpp"\n#include "library/global_usings.hpp"\n\n`;
48
49
  let containerCode = `jspp::JsPromise ${MODULE_NAME}() {\n`;
49
50
  this.indentationLevel++;
50
51
  containerCode +=
@@ -63,6 +64,7 @@ export class CodeGenerator {
63
64
  containerCode += "}\n\n";
64
65
  let mainCode = "int main(int argc, char** argv) {\n";
65
66
  mainCode += ` try {\n`;
67
+ mainCode += ` jspp::initialize_runtime();\n`;
66
68
  mainCode += ` jspp::setup_process_argv(argc, argv);\n`;
67
69
  mainCode += ` auto p = ${MODULE_NAME}();\n`;
68
70
  mainCode += ` p.then(nullptr, [](jspp::AnyValue err) {\n`;
@@ -54,12 +54,14 @@ export function visitSourceFile(node, context) {
54
54
  this.markSymbolAsInitialized(funcName, globalScopeSymbols, localScopeSymbols);
55
55
  // Update features in the symbol registry
56
56
  const nativeName = this.generateUniqueName(`__${funcName}_native_`, hoistedSymbols);
57
+ const argumentKeywordIsUsed = this.isVariableUsedWithoutDeclaration("arguments", stmt.body);
57
58
  hoistedSymbols.update(funcName, {
58
59
  features: {
59
60
  native: {
60
61
  type: "lambda",
61
62
  name: nativeName,
62
63
  parameters: this.validateFunctionParams(stmt.parameters),
64
+ argumentKeywordIsUsed,
63
65
  },
64
66
  },
65
67
  });
@@ -151,12 +153,14 @@ export function visitBlock(node, context) {
151
153
  this.markSymbolAsInitialized(funcName, globalScopeSymbols, localScopeSymbols);
152
154
  // Update features in the symbol registry
153
155
  const nativeName = this.generateUniqueName(`__${funcName}_native_`, hoistedSymbols);
156
+ const argumentKeywordIsUsed = this.isVariableUsedWithoutDeclaration("arguments", stmt.body);
154
157
  hoistedSymbols.update(funcName, {
155
158
  features: {
156
159
  native: {
157
160
  type: "lambda",
158
161
  name: nativeName,
159
162
  parameters: this.validateFunctionParams(stmt.parameters),
163
+ argumentKeywordIsUsed,
160
164
  },
161
165
  },
162
166
  });
@@ -238,7 +242,7 @@ export function visitEnumDeclaration(node, context) {
238
242
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(name, scope);
239
243
  // Mark as initialized
240
244
  this.markSymbolAsInitialized(name, context.globalScopeSymbols, context.localScopeSymbols);
241
- const enumVar = typeInfo.needsHeapAllocation ? `(*${name})` : name;
245
+ const enumVar = typeInfo?.needsHeapAllocation ? `(*${name})` : name;
242
246
  let code = `${this.indent()}${enumVar} = jspp::AnyValue::make_object({});\n`;
243
247
  code += `${this.indent()}{\n`;
244
248
  this.indentationLevel++;
@@ -337,10 +341,7 @@ export function visitIfStatement(node, context) {
337
341
  const ifStmt = node;
338
342
  const isBinaryExpression = ts.isBinaryExpression(ifStmt.expression) &&
339
343
  constants.booleanOperators.includes(ifStmt.expression.operatorToken.kind);
340
- const condition = this.visit(ifStmt.expression, {
341
- ...context,
342
- supportedNativeLiterals: isBinaryExpression ? ["boolean"] : undefined,
343
- });
344
+ const condition = this.visit(ifStmt.expression, context);
344
345
  const thenStmt = this.visit(ifStmt.thenStatement, {
345
346
  ...context,
346
347
  isFunctionBody: false,
@@ -366,7 +367,7 @@ export function visitExpressionStatement(node, context) {
366
367
  export function visitThrowStatement(node, context) {
367
368
  const throwStmt = node;
368
369
  const expr = this.visit(throwStmt.expression, context);
369
- return `${this.indent()}throw jspp::Exception(${expr});
370
+ return `${this.indent()}throw jspp::Exception(${expr});
370
371
  `;
371
372
  }
372
373
  export function visitTryStatement(node, context) {
@@ -656,7 +657,8 @@ export function visitCatchClause(node, context) {
656
657
  const exceptionValueCode = `jspp::Exception::exception_to_any_value(${exceptionName})`;
657
658
  if (ts.isIdentifier(catchClause.variableDeclaration.name)) {
658
659
  const varName = catchClause.variableDeclaration.name.text;
659
- code += `${this.indent()}jspp::AnyValue ${varName} = ${exceptionValueCode};\n`;
660
+ code +=
661
+ `${this.indent()}jspp::AnyValue ${varName} = ${exceptionValueCode};\n`;
660
662
  }
661
663
  else {
662
664
  code += this.generateDestructuring(catchClause.variableDeclaration.name, exceptionValueCode, context) + ";\n";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ugo-studio/jspp",
3
- "version": "0.2.9",
3
+ "version": "0.3.1",
4
4
  "description": "A modern transpiler that converts JavaScript code into high-performance, standard C++23.",
5
5
  "main": "dist/index.js",
6
6
  "module": "src/index.ts",
@@ -14,7 +14,7 @@
14
14
  "scripts"
15
15
  ],
16
16
  "scripts": {
17
- "postinstall": "bun run scripts/setup-compiler.ts",
17
+ "postinstall": "bun run scripts/setup-compiler.ts && bun run scripts/precompile-headers.ts",
18
18
  "dev": "bun run src/cli/index.ts",
19
19
  "typecheck": "tsc --noEmit",
20
20
  "precompile": "bun run scripts/precompile-headers.ts",