@ugo-studio/jspp 0.3.1 → 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 (64) hide show
  1. package/dist/cli/args.js +22 -0
  2. package/dist/cli/compiler.js +53 -0
  3. package/dist/cli/index.js +43 -117
  4. package/dist/cli/pch.js +71 -0
  5. package/dist/cli/runner.js +23 -0
  6. package/dist/cli/spinner.js +27 -11
  7. package/dist/cli/transpiler.js +20 -0
  8. package/dist/cli/utils.js +25 -27
  9. package/dist/cli/wasm.js +70 -0
  10. package/dist/index.js +17 -6
  11. package/dist/{analysis → interpreter/analysis}/scope.js +34 -1
  12. package/dist/{analysis → interpreter/analysis}/typeAnalyzer.js +542 -3
  13. package/dist/{core → interpreter/core}/codegen/class-handlers.js +1 -1
  14. package/dist/{core → interpreter/core}/codegen/control-flow-handlers.js +2 -2
  15. package/dist/{core → interpreter/core}/codegen/declaration-handlers.js +21 -9
  16. package/dist/{core → interpreter/core}/codegen/destructuring-handlers.js +3 -3
  17. package/dist/{core → interpreter/core}/codegen/expression-handlers.js +44 -61
  18. package/dist/{core → interpreter/core}/codegen/function-handlers.js +108 -61
  19. package/dist/{core → interpreter/core}/codegen/helpers.js +79 -11
  20. package/dist/interpreter/core/codegen/index.js +156 -0
  21. package/dist/{core → interpreter/core}/codegen/literal-handlers.js +9 -0
  22. package/dist/{core → interpreter/core}/codegen/statement-handlers.js +39 -1
  23. package/package.json +6 -4
  24. package/scripts/precompile-headers.ts +63 -19
  25. package/scripts/setup-emsdk.ts +114 -0
  26. package/src/prelude/any_value.cpp +851 -599
  27. package/src/prelude/any_value.hpp +28 -30
  28. package/src/prelude/jspp.hpp +3 -1
  29. package/src/prelude/library/boolean.cpp +30 -0
  30. package/src/prelude/library/boolean.hpp +14 -0
  31. package/src/prelude/library/error.cpp +2 -2
  32. package/src/prelude/library/global.cpp +2 -0
  33. package/src/prelude/library/math.cpp +46 -43
  34. package/src/prelude/library/math.hpp +2 -0
  35. package/src/prelude/library/object.cpp +1 -1
  36. package/src/prelude/types.hpp +10 -9
  37. package/src/prelude/utils/access.hpp +2 -2
  38. package/src/prelude/utils/assignment_operators.hpp +40 -20
  39. package/src/prelude/utils/log_any_value/log_any_value.hpp +1 -1
  40. package/src/prelude/utils/log_any_value/primitives.hpp +1 -1
  41. package/src/prelude/utils/operators.hpp +87 -87
  42. package/src/prelude/utils/operators_native.hpp +349 -0
  43. package/src/prelude/utils/well_known_symbols.hpp +13 -13
  44. package/src/prelude/values/array.cpp +3 -3
  45. package/src/prelude/values/boolean.cpp +64 -0
  46. package/src/prelude/values/number.cpp +137 -92
  47. package/src/prelude/values/object.cpp +163 -122
  48. package/src/prelude/values/prototypes/boolean.hpp +24 -0
  49. package/src/prelude/values/prototypes/number.hpp +8 -1
  50. package/dist/cli/file-utils.js +0 -20
  51. package/dist/cli-utils/args.js +0 -59
  52. package/dist/cli-utils/colors.js +0 -9
  53. package/dist/cli-utils/file-utils.js +0 -20
  54. package/dist/cli-utils/spinner.js +0 -55
  55. package/dist/cli.js +0 -153
  56. package/dist/core/codegen/index.js +0 -88
  57. package/src/prelude/utils/operators_primitive.hpp +0 -337
  58. /package/dist/{ast → interpreter/ast}/symbols.js +0 -0
  59. /package/dist/{ast → interpreter/ast}/types.js +0 -0
  60. /package/dist/{core → interpreter/core}/codegen/visitor.js +0 -0
  61. /package/dist/{core → interpreter/core}/constants.js +0 -0
  62. /package/dist/{core → interpreter/core}/error.js +0 -0
  63. /package/dist/{core → interpreter/core}/parser.js +0 -0
  64. /package/dist/{core → interpreter/core}/traverser.js +0 -0
@@ -1,5 +1,5 @@
1
1
  import ts from "typescript";
2
- import { RESERVED_VAR_NAMES_FOR_STRICT_MODE } from "../../analysis/scope.js";
2
+ import { RESERVED_VAR_NAMES } from "../../analysis/scope.js";
3
3
  import { CompilerError } from "../error.js";
4
4
  import { CodeGenerator } from "./index.js";
5
5
  export function visitVariableDeclarationList(node, context) {
@@ -18,8 +18,8 @@ export function visitVariableDeclaration(node, context) {
18
18
  return this.generateDestructuring(varDecl.name, rhsCode, context);
19
19
  }
20
20
  const name = varDecl.name.getText();
21
- if (RESERVED_VAR_NAMES_FOR_STRICT_MODE.has(name)) {
22
- throw new CompilerError(`Cannot declare a variable named '${name}' in strict mode.`, varDecl.name, "SyntaxError");
21
+ if (RESERVED_VAR_NAMES.has(name)) {
22
+ throw new CompilerError(`Cannot declare a variable named '${name}'.`, varDecl.name, "SyntaxError");
23
23
  }
24
24
  const scope = this.getScopeForNode(varDecl);
25
25
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(name, scope);
@@ -53,7 +53,7 @@ export function visitVariableDeclaration(node, context) {
53
53
  (ts.isFunctionExpression(initExpr) && !initExpr.name)) {
54
54
  const initContext = {
55
55
  ...context,
56
- lambdaName: name, // Use the variable name as function name
56
+ functionName: name, // Use the variable name as function name
57
57
  };
58
58
  const nativeName = this.generateUniqueName(`__${name}_native_`, context.localScopeSymbols, context.globalScopeSymbols);
59
59
  const scopeNode = ts.isVariableDeclarationList(varDecl.parent)
@@ -92,16 +92,28 @@ export function visitVariableDeclaration(node, context) {
92
92
  return nativeLambdaCode;
93
93
  }
94
94
  }
95
- initializer = " = " + initText;
95
+ initializer = initText;
96
96
  }
97
97
  const isLetOrConst = (varDecl.parent.flags & (ts.NodeFlags.Let | ts.NodeFlags.Const)) !== 0;
98
98
  const shouldDeref = context.derefBeforeAssignment &&
99
99
  (!context.localScopeSymbols.has(name));
100
- const assignmentTarget = shouldDeref
100
+ let assignmentTarget = shouldDeref
101
101
  ? this.getDerefCode(name, name, context, typeInfo)
102
102
  : (typeInfo?.needsHeapAllocation && !shouldSkipDeref
103
103
  ? `*${name}`
104
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
+ }
105
117
  if (nativeLambdaCode)
106
118
  nativeLambdaCode += `;\n${this.indent()}`;
107
119
  if (isLetOrConst) {
@@ -116,19 +128,19 @@ export function visitVariableDeclaration(node, context) {
116
128
  return `${nativeLambdaCode}${assignmentTarget} = jspp::Constants::UNDEFINED`;
117
129
  }
118
130
  }
119
- return `${nativeLambdaCode}${assignmentTarget}${initializer}`;
131
+ return `${nativeLambdaCode}${assignmentTarget} = ${initializer}`;
120
132
  }
121
133
  // For 'var', it's a bit more complex.
122
134
  if (context.isAssignmentOnly) {
123
135
  if (!initializer)
124
136
  return "";
125
- return `${nativeLambdaCode}${assignmentTarget}${initializer}`;
137
+ return `${nativeLambdaCode}${assignmentTarget} = ${initializer}`;
126
138
  }
127
139
  else {
128
140
  // This case should not be hit with the new hoisting logic,
129
141
  // but is kept for safety.
130
142
  const initValue = initializer
131
- ? initializer.substring(3)
143
+ ? initializer
132
144
  : "jspp::Constants::UNDEFINED";
133
145
  if (typeInfo?.needsHeapAllocation) {
134
146
  return `auto ${name} = std::make_shared<jspp::AnyValue>(${initValue})`;
@@ -1,5 +1,5 @@
1
1
  import ts from "typescript";
2
- import { RESERVED_VAR_NAMES_FOR_STRICT_MODE } from "../../analysis/scope.js";
2
+ import { RESERVED_VAR_NAMES } from "../../analysis/scope.js";
3
3
  import { CompilerError } from "../error.js";
4
4
  import { visitObjectPropertyName } from "./expression-handlers.js";
5
5
  import { CodeGenerator } from "./index.js";
@@ -13,8 +13,8 @@ export function generateDestructuring(lhs, rhsCode, context) {
13
13
  const genAssignment = (pattern, valueCode) => {
14
14
  if (ts.isIdentifier(pattern)) {
15
15
  const name = pattern.text;
16
- if (RESERVED_VAR_NAMES_FOR_STRICT_MODE.has(name)) {
17
- throw new CompilerError(`Cannot destructure to a variable named '${name}' in strict mode.`, pattern, "SyntaxError");
16
+ if (RESERVED_VAR_NAMES.has(name)) {
17
+ throw new CompilerError(`Cannot destructure to a variable named '${name}'.`, pattern, "SyntaxError");
18
18
  }
19
19
  const scope = this.getScopeForNode(pattern);
20
20
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(name, scope);
@@ -607,7 +607,8 @@ export function visitBinaryExpression(node, context) {
607
607
  finalLeft = this.getDerefCode(leftText, this.getJsVarName(binExpr.left), visitContext, typeInfo);
608
608
  }
609
609
  // Number optimizations
610
- if (typeInfo && typeInfo.type === "number") {
610
+ const nodeType = this.typeAnalyzer.inferNodeReturnType(binExpr.left);
611
+ if (nodeType === "number") {
611
612
  finalLeft = `${finalLeft}.as_double()`;
612
613
  }
613
614
  }
@@ -623,7 +624,8 @@ export function visitBinaryExpression(node, context) {
623
624
  finalRight = this.getDerefCode(rightText, this.getJsVarName(binExpr.right), visitContext, typeInfo);
624
625
  }
625
626
  // Number optimizations
626
- if (typeInfo && typeInfo.type === "number") {
627
+ const nodeType = this.typeAnalyzer.inferNodeReturnType(binExpr.right);
628
+ if (nodeType === "number") {
627
629
  finalRight = `${finalRight}.as_double()`;
628
630
  }
629
631
  }
@@ -642,101 +644,82 @@ export function visitBinaryExpression(node, context) {
642
644
  if (opToken.kind === ts.SyntaxKind.QuestionQuestionToken) {
643
645
  return `jspp::nullish_coalesce(${finalLeft}, ${finalRight})`;
644
646
  }
645
- const isLiteral = (n) => ts.isNumericLiteral(n);
646
- const supportsNativeBoolean = ts.isIfStatement(node.parent) ||
647
- ts.isConditionalExpression(node.parent);
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
  }
@@ -5,8 +5,18 @@ 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
+ // const nativeExcessArgsName = this.generateUniqueName(
9
+ // "__excess_args",
10
+ // declaredSymbols,
11
+ // context.globalScopeSymbols,
12
+ // context.localScopeSymbols,
13
+ // );
14
+ // const nativeTotalArgsSizeName = this.generateUniqueName(
15
+ // "__total_args_size",
16
+ // declaredSymbols,
17
+ // context.globalScopeSymbols,
18
+ // context.localScopeSymbols,
19
+ // );
10
20
  const isInsideGeneratorFunction = this.isGeneratorFunction(node);
11
21
  const isInsideAsyncFunction = this.isAsyncFunction(node);
12
22
  const isArrow = ts.isArrowFunction(node);
@@ -14,15 +24,27 @@ export function generateLambdaComponents(node, context, options) {
14
24
  isInsideGeneratorFunction: isInsideGeneratorFunction,
15
25
  isInsideAsyncFunction: isInsideAsyncFunction,
16
26
  });
17
- const funcReturnType = (isInsideGeneratorFunction && isInsideAsyncFunction)
18
- ? "jspp::JsAsyncIterator<jspp::AnyValue>"
19
- : (isInsideGeneratorFunction
20
- ? "jspp::JsIterator<jspp::AnyValue>"
21
- : (isInsideAsyncFunction ? "jspp::JsPromise" : "jspp::AnyValue"));
27
+ const getFuncReturnType = (isInsideNativeLambda) => {
28
+ if (isInsideGeneratorFunction && isInsideAsyncFunction) {
29
+ return "jspp::JsAsyncIterator<jspp::AnyValue>";
30
+ }
31
+ else if (isInsideGeneratorFunction) {
32
+ return "jspp::JsIterator<jspp::AnyValue>";
33
+ }
34
+ else if (isInsideAsyncFunction)
35
+ return "jspp::JsPromise";
36
+ else {
37
+ const inferedReturnType = this.typeAnalyzer.inferFunctionReturnType(node);
38
+ if (isInsideNativeLambda && inferedReturnType === "number") {
39
+ return "double";
40
+ }
41
+ return "jspp::AnyValue";
42
+ }
43
+ };
22
44
  // Lambda arguments: regular functions use std::span for performance.
23
45
  // Generators and async functions use std::vector to ensure they are safely copied into the coroutine frame.
24
46
  const paramThisType = "jspp::AnyValue";
25
- const paramArgsType = (isInsideGeneratorFunction || isInsideAsyncFunction)
47
+ const paramArgsType = isInsideGeneratorFunction || isInsideAsyncFunction
26
48
  ? "std::vector<jspp::AnyValue>"
27
49
  : "std::span<const jspp::AnyValue>";
28
50
  const globalScopeSymbols = this.prepareScopeSymbolsForVisit(context.globalScopeSymbols, context.localScopeSymbols);
@@ -31,7 +53,7 @@ export function generateLambdaComponents(node, context, options) {
31
53
  isMainContext: false,
32
54
  isInsideFunction: true,
33
55
  isFunctionBody: false,
34
- lambdaName: undefined,
56
+ functionName: undefined,
35
57
  globalScopeSymbols,
36
58
  localScopeSymbols: new DeclaredSymbols(),
37
59
  superClassVar: context.superClassVar,
@@ -95,7 +117,8 @@ export function generateLambdaComponents(node, context, options) {
95
117
  `${this.indent()}auto ${name} = std::make_shared<jspp::AnyValue>(${signatureName});\n`;
96
118
  }
97
119
  else {
98
- nativeParamsContent += this.generateDestructuring(p.name, signatureName, visitContext) + ";\n";
120
+ nativeParamsContent += this.generateDestructuring(p.name, signatureName, visitContext) +
121
+ ";\n";
99
122
  }
100
123
  }
101
124
  });
@@ -114,8 +137,7 @@ export function generateLambdaComponents(node, context, options) {
114
137
  checks: { initialized: true },
115
138
  });
116
139
  const scope = this.getScopeForNode(p);
117
- const typeInfo = this.typeAnalyzer.scopeManager
118
- .lookupFromScope(name, scope);
140
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(name, scope);
119
141
  // Handle rest parameters
120
142
  if (!!p.dotDotDotToken) {
121
143
  const initValue = `jspp::AnyValue::make_array(${argsName}.subspan(${i}))`;
@@ -148,39 +170,43 @@ export function generateLambdaComponents(node, context, options) {
148
170
  const initValue = `${argsName}.size() > ${i} ? ${argsName}[${i}] : ${defaultValue}`;
149
171
  paramsCode +=
150
172
  `${this.indent()}auto ${tempName} = ${initValue};\n`;
151
- paramsCode += this.generateDestructuring(p.name, tempName, visitContext) + ";\n";
173
+ paramsCode +=
174
+ this.generateDestructuring(p.name, tempName, visitContext) +
175
+ ";\n";
152
176
  }
153
177
  });
154
178
  return paramsCode;
155
179
  };
156
180
  // Generate params and function body
157
181
  let paramsContent = "";
158
- let blockContentWithoutOpeningBrace = "";
182
+ let getBlockContentWithoutOpeningBrace = () => "";
159
183
  // 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
- }
184
+ // TODO: compile arguments array from parameters
185
+ // if (
186
+ // this.isVariableUsedWithoutDeclaration("arguments", node.body as ts.Node)
187
+ // ) {
188
+ // // Add span parameter for holding the excess arguments from the caller of the native lambda
189
+ // nativeFuncArgs +=
190
+ // `, const std::size_t ${nativeTotalArgsSizeName}, std::span<const jspp::AnyValue> ${nativeExcessArgsName} = {}`;
191
+ // // this.indentationLevel++;
192
+ // // nativeParamsContent +=
193
+ // // `${this.indent()}jspp::AnyValue arguments = jspp::Constants::UNDEFINED;\n`;
194
+ // // nativeParamsContent += `${this.indent()}{\n`;
195
+ // // this.indentationLevel++;
196
+ // // nativeParamsContent +=
197
+ // // `${this.indent()}std::vector<jspp::AnyValue> ${argsName};\n`;
198
+ // // this.validateFunctionParams(node.parameters).forEach(
199
+ // // (p, i) => {
200
+ // // },
201
+ // // );
202
+ // // this.indentationLevel--;
203
+ // // nativeParamsContent += `${this.indent()}}\n`;
204
+ // // this.indentationLevel--;
205
+ // // this.indentationLevel++;
206
+ // // paramsContent =
207
+ // // `${this.indent()}jspp::AnyValue arguments = jspp::AnyValue::make_array(${argsName});\n`;
208
+ // // this.indentationLevel--;
209
+ // }
184
210
  if (node.body) {
185
211
  if (ts.isBlock(node.body)) {
186
212
  // Hoist var declarations in the function body
@@ -202,34 +228,53 @@ export function generateLambdaComponents(node, context, options) {
202
228
  paramsContent += generateParamsBuilder();
203
229
  this.indentationLevel--;
204
230
  // The block visitor already adds braces, so we need to remove the opening brace to inject the preamble and param extraction.
205
- blockContentWithoutOpeningBrace = this.visit(node.body, {
206
- ...visitContext,
207
- isMainContext: false,
208
- isInsideFunction: true,
209
- isFunctionBody: true,
210
- isInsideGeneratorFunction: isInsideGeneratorFunction,
211
- isInsideAsyncFunction: isInsideAsyncFunction,
212
- }).trimStart().substring(2);
231
+ const properIndentationLevel = this.indentationLevel;
232
+ const nodeBody = node.body;
233
+ getBlockContentWithoutOpeningBrace = (isInsideNativeLambda) => {
234
+ const currentIndentationLevel = this.indentationLevel;
235
+ this.indentationLevel = properIndentationLevel;
236
+ const code = this.visit(nodeBody, {
237
+ ...visitContext,
238
+ isMainContext: false,
239
+ isInsideFunction: true,
240
+ isFunctionBody: true,
241
+ isInsideGeneratorFunction,
242
+ isInsideAsyncFunction,
243
+ isInsideNativeLambda,
244
+ })
245
+ .trim()
246
+ .substring(2);
247
+ this.indentationLevel = currentIndentationLevel;
248
+ return code;
249
+ };
213
250
  }
214
251
  else {
215
252
  this.indentationLevel++;
216
253
  paramsContent += generateParamsBuilder();
217
- blockContentWithoutOpeningBrace =
218
- `${this.indent()}${returnCommand} ${this.visit(node.body, {
254
+ const properIndentationLevel = this.indentationLevel;
255
+ const nodeBody = node.body;
256
+ getBlockContentWithoutOpeningBrace = (isInsideNativeLambda) => {
257
+ const currentIndentationLevel = this.indentationLevel;
258
+ this.indentationLevel = properIndentationLevel;
259
+ let code = `${this.indent()}${returnCommand} ${this.visit(nodeBody, {
219
260
  ...visitContext,
220
261
  isMainContext: false,
221
262
  isInsideFunction: true,
222
263
  isFunctionBody: false,
223
- isInsideGeneratorFunction: isInsideGeneratorFunction,
224
- isInsideAsyncFunction: isInsideAsyncFunction,
264
+ isInsideGeneratorFunction,
265
+ isInsideAsyncFunction,
266
+ isInsideNativeLambda,
225
267
  })};\n`;
268
+ this.indentationLevel--;
269
+ code += `${this.indent()}}`;
270
+ this.indentationLevel = currentIndentationLevel;
271
+ return code;
272
+ };
226
273
  this.indentationLevel--;
227
- blockContentWithoutOpeningBrace += `${this.indent()}}`;
228
274
  }
229
275
  }
230
276
  else {
231
- blockContentWithoutOpeningBrace =
232
- `${returnCommand} jspp::Constants::UNDEFINED; }\n`;
277
+ getBlockContentWithoutOpeningBrace = () => `${returnCommand} jspp::Constants::UNDEFINED; }`;
233
278
  }
234
279
  return {
235
280
  node,
@@ -239,37 +284,39 @@ export function generateLambdaComponents(node, context, options) {
239
284
  thisArgParam,
240
285
  funcArgs,
241
286
  nativeFuncArgs,
242
- funcReturnType,
287
+ getFuncReturnType,
243
288
  preamble,
244
289
  nativePreamble,
245
290
  paramsContent,
246
291
  nativeParamsContent,
247
- blockContentWithoutOpeningBrace,
292
+ getBlockContentWithoutOpeningBrace,
248
293
  isInsideGeneratorFunction,
249
294
  isInsideAsyncFunction,
250
295
  };
251
296
  }
252
297
  export function generateNativeLambda(comps) {
253
- const { options, thisArgParam, nativeFuncArgs, funcReturnType, nativePreamble, nativeParamsContent, blockContentWithoutOpeningBrace, } = comps;
298
+ const { options, thisArgParam, nativeFuncArgs, getFuncReturnType, nativePreamble, nativeParamsContent, getBlockContentWithoutOpeningBrace, } = comps;
254
299
  const capture = options?.capture || "[=]";
255
300
  const nativeName = options?.nativeName;
256
301
  const selfParam = nativeName ? `this auto&& ${nativeName}, ` : "";
257
302
  const mutableLabel = nativeName ? "" : "mutable ";
303
+ const funcReturnType = getFuncReturnType(true);
258
304
  let lambda = `${capture}(${selfParam}${thisArgParam}${nativeFuncArgs}) ${mutableLabel}-> ${funcReturnType} {\n`;
259
305
  lambda += nativePreamble;
260
306
  lambda += nativeParamsContent;
261
- lambda += blockContentWithoutOpeningBrace;
307
+ lambda += getBlockContentWithoutOpeningBrace(true);
262
308
  return lambda;
263
309
  }
264
310
  export function generateWrappedLambda(comps) {
265
- const { node, context, options, thisArgParam, funcArgs, funcReturnType, preamble, paramsContent, blockContentWithoutOpeningBrace, isInsideGeneratorFunction, isInsideAsyncFunction, } = comps;
311
+ const { node, context, options, thisArgParam, funcArgs, getFuncReturnType, preamble, paramsContent, getBlockContentWithoutOpeningBrace, isInsideGeneratorFunction, isInsideAsyncFunction, } = comps;
266
312
  const capture = options?.capture || "[=]";
267
313
  const isAssignment = options?.isAssignment || false;
314
+ const funcReturnType = getFuncReturnType(false);
268
315
  const noTypeSignature = options?.noTypeSignature || false;
269
316
  let lambda = `${capture}(${thisArgParam}, ${funcArgs}) mutable -> ${funcReturnType} {\n`;
270
317
  lambda += preamble;
271
318
  lambda += paramsContent;
272
- lambda += blockContentWithoutOpeningBrace;
319
+ lambda += getBlockContentWithoutOpeningBrace(false);
273
320
  let callable = lambda;
274
321
  let method = "";
275
322
  // Handle generator function
@@ -308,7 +355,7 @@ export function generateWrappedLambda(comps) {
308
355
  method = `jspp::AnyValue::make_function`;
309
356
  }
310
357
  }
311
- const funcName = context?.lambdaName || node.name?.getText();
358
+ const funcName = context?.functionName || node.name?.getText();
312
359
  const hasName = !!funcName && funcName.length > 0;
313
360
  let args = callable;
314
361
  const isArrow = ts.isArrowFunction(node);
@@ -378,7 +425,7 @@ export function visitFunctionExpression(node, context) {
378
425
  `${this.indent()}auto ${funcName} = std::make_shared<jspp::AnyValue>();\n`;
379
426
  const lambda = this.generateWrappedLambda(this.generateLambdaComponents(funcExpr, {
380
427
  ...context,
381
- lambdaName: funcName,
428
+ functionName: funcName,
382
429
  }, {
383
430
  isAssignment: true,
384
431
  capture: "[=]",