@ugo-studio/jspp 0.2.6 → 0.2.8

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 (43) hide show
  1. package/dist/analysis/typeAnalyzer.js +56 -24
  2. package/dist/ast/symbols.js +28 -19
  3. package/dist/cli/index.js +1 -1
  4. package/dist/core/codegen/class-handlers.js +8 -8
  5. package/dist/core/codegen/control-flow-handlers.js +17 -7
  6. package/dist/core/codegen/declaration-handlers.js +21 -9
  7. package/dist/core/codegen/expression-handlers.js +558 -126
  8. package/dist/core/codegen/function-handlers.js +101 -108
  9. package/dist/core/codegen/helpers.js +28 -7
  10. package/dist/core/codegen/index.js +6 -4
  11. package/dist/core/codegen/literal-handlers.js +4 -2
  12. package/dist/core/codegen/statement-handlers.js +39 -19
  13. package/package.json +1 -1
  14. package/scripts/precompile-headers.ts +8 -1
  15. package/src/prelude/any_value.hpp +89 -59
  16. package/src/prelude/any_value_access.hpp +1 -1
  17. package/src/prelude/any_value_helpers.hpp +85 -43
  18. package/src/prelude/index.hpp +1 -0
  19. package/src/prelude/library/array.hpp +3 -2
  20. package/src/prelude/types.hpp +8 -8
  21. package/src/prelude/utils/access.hpp +62 -6
  22. package/src/prelude/utils/assignment_operators.hpp +14 -14
  23. package/src/prelude/utils/log_any_value/array.hpp +0 -15
  24. package/src/prelude/utils/log_any_value/primitives.hpp +2 -0
  25. package/src/prelude/utils/operators.hpp +117 -474
  26. package/src/prelude/utils/operators_primitive.hpp +337 -0
  27. package/src/prelude/values/helpers/array.hpp +4 -4
  28. package/src/prelude/values/helpers/async_iterator.hpp +2 -2
  29. package/src/prelude/values/helpers/function.hpp +3 -3
  30. package/src/prelude/values/helpers/iterator.hpp +2 -2
  31. package/src/prelude/values/helpers/object.hpp +3 -3
  32. package/src/prelude/values/helpers/promise.hpp +1 -1
  33. package/src/prelude/values/helpers/string.hpp +1 -1
  34. package/src/prelude/values/helpers/symbol.hpp +1 -1
  35. package/src/prelude/values/prototypes/array.hpp +1125 -853
  36. package/src/prelude/values/prototypes/async_iterator.hpp +32 -14
  37. package/src/prelude/values/prototypes/function.hpp +30 -18
  38. package/src/prelude/values/prototypes/iterator.hpp +40 -17
  39. package/src/prelude/values/prototypes/number.hpp +119 -62
  40. package/src/prelude/values/prototypes/object.hpp +10 -4
  41. package/src/prelude/values/prototypes/promise.hpp +167 -109
  42. package/src/prelude/values/prototypes/string.hpp +407 -231
  43. package/src/prelude/values/prototypes/symbol.hpp +45 -23
@@ -2,6 +2,54 @@ import ts from "typescript";
2
2
  import { constants } from "../constants.js";
3
3
  import { CompilerError } from "../error.js";
4
4
  import { CodeGenerator } from "./index.js";
5
+ /**
6
+ * Helper to recursively flatten static spread elements in arrays or call arguments.
7
+ */
8
+ function flattenArrayElements(elements) {
9
+ const flattened = [];
10
+ for (const elem of elements) {
11
+ if (elem && ts.isSpreadElement(elem)) {
12
+ const expr = elem.expression;
13
+ if (ts.isArrayLiteralExpression(expr)) {
14
+ flattened.push(...flattenArrayElements(expr.elements));
15
+ }
16
+ else if (ts.isStringLiteral(expr) ||
17
+ ts.isNoSubstitutionTemplateLiteral(expr)) {
18
+ for (const char of expr.text) {
19
+ flattened.push(ts.factory.createStringLiteral(char));
20
+ }
21
+ }
22
+ else {
23
+ flattened.push({ dynamicSpread: expr });
24
+ }
25
+ }
26
+ else {
27
+ flattened.push(elem);
28
+ }
29
+ }
30
+ return flattened;
31
+ }
32
+ /**
33
+ * Helper to recursively flatten static spread assignments in object literals.
34
+ */
35
+ function flattenObjectProperties(properties) {
36
+ const flattened = [];
37
+ for (const prop of properties) {
38
+ if (ts.isSpreadAssignment(prop)) {
39
+ const expr = prop.expression;
40
+ if (ts.isObjectLiteralExpression(expr)) {
41
+ flattened.push(...flattenObjectProperties(expr.properties));
42
+ }
43
+ else {
44
+ flattened.push(prop);
45
+ }
46
+ }
47
+ else {
48
+ flattened.push(prop);
49
+ }
50
+ }
51
+ return flattened;
52
+ }
5
53
  export function visitObjectPropertyName(node, context) {
6
54
  if (ts.isNumericLiteral(node)) {
7
55
  return context.isBracketNotationPropertyAccess
@@ -29,11 +77,12 @@ export function visitObjectPropertyName(node, context) {
29
77
  }
30
78
  export function visitObjectLiteralExpression(node, context) {
31
79
  const obj = node;
80
+ const properties = flattenObjectProperties(obj.properties);
32
81
  const objVar = this.generateUniqueName("__obj_", this.getDeclaredSymbols(node));
33
- if (!obj.properties.some((prop) => ts.isPropertyAssignment(prop) ||
82
+ if (!properties.some((prop) => ts.isPropertyAssignment(prop) ||
34
83
  ts.isShorthandPropertyAssignment(prop) ||
35
84
  ts.isMethodDeclaration(prop) || ts.isGetAccessor(prop) ||
36
- ts.isSetAccessor(prop))) {
85
+ ts.isSetAccessor(prop) || ts.isSpreadAssignment(prop))) {
37
86
  // Empty object
38
87
  return `jspp::AnyValue::make_object_with_proto({}, ::Object.get_own_property("prototype"))`;
39
88
  }
@@ -41,7 +90,7 @@ export function visitObjectLiteralExpression(node, context) {
41
90
  code +=
42
91
  `${this.indent()} auto ${objVar} = jspp::AnyValue::make_object_with_proto({}, ::Object.get_own_property("prototype"));\n`;
43
92
  this.indentationLevel++;
44
- for (const prop of obj.properties) {
93
+ for (const prop of properties) {
45
94
  if (ts.isPropertyAssignment(prop)) {
46
95
  const key = visitObjectPropertyName.call(this, prop.name, {
47
96
  ...context,
@@ -78,10 +127,10 @@ export function visitObjectLiteralExpression(node, context) {
78
127
  ...context,
79
128
  isObjectLiteralExpression: true,
80
129
  });
81
- const lambda = this.generateLambda(prop, {
130
+ const lambda = this.generateWrappedLambda(this.generateLambdaComponents(prop, {
82
131
  ...context,
83
132
  isInsideFunction: true,
84
- });
133
+ }));
85
134
  code +=
86
135
  `${this.indent()}${objVar}.define_data_property(${key}, ${lambda});\n`;
87
136
  }
@@ -90,10 +139,10 @@ export function visitObjectLiteralExpression(node, context) {
90
139
  ...context,
91
140
  isObjectLiteralExpression: true,
92
141
  });
93
- const lambda = this.generateLambda(prop, {
142
+ const lambda = this.generateWrappedLambda(this.generateLambdaComponents(prop, {
94
143
  ...context,
95
144
  isInsideFunction: true,
96
- });
145
+ }));
97
146
  code +=
98
147
  `${this.indent()}${objVar}.define_getter(${key}, ${lambda});\n`;
99
148
  }
@@ -102,36 +151,96 @@ export function visitObjectLiteralExpression(node, context) {
102
151
  ...context,
103
152
  isObjectLiteralExpression: true,
104
153
  });
105
- const lambda = this.generateLambda(prop, {
154
+ const lambda = this.generateWrappedLambda(this.generateLambdaComponents(prop, {
106
155
  ...context,
107
156
  isInsideFunction: true,
108
- });
157
+ }));
109
158
  code +=
110
159
  `${this.indent()}${objVar}.define_setter(${key}, ${lambda});\n`;
111
160
  }
161
+ else if (ts.isSpreadAssignment(prop)) {
162
+ let spreadExpr = this.visit(prop.expression, context);
163
+ if (ts.isIdentifier(prop.expression)) {
164
+ const scope = this.getScopeForNode(prop.expression);
165
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(prop.expression.text, scope);
166
+ if (typeInfo && !typeInfo.isBuiltin && !typeInfo.isParameter) {
167
+ spreadExpr = this.getDerefCode(spreadExpr, this.getJsVarName(prop.expression), context, typeInfo);
168
+ }
169
+ }
170
+ code +=
171
+ `${this.indent()}jspp::Access::spread_object(${objVar}, ${spreadExpr});\n`;
172
+ }
112
173
  }
113
174
  this.indentationLevel--;
114
175
  code += `${this.indent()} return ${objVar};\n${this.indent()}})()`;
115
176
  return code;
116
177
  }
117
178
  export function visitArrayLiteralExpression(node, context) {
118
- const elements = node.elements
119
- .map((elem) => {
120
- let elemText = this.visit(elem, context);
121
- if (ts.isIdentifier(elem)) {
122
- const scope = this.getScopeForNode(elem);
123
- const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(elem.text, scope);
124
- if (typeInfo && !typeInfo.isBuiltin && !typeInfo.isParameter) {
125
- elemText = this.getDerefCode(elemText, this.getJsVarName(elem), context, typeInfo);
179
+ const flattened = flattenArrayElements(node.elements);
180
+ const hasSpread = flattened.some((e) => typeof e === "object" && "dynamicSpread" in e);
181
+ if (!hasSpread) {
182
+ const elements = flattened
183
+ .map((elem) => {
184
+ const expr = elem;
185
+ let elemText = this.visit(expr, context);
186
+ if (ts.isIdentifier(expr)) {
187
+ const scope = this.getScopeForNode(expr);
188
+ const typeInfo = this.typeAnalyzer.scopeManager
189
+ .lookupFromScope(expr.text, scope);
190
+ if (typeInfo && !typeInfo.isBuiltin && !typeInfo.isParameter) {
191
+ elemText = this.getDerefCode(elemText, this.getJsVarName(expr), context, typeInfo);
192
+ }
193
+ }
194
+ if (ts.isOmittedExpression(expr)) {
195
+ elemText = "jspp::Constants::UNINITIALIZED";
196
+ }
197
+ return elemText;
198
+ });
199
+ const elementsJoined = elements.join(", ");
200
+ const elementsSpan = elements.length > 0
201
+ ? `std::span<const jspp::AnyValue>((const jspp::AnyValue[]){${elementsJoined}}, ${elements.length})`
202
+ : "std::span<const jspp::AnyValue>{}";
203
+ return `jspp::AnyValue::make_array_with_proto(${elementsSpan}, ::Array.get_own_property("prototype"))`;
204
+ }
205
+ const arrVar = this.generateUniqueName("__arr_", this.getDeclaredSymbols(node));
206
+ let code = `([&]() {\n`;
207
+ code += `${this.indent()} std::vector<jspp::AnyValue> ${arrVar};\n`;
208
+ this.indentationLevel++;
209
+ for (const elem of flattened) {
210
+ if (typeof elem === "object" && "dynamicSpread" in elem) {
211
+ const spreadExprSource = elem.dynamicSpread;
212
+ let spreadExpr = this.visit(spreadExprSource, context);
213
+ if (ts.isIdentifier(spreadExprSource)) {
214
+ const scope = this.getScopeForNode(spreadExprSource);
215
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(spreadExprSource.text, scope);
216
+ if (typeInfo && !typeInfo.isBuiltin && !typeInfo.isParameter) {
217
+ spreadExpr = this.getDerefCode(spreadExpr, this.getJsVarName(spreadExprSource), context, typeInfo);
218
+ }
126
219
  }
220
+ code +=
221
+ `${this.indent()}jspp::Access::spread_array(${arrVar}, ${spreadExpr});\n`;
127
222
  }
128
- return elemText;
129
- });
130
- const elementsJoined = elements.join(", ");
131
- const elementsSpan = elements.length > 0
132
- ? `std::span<const jspp::AnyValue>((const jspp::AnyValue[]){${elementsJoined}}, ${elements.length})`
133
- : "std::span<const jspp::AnyValue>{}";
134
- return `jspp::AnyValue::make_array_with_proto(${elementsSpan}, ::Array.get_own_property("prototype"))`;
223
+ else {
224
+ const expr = elem;
225
+ let elemText = this.visit(expr, context);
226
+ if (ts.isIdentifier(expr)) {
227
+ const scope = this.getScopeForNode(expr);
228
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(expr.text, scope);
229
+ if (typeInfo && !typeInfo.isBuiltin && !typeInfo.isParameter) {
230
+ elemText = this.getDerefCode(elemText, this.getJsVarName(expr), context, typeInfo);
231
+ }
232
+ }
233
+ if (ts.isOmittedExpression(expr)) {
234
+ elemText = "jspp::Constants::UNINITIALIZED";
235
+ }
236
+ code += `${this.indent()}${arrVar}.push_back(${elemText});\n`;
237
+ }
238
+ }
239
+ this.indentationLevel--;
240
+ code +=
241
+ `${this.indent()} return jspp::AnyValue::make_array_with_proto(std::move(${arrVar}), ::Array.get_own_property("prototype"));\n`;
242
+ code += `${this.indent()}})()`;
243
+ return code;
135
244
  }
136
245
  export function visitPrefixUnaryExpression(node, context) {
137
246
  const prefixUnaryExpr = node;
@@ -151,6 +260,15 @@ export function visitPrefixUnaryExpression(node, context) {
151
260
  }
152
261
  return `${operator}(${target})`;
153
262
  }
263
+ if (operator === "+") {
264
+ return `jspp::plus(${operand})`;
265
+ }
266
+ if (operator === "-") {
267
+ return `jspp::negate(${operand})`;
268
+ }
269
+ if (operator === "!") {
270
+ return `jspp::logical_not(${operand})`;
271
+ }
154
272
  if (operator === "~") {
155
273
  let target = operand;
156
274
  if (ts.isIdentifier(prefixUnaryExpr.operand)) {
@@ -163,7 +281,7 @@ export function visitPrefixUnaryExpression(node, context) {
163
281
  target = `*${operand}`;
164
282
  }
165
283
  }
166
- return `${operator}(${target})`;
284
+ return `jspp::bitwise_not(${target})`;
167
285
  }
168
286
  return `${operator}${operand}`;
169
287
  }
@@ -215,7 +333,7 @@ export function visitPropertyAccessExpression(node, context) {
215
333
  finalExpr = this.getDerefCode(exprText, this.getJsVarName(propAccess.expression), context, typeInfo);
216
334
  }
217
335
  if (propAccess.questionDotToken) {
218
- return `jspp::Access::optional_get_property(${finalExpr}, "${propName}")`;
336
+ return `jspp::Access::get_optional_property(${finalExpr}, "${propName}")`;
219
337
  }
220
338
  return `${finalExpr}.get_own_property("${propName}")`;
221
339
  }
@@ -253,7 +371,7 @@ export function visitElementAccessExpression(node, context) {
253
371
  }
254
372
  }
255
373
  if (elemAccess.questionDotToken) {
256
- return `jspp::Access::optional_get_element(${finalExpr}, ${argText})`;
374
+ return `jspp::Access::get_optional_element(${finalExpr}, ${argText})`;
257
375
  }
258
376
  return `${finalExpr}.get_own_property(${argText})`;
259
377
  }
@@ -452,16 +570,17 @@ export function visitBinaryExpression(node, context) {
452
570
  ? this.getDerefCode(leftText, leftText, visitContext, typeInfo)
453
571
  : (typeInfo.needsHeapAllocation ? `*${leftText}` : leftText);
454
572
  // Update scope symbols on variable re-assignment
573
+ // Reset features
455
574
  if (ts.isIdentifier(binExpr.left)) {
456
575
  if (!ts.isFunctionDeclaration(binExpr.right)) {
457
576
  if (context.localScopeSymbols.has(binExpr.left.text)) {
458
- context.localScopeSymbols.update(binExpr.left.text, {
459
- func: null,
577
+ context.localScopeSymbols.set(binExpr.left.text, {
578
+ features: {},
460
579
  });
461
580
  }
462
581
  else if (context.globalScopeSymbols.has(binExpr.left.text)) {
463
- context.globalScopeSymbols.update(binExpr.left.text, {
464
- func: null,
582
+ context.globalScopeSymbols.set(binExpr.left.text, {
583
+ features: {},
465
584
  });
466
585
  }
467
586
  }
@@ -481,6 +600,10 @@ export function visitBinaryExpression(node, context) {
481
600
  if (typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin) {
482
601
  finalLeft = this.getDerefCode(leftText, this.getJsVarName(binExpr.left), visitContext, typeInfo);
483
602
  }
603
+ // Number optimizations
604
+ if (typeInfo && typeInfo.type === "number") {
605
+ finalLeft = `${finalLeft}.as_double()`;
606
+ }
484
607
  }
485
608
  let finalRight = rightText;
486
609
  if (ts.isIdentifier(binExpr.right)) {
@@ -493,6 +616,10 @@ export function visitBinaryExpression(node, context) {
493
616
  if (typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin) {
494
617
  finalRight = this.getDerefCode(rightText, this.getJsVarName(binExpr.right), visitContext, typeInfo);
495
618
  }
619
+ // Number optimizations
620
+ if (typeInfo && typeInfo.type === "number") {
621
+ finalRight = `${finalRight}.as_double()`;
622
+ }
496
623
  }
497
624
  if (opToken.kind === ts.SyntaxKind.InKeyword) {
498
625
  return `jspp::Access::in(${finalLeft}, ${finalRight})`;
@@ -533,11 +660,26 @@ export function visitBinaryExpression(node, context) {
533
660
  if (opToken.kind === ts.SyntaxKind.ExclamationEqualsToken) {
534
661
  return `!jspp::is_equal_to_primitive(${literalLeft}, ${literalRight})`;
535
662
  }
536
- if (isLiteral(binExpr.left) && isLiteral(binExpr.right)) {
537
- return `(${literalLeft} ${op} ${literalRight})`;
663
+ let funcName = "";
664
+ if (opToken.kind === ts.SyntaxKind.LessThanToken) {
665
+ funcName = "jspp::less_than_primitive";
666
+ }
667
+ if (opToken.kind === ts.SyntaxKind.LessThanEqualsToken) {
668
+ funcName = "jspp::less_than_or_equal_primitive";
538
669
  }
539
- else
540
- return `(${literalLeft} ${op} ${literalRight}).as_boolean()`;
670
+ if (opToken.kind === ts.SyntaxKind.GreaterThanToken) {
671
+ funcName = "jspp::greater_than_primitive";
672
+ }
673
+ if (opToken.kind === ts.SyntaxKind.GreaterThanEqualsToken) {
674
+ funcName = "jspp::greater_than_or_equal_primitive";
675
+ }
676
+ // For C++ primitive literals, standard operators are fine if they map directly,
677
+ // but we are safe using our functions (which handle doubles correctly).
678
+ // Actually, for pure numeric literals like "1 < 2", we can leave it as is if we want optimization,
679
+ // but consistency is safer.
680
+ // Let's stick to valid C++ syntax for literals if possible to avoid overhead?
681
+ // jspp::less_than(1, 2) works.
682
+ return `${funcName}(${literalLeft}, ${literalRight})`;
541
683
  }
542
684
  // Return boxed value
543
685
  if (opToken.kind === ts.SyntaxKind.EqualsEqualsEqualsToken) {
@@ -558,27 +700,38 @@ export function visitBinaryExpression(node, context) {
558
700
  if (opToken.kind === ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken) {
559
701
  return `jspp::unsigned_right_shift(${literalLeft}, ${literalRight})`;
560
702
  }
561
- // Use literal for at most one side
562
- if (isLiteral(binExpr.left)) {
563
- finalLeft = binExpr.left.getText();
564
- }
565
- else if (isLiteral(binExpr.right)) {
566
- finalRight = binExpr.right.getText();
567
- }
568
703
  // For other arithmetic and bitwise operations, use native operations if possible
569
- if (op === "+" ||
570
- op === "-" ||
571
- op === "*" ||
572
- op === "/" ||
573
- op === "%" ||
574
- op === "^" ||
575
- op === "&" ||
576
- op === "|" ||
577
- op === "<<" ||
578
- op === ">>") {
579
- return `(${finalLeft} ${op} ${finalRight})`;
580
- }
581
- return `${finalLeft} ${op} ${finalRight}`;
704
+ switch (op) {
705
+ case "+":
706
+ return `jspp::add(${literalLeft}, ${literalRight})`;
707
+ case "-":
708
+ return `jspp::sub(${literalLeft}, ${literalRight})`;
709
+ case "*":
710
+ return `jspp::mul(${literalLeft}, ${literalRight})`;
711
+ case "/":
712
+ return `jspp::div(${literalLeft}, ${literalRight})`;
713
+ case "%":
714
+ return `jspp::mod(${literalLeft}, ${literalRight})`;
715
+ case "^":
716
+ return `jspp::bitwise_xor(${literalLeft}, ${literalRight})`;
717
+ case "&":
718
+ return `jspp::bitwise_and(${literalLeft}, ${literalRight})`;
719
+ case "|":
720
+ return `jspp::bitwise_or(${literalLeft}, ${literalRight})`;
721
+ case "<<":
722
+ return `jspp::left_shift(${literalLeft}, ${literalRight})`;
723
+ case ">>":
724
+ return `jspp::right_shift(${literalLeft}, ${literalRight})`;
725
+ case "<":
726
+ return `jspp::less_than(${literalLeft}, ${literalRight})`;
727
+ case ">":
728
+ return `jspp::greater_than(${literalLeft}, ${literalRight})`;
729
+ case "<=":
730
+ return `jspp::less_than_or_equal(${literalLeft}, ${literalRight})`;
731
+ case ">=":
732
+ return `jspp::greater_than_or_equal(${literalLeft}, ${literalRight})`;
733
+ }
734
+ return `/* Unhandled Operator: ${finalLeft} ${op} ${finalRight} */`; // Default fallback
582
735
  }
583
736
  export function visitConditionalExpression(node, context) {
584
737
  const condExpr = node;
@@ -604,35 +757,113 @@ export function visitConditionalExpression(node, context) {
604
757
  export function visitCallExpression(node, context) {
605
758
  const callExpr = node;
606
759
  const callee = callExpr.expression;
760
+ const flattened = flattenArrayElements(callExpr.arguments);
761
+ const hasSpread = flattened.some((e) => typeof e === "object" && "dynamicSpread" in e);
607
762
  if (callee.kind === ts.SyntaxKind.SuperKeyword) {
608
763
  if (!context.superClassVar) {
609
764
  throw new CompilerError("super() called but no super class variable found in context", callee, "SyntaxError");
610
765
  }
611
- const args = callExpr.arguments.map((arg) => this.visit(arg, context))
612
- .join(", ");
613
- return `(${context.superClassVar}).call(${this.globalThisVar}, (const jspp::AnyValue[]){${args}}, "super")`;
766
+ if (!hasSpread) {
767
+ const args = flattened.map((arg) => this.visit(arg, context))
768
+ .join(", ");
769
+ return `(${context.superClassVar}).call(${this.globalThisVar}, (const jspp::AnyValue[]){${args}}, "super")`;
770
+ }
771
+ else {
772
+ const argsVar = this.generateUniqueName("__args_", this.getDeclaredSymbols(node));
773
+ let code = `([&]() {\n`;
774
+ code +=
775
+ `${this.indent()} std::vector<jspp::AnyValue> ${argsVar};\n`;
776
+ this.indentationLevel++;
777
+ for (const arg of flattened) {
778
+ if (typeof arg === "object" && "dynamicSpread" in arg) {
779
+ const spreadExprSource = arg.dynamicSpread;
780
+ let spreadExpr = this.visit(spreadExprSource, context);
781
+ if (ts.isIdentifier(spreadExprSource)) {
782
+ const scope = this.getScopeForNode(spreadExprSource);
783
+ const typeInfo = this.typeAnalyzer.scopeManager
784
+ .lookupFromScope(spreadExprSource.text, scope);
785
+ spreadExpr = this.getDerefCode(spreadExpr, this.getJsVarName(spreadExprSource), context, typeInfo);
786
+ }
787
+ code +=
788
+ `${this.indent()}jspp::Access::spread_array(${argsVar}, ${spreadExpr});\n`;
789
+ }
790
+ else {
791
+ const expr = arg;
792
+ let argText = this.visit(expr, context);
793
+ if (ts.isIdentifier(expr)) {
794
+ const scope = this.getScopeForNode(expr);
795
+ const typeInfo = this.typeAnalyzer.scopeManager
796
+ .lookupFromScope(expr.text, scope);
797
+ argText = this.getDerefCode(argText, this.getJsVarName(expr), context, typeInfo);
798
+ }
799
+ code +=
800
+ `${this.indent()}${argsVar}.push_back(${argText});\n`;
801
+ }
802
+ }
803
+ code +=
804
+ `${this.indent()} return (${context.superClassVar}).call(${this.globalThisVar}, ${argsVar}, "super");\n`;
805
+ this.indentationLevel--;
806
+ code += `${this.indent()}})()`;
807
+ return code;
808
+ }
614
809
  }
615
- const argsArray = callExpr.arguments
616
- .map((arg) => {
617
- const argText = this.visit(arg, context);
618
- if (ts.isIdentifier(arg)) {
619
- const scope = this.getScopeForNode(arg);
620
- const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(arg.text, scope);
621
- if (!typeInfo) {
622
- return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(arg)})`;
810
+ const generateArgsSpan = (args) => {
811
+ const argsArray = args
812
+ .map((arg) => {
813
+ const expr = arg;
814
+ const argText = this.visit(expr, context);
815
+ if (ts.isIdentifier(expr)) {
816
+ const scope = this.getScopeForNode(expr);
817
+ const typeInfo = this.typeAnalyzer.scopeManager
818
+ .lookupFromScope(expr.text, scope);
819
+ if (!typeInfo) {
820
+ return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(expr)})`;
821
+ }
822
+ if (typeInfo && !typeInfo.isBuiltin) {
823
+ return this.getDerefCode(argText, this.getJsVarName(expr), context, typeInfo);
824
+ }
825
+ }
826
+ return argText;
827
+ });
828
+ const argsJoined = argsArray.join(", ");
829
+ const argsCount = argsArray.length;
830
+ return argsCount > 0
831
+ ? `std::span<const jspp::AnyValue>((const jspp::AnyValue[]){${argsJoined}}, ${argsCount})`
832
+ : "std::span<const jspp::AnyValue>{}";
833
+ };
834
+ const generateArgsVectorBuilder = (args, argsVar) => {
835
+ let code = `${this.indent()}std::vector<jspp::AnyValue> ${argsVar};\n`;
836
+ for (const arg of args) {
837
+ if (typeof arg === "object" && "dynamicSpread" in arg) {
838
+ const spreadExprSource = arg.dynamicSpread;
839
+ let spreadExpr = this.visit(spreadExprSource, context);
840
+ if (ts.isIdentifier(spreadExprSource)) {
841
+ const scope = this.getScopeForNode(spreadExprSource);
842
+ const typeInfo = this.typeAnalyzer.scopeManager
843
+ .lookupFromScope(spreadExprSource.text, scope);
844
+ if (typeInfo && !typeInfo.isBuiltin && !typeInfo.isParameter) {
845
+ spreadExpr = this.getDerefCode(spreadExpr, this.getJsVarName(spreadExprSource), context, typeInfo);
846
+ }
847
+ }
848
+ code +=
849
+ `${this.indent()}jspp::Access::spread_array(${argsVar}, ${spreadExpr});\n`;
623
850
  }
624
- if (typeInfo && !typeInfo.isBuiltin) {
625
- return this.getDerefCode(argText, this.getJsVarName(arg), context, typeInfo);
851
+ else {
852
+ const expr = arg;
853
+ let argText = this.visit(expr, context);
854
+ if (ts.isIdentifier(expr)) {
855
+ const scope = this.getScopeForNode(expr);
856
+ const typeInfo = this.typeAnalyzer.scopeManager
857
+ .lookupFromScope(expr.text, scope);
858
+ if (typeInfo && !typeInfo.isBuiltin && !typeInfo.isParameter) {
859
+ argText = this.getDerefCode(argText, this.getJsVarName(expr), context, typeInfo);
860
+ }
861
+ }
862
+ code += `${this.indent()}${argsVar}.push_back(${argText});\n`;
626
863
  }
627
864
  }
628
- return argText;
629
- });
630
- const args = argsArray.join(", ");
631
- const argsCount = argsArray.length;
632
- const argsSpan = argsCount > 0
633
- ? `std::span<const jspp::AnyValue>((const jspp::AnyValue[]){${args}}, ${argsCount})`
634
- : "std::span<const jspp::AnyValue>{}";
635
- // Handle obj.method() -> pass obj as 'this'
865
+ return code;
866
+ };
636
867
  if (ts.isPropertyAccessExpression(callee)) {
637
868
  const propAccess = callee;
638
869
  if (propAccess.expression.kind === ts.SyntaxKind.SuperKeyword) {
@@ -640,12 +871,25 @@ export function visitCallExpression(node, context) {
640
871
  throw new CompilerError("super.method() called but no super class variable found in context", propAccess.expression, "SyntaxError");
641
872
  }
642
873
  const propName = propAccess.name.getText();
643
- return `(${context.superClassVar}).get_own_property("prototype").get_own_property("${propName}").call(${this.globalThisVar}, ${argsSpan}, "${this.escapeString(propName)}")`;
874
+ if (!hasSpread) {
875
+ const argsSpan = generateArgsSpan(flattened);
876
+ return `(${context.superClassVar}).get_own_property("prototype").get_own_property("${propName}").call(${this.globalThisVar}, ${argsSpan}, "${this.escapeString(propName)}")`;
877
+ }
878
+ else {
879
+ const argsVar = this.generateUniqueName("__args_", this.getDeclaredSymbols(node));
880
+ let code = `([&]() {\n`;
881
+ this.indentationLevel++;
882
+ code += generateArgsVectorBuilder(flattened, argsVar);
883
+ code +=
884
+ `${this.indent}return (${context.superClassVar}).get_own_property("prototype").get_own_property("${propName}").call(${this.globalThisVar}, ${argsVar}, "${this.escapeString(propName)}");\n`;
885
+ this.indentationLevel--;
886
+ code += `${this.indent()}})()`;
887
+ return code;
888
+ }
644
889
  }
645
890
  const objExpr = propAccess.expression;
646
891
  const propName = propAccess.name.getText();
647
892
  const objCode = this.visit(objExpr, context);
648
- // We need to dereference the object expression if it's a variable
649
893
  let derefObj = objCode;
650
894
  if (ts.isIdentifier(objExpr)) {
651
895
  const scope = this.getScopeForNode(objExpr);
@@ -657,12 +901,37 @@ export function visitCallExpression(node, context) {
657
901
  derefObj = this.getDerefCode(objCode, this.getJsVarName(objExpr), context, typeInfo);
658
902
  }
659
903
  }
660
- if (callExpr.questionDotToken) {
661
- return `jspp::Access::optional_call(${derefObj}.get_own_property("${propName}"), ${derefObj}, ${argsSpan}, "${this.escapeString(propName)}")`;
904
+ if (!hasSpread) {
905
+ const argsSpan = generateArgsSpan(flattened);
906
+ if (propAccess.questionDotToken) {
907
+ const method = callExpr.questionDotToken
908
+ ? "jspp::Access::call_optional_property_with_optional_call"
909
+ : "jspp::Access::call_optional_property";
910
+ return `${method}(${derefObj}, "${propName}", ${argsSpan}, "${this.escapeString(propName)}")`;
911
+ }
912
+ return `${derefObj}.call_own_property("${propName}", ${argsSpan})`;
913
+ }
914
+ else {
915
+ const argsVar = this.generateUniqueName("__args_", this.getDeclaredSymbols(node));
916
+ let code = `([&]() {\n`;
917
+ this.indentationLevel++;
918
+ code += generateArgsVectorBuilder(flattened, argsVar);
919
+ if (propAccess.questionDotToken) {
920
+ const method = callExpr.questionDotToken
921
+ ? "jspp::Access::call_optional_property_with_optional_call"
922
+ : "jspp::Access::call_optional_property";
923
+ code +=
924
+ `${this.indent()}return ${method}(${derefObj}, "${propName}", ${argsVar}, "${this.escapeString(propName)}");\n`;
925
+ }
926
+ else {
927
+ code +=
928
+ `${this.indent()}return ${derefObj}.call_own_property("${propName}", ${argsVar});\n`;
929
+ }
930
+ this.indentationLevel--;
931
+ code += `${this.indent()}})()`;
932
+ return code;
662
933
  }
663
- return `${derefObj}.call_own_property("${propName}", ${argsSpan})`;
664
934
  }
665
- // Handle obj[method]() -> pass obj as 'this'
666
935
  if (ts.isElementAccessExpression(callee)) {
667
936
  const elemAccess = callee;
668
937
  const objExpr = elemAccess.expression;
@@ -679,7 +948,6 @@ export function visitCallExpression(node, context) {
679
948
  }
680
949
  }
681
950
  let argText = visitObjectPropertyName.call(this, elemAccess.argumentExpression, { ...context, isBracketNotationPropertyAccess: true });
682
- // Dereference argument if needed (logic copied from visitElementAccessExpression)
683
951
  if (ts.isIdentifier(elemAccess.argumentExpression)) {
684
952
  const argScope = this.getScopeForNode(elemAccess.argumentExpression);
685
953
  const argTypeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(elemAccess.argumentExpression.getText(), argScope);
@@ -692,39 +960,135 @@ export function visitCallExpression(node, context) {
692
960
  argText = this.getDerefCode(argText, this.getJsVarName(elemAccess.argumentExpression), context, argTypeInfo);
693
961
  }
694
962
  }
695
- if (callExpr.questionDotToken) {
696
- return `jspp::Access::optional_call(${derefObj}.get_own_property(${argText}), ${derefObj}, ${argsSpan})`;
963
+ if (!hasSpread) {
964
+ const argsSpan = generateArgsSpan(flattened);
965
+ if (elemAccess.questionDotToken) {
966
+ const method = callExpr.questionDotToken
967
+ ? "jspp::Access::call_optional_property_with_optional_call"
968
+ : "jspp::Access::call_optional_property";
969
+ return `${method}(${derefObj}, ${argText}, ${argsSpan})`;
970
+ }
971
+ return `${derefObj}.call_own_property(${argText}, ${argsSpan})`;
972
+ }
973
+ else {
974
+ const argsVar = this.generateUniqueName("__args_", this.getDeclaredSymbols(node));
975
+ let code = `([&]() {\n`;
976
+ this.indentationLevel++;
977
+ code += generateArgsVectorBuilder(flattened, argsVar);
978
+ if (elemAccess.questionDotToken) {
979
+ const method = callExpr.questionDotToken
980
+ ? "jspp::Access::call_optional_property_with_optional_call"
981
+ : "jspp::Access::call_optional_property";
982
+ code +=
983
+ `${this.indent()}return ${method}(${derefObj}, ${argText}, ${argsVar});\n`;
984
+ }
985
+ else {
986
+ code +=
987
+ `${this.indent()}return ${derefObj}.call_own_property(${argText}, ${argsVar});\n`;
988
+ }
989
+ this.indentationLevel--;
990
+ code += `${this.indent()}})()`;
991
+ return code;
697
992
  }
698
- return `${derefObj}.call_own_property(${argText}, ${argsSpan})`;
699
993
  }
700
994
  const calleeCode = this.visit(callee, context);
701
995
  let derefCallee = calleeCode;
996
+ let calleeTypeInfo = null;
702
997
  if (ts.isIdentifier(callee)) {
703
998
  const scope = this.getScopeForNode(callee);
704
- const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(callee.text, scope);
705
- if (!typeInfo && !this.isBuiltinObject(callee)) {
999
+ calleeTypeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(callee.text, scope);
1000
+ if (!calleeTypeInfo && !this.isBuiltinObject(callee)) {
706
1001
  return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(callee)})`;
707
1002
  }
708
- if (typeInfo?.isBuiltin) {
1003
+ if (calleeTypeInfo?.isBuiltin) {
709
1004
  derefCallee = calleeCode;
710
1005
  }
711
- else if (typeInfo) {
712
- const name = callee.getText();
713
- const symbol = context.localScopeSymbols.get(name) ??
714
- context.globalScopeSymbols.get(name);
715
- // Optimization: Direct lambda call
716
- if (symbol && symbol.func?.nativeName) {
717
- const callExpr = `${symbol.func.nativeName}(jspp::Constants::UNDEFINED, ${argsSpan})`;
718
- if (symbol.func.isGenerator) {
719
- if (symbol.func.isAsync) {
720
- return `jspp::AnyValue::from_async_iterator(${callExpr})`;
1006
+ else if (calleeTypeInfo) {
1007
+ derefCallee = this.getDerefCode(calleeCode, this.getJsVarName(callee), context, calleeTypeInfo);
1008
+ }
1009
+ }
1010
+ // Direct native lamda if available
1011
+ if (ts.isIdentifier(callee) && calleeTypeInfo) {
1012
+ const name = callee.getText();
1013
+ const symbol = context.localScopeSymbols.get(name) ??
1014
+ context.globalScopeSymbols.get(name);
1015
+ const nativeFeature = symbol?.features?.native;
1016
+ if (nativeFeature && nativeFeature.type === "lambda") {
1017
+ const nativeName = nativeFeature.name;
1018
+ const parameters = nativeFeature.parameters || [];
1019
+ if (!hasSpread) {
1020
+ let argsPart = "";
1021
+ if (parameters) {
1022
+ const argsArray = flattened.map((arg) => {
1023
+ const expr = arg;
1024
+ let argText = this.visit(expr, context);
1025
+ if (ts.isIdentifier(expr)) {
1026
+ const scope = this.getScopeForNode(expr);
1027
+ const typeInfo = this.typeAnalyzer.scopeManager
1028
+ .lookupFromScope(expr.text, scope);
1029
+ argText = this.getDerefCode(argText, this.getJsVarName(expr), context, typeInfo);
1030
+ }
1031
+ return argText;
1032
+ });
1033
+ const argsText = argsArray.slice(0, parameters.length)
1034
+ .filter((_, i) => !parameters[i]?.dotDotDotToken).join(", ");
1035
+ if (argsText)
1036
+ argsPart += `, ${argsText}`;
1037
+ if (argsArray.length > parameters.length &&
1038
+ !!parameters[parameters.length - 1]?.dotDotDotToken) {
1039
+ const restArgsText = `jspp::AnyValue::make_array(std::vector<jspp::AnyValue>{${argsArray.slice(parameters.length - 1).join(", ")}})`;
1040
+ argsPart += `, ${restArgsText}`;
1041
+ }
1042
+ }
1043
+ const callImplementation = `${nativeName}(jspp::Constants::UNDEFINED${argsPart})`;
1044
+ if (symbol.features.isGenerator) {
1045
+ if (symbol.features.isAsync) {
1046
+ return `jspp::AnyValue::from_async_iterator(${callImplementation})`;
1047
+ }
1048
+ return `jspp::AnyValue::from_iterator(${callImplementation})`;
1049
+ }
1050
+ if (symbol.features.isAsync) {
1051
+ return `jspp::AnyValue::from_promise(${callImplementation})`;
1052
+ }
1053
+ return callImplementation;
1054
+ }
1055
+ else {
1056
+ const argsVar = this.generateUniqueName("__args_", this.getDeclaredSymbols(node));
1057
+ let code = `([&]() {\n`;
1058
+ this.indentationLevel++;
1059
+ code += generateArgsVectorBuilder(flattened, argsVar);
1060
+ const callArgs = [];
1061
+ for (let i = 0; i < parameters.length; i++) {
1062
+ const p = parameters[i];
1063
+ if (!p)
1064
+ continue;
1065
+ if (p.dotDotDotToken) {
1066
+ callArgs.push(`jspp::AnyValue::make_array(std::vector<jspp::AnyValue>(${argsVar}.begin() + std::min((size_t)${i}, ${argsVar}.size()), ${argsVar}.end()))`);
1067
+ }
1068
+ else {
1069
+ callArgs.push(`(${argsVar}.size() > ${i} ? ${argsVar}[${i}] : jspp::Constants::UNDEFINED)`);
1070
+ }
1071
+ }
1072
+ let callExprStr = `${nativeName}(jspp::Constants::UNDEFINED${callArgs.length > 0 ? ", " + callArgs.join(", ") : ""})`;
1073
+ if (symbol.features.isGenerator) {
1074
+ if (symbol.features.isAsync) {
1075
+ callExprStr =
1076
+ `jspp::AnyValue::from_async_iterator(${callExprStr})`;
1077
+ }
1078
+ else {
1079
+ callExprStr =
1080
+ `jspp::AnyValue::from_iterator(${callExprStr})`;
721
1081
  }
722
- return `jspp::AnyValue::from_iterator(${callExpr})`;
723
1082
  }
724
- return callExpr;
1083
+ else if (symbol.features.isAsync) {
1084
+ callExprStr =
1085
+ `jspp::AnyValue::from_promise(${callExprStr})`;
1086
+ }
1087
+ code += `${this.indent()}return ${callExprStr};\n`;
1088
+ this.indentationLevel--;
1089
+ code += `${this.indent()}})()`;
1090
+ return code;
725
1091
  }
726
- // AnyValue function call
727
- derefCallee = this.getDerefCode(calleeCode, this.getJsVarName(callee), context, typeInfo);
728
1092
  }
729
1093
  }
730
1094
  let calleeName = "";
@@ -736,14 +1100,33 @@ export function visitCallExpression(node, context) {
736
1100
  const funcExpr = callee.expression;
737
1101
  calleeName = this.escapeString(funcExpr.name?.getText() || "");
738
1102
  }
739
- // Pass undefined as 'this' for normal function calls
740
1103
  const calleeNamePart = calleeName && calleeName.length > 0
741
1104
  ? `, "${calleeName}"`
742
1105
  : "";
743
- if (callExpr.questionDotToken) {
744
- return `jspp::Access::optional_call(${derefCallee}, jspp::Constants::UNDEFINED, ${argsSpan}${calleeNamePart})`;
1106
+ if (!hasSpread) {
1107
+ const argsSpan = generateArgsSpan(flattened);
1108
+ if (callExpr.questionDotToken) {
1109
+ return `${derefCallee}.optional_call(jspp::Constants::UNDEFINED, ${argsSpan}${calleeNamePart})`;
1110
+ }
1111
+ return `${derefCallee}.call(jspp::Constants::UNDEFINED, ${argsSpan}${calleeNamePart})`;
1112
+ }
1113
+ else {
1114
+ const argsVar = this.generateUniqueName("__args_", this.getDeclaredSymbols(node));
1115
+ let code = `([&]() {\n`;
1116
+ this.indentationLevel++;
1117
+ code += generateArgsVectorBuilder(flattened, argsVar);
1118
+ if (callExpr.questionDotToken) {
1119
+ code +=
1120
+ `${this.indent()}return ${derefCallee}.optional_call(jspp::Constants::UNDEFINED, ${argsVar}${calleeNamePart});\n`;
1121
+ }
1122
+ else {
1123
+ code +=
1124
+ `${this.indent()}return ${derefCallee}.call(jspp::Constants::UNDEFINED, ${argsVar}${calleeNamePart});\n`;
1125
+ }
1126
+ this.indentationLevel--;
1127
+ code += `${this.indent()}})()`;
1128
+ return code;
745
1129
  }
746
- return `${derefCallee}.call(jspp::Constants::UNDEFINED, ${argsSpan}${calleeNamePart})`;
747
1130
  }
748
1131
  export function visitVoidExpression(node, context) {
749
1132
  const voidExpr = node;
@@ -769,9 +1152,9 @@ export function visitTemplateExpression(node, context) {
769
1152
  finalExpr = this.getDerefCode(exprText, this.getJsVarName(expr), context, typeInfo);
770
1153
  }
771
1154
  }
772
- result += ` + (${finalExpr})`;
1155
+ result = `jspp::add(${result}, ${finalExpr})`;
773
1156
  if (span.literal.text) {
774
- result += ` + jspp::AnyValue::make_string("${this.escapeString(span.literal.text)}")`;
1157
+ result = `jspp::add(${result}, jspp::AnyValue::make_string("${this.escapeString(span.literal.text)}"))`;
775
1158
  }
776
1159
  }
777
1160
  return result;
@@ -779,6 +1162,8 @@ export function visitTemplateExpression(node, context) {
779
1162
  export function visitNewExpression(node, context) {
780
1163
  const newExpr = node;
781
1164
  const exprText = this.visit(newExpr.expression, context);
1165
+ const flattened = flattenArrayElements(newExpr.arguments || []);
1166
+ const hasSpread = flattened.some((e) => typeof e === "object" && "dynamicSpread" in e);
782
1167
  let derefExpr = exprText;
783
1168
  let name = `"${exprText}"`;
784
1169
  if (ts.isIdentifier(newExpr.expression)) {
@@ -793,30 +1178,77 @@ export function visitNewExpression(node, context) {
793
1178
  derefExpr = this.getDerefCode(exprText, name, context, typeInfo);
794
1179
  }
795
1180
  }
796
- const argsArray = newExpr.arguments
797
- ? newExpr.arguments
1181
+ if (!hasSpread) {
1182
+ const argsArray = flattened
798
1183
  .map((arg) => {
799
- const argText = this.visit(arg, context);
800
- if (ts.isIdentifier(arg)) {
801
- const scope = this.getScopeForNode(arg);
1184
+ const expr = arg;
1185
+ const argText = this.visit(expr, context);
1186
+ if (ts.isIdentifier(expr)) {
1187
+ const scope = this.getScopeForNode(expr);
802
1188
  const typeInfo = this.typeAnalyzer.scopeManager
803
- .lookupFromScope(arg.text, scope);
1189
+ .lookupFromScope(expr.text, scope);
804
1190
  if (!typeInfo) {
805
- return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(arg)})`;
1191
+ return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(expr)})`;
806
1192
  }
807
- if (typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin) {
808
- return this.getDerefCode(argText, this.getJsVarName(arg), context, typeInfo);
1193
+ if (typeInfo && !typeInfo.isParameter &&
1194
+ !typeInfo.isBuiltin) {
1195
+ return this.getDerefCode(argText, this.getJsVarName(expr), context, typeInfo);
809
1196
  }
810
1197
  }
811
1198
  return argText;
812
- })
813
- : [];
814
- const args = argsArray.join(", ");
815
- const argsCount = argsArray.length;
816
- const argsSpan = argsCount > 0
817
- ? `std::span<const jspp::AnyValue>((const jspp::AnyValue[]){${args}}, ${argsCount})`
818
- : "std::span<const jspp::AnyValue>{}";
819
- return `${derefExpr}.construct(${argsSpan}, ${name})`;
1199
+ });
1200
+ const args = argsArray.join(", ");
1201
+ const argsCount = argsArray.length;
1202
+ const argsSpan = argsCount > 0
1203
+ ? `std::span<const jspp::AnyValue>((const jspp::AnyValue[]){${args}}, ${argsCount})`
1204
+ : "std::span<const jspp::AnyValue>{}";
1205
+ return `${derefExpr}.construct(${argsSpan}, ${name})`;
1206
+ }
1207
+ else {
1208
+ const argsVar = this.generateUniqueName("__args_", this.getDeclaredSymbols(node));
1209
+ let code = `([&]() {\n`;
1210
+ this.indentationLevel++;
1211
+ code += `${this.indent()}std::vector<jspp::AnyValue> ${argsVar};\n`;
1212
+ if (newExpr.arguments) {
1213
+ for (const arg of flattened) {
1214
+ if (typeof arg === "object" && "dynamicSpread" in arg) {
1215
+ const spreadExprSource = arg.dynamicSpread;
1216
+ let spreadExpr = this.visit(spreadExprSource, context);
1217
+ if (ts.isIdentifier(spreadExprSource)) {
1218
+ const scope = this.getScopeForNode(spreadExprSource);
1219
+ const typeInfo = this.typeAnalyzer.scopeManager
1220
+ .lookupFromScope(spreadExprSource.text, scope);
1221
+ if (typeInfo && !typeInfo.isBuiltin &&
1222
+ !typeInfo.isParameter) {
1223
+ spreadExpr = this.getDerefCode(spreadExpr, this.getJsVarName(spreadExprSource), context, typeInfo);
1224
+ }
1225
+ }
1226
+ code +=
1227
+ `${this.indent()}jspp::Access::spread_array(${argsVar}, ${spreadExpr});\n`;
1228
+ }
1229
+ else {
1230
+ const expr = arg;
1231
+ let argText = this.visit(expr, context);
1232
+ if (ts.isIdentifier(expr)) {
1233
+ const scope = this.getScopeForNode(expr);
1234
+ const typeInfo = this.typeAnalyzer.scopeManager
1235
+ .lookupFromScope(expr.text, scope);
1236
+ if (typeInfo && !typeInfo.isBuiltin &&
1237
+ !typeInfo.isParameter) {
1238
+ argText = this.getDerefCode(argText, this.getJsVarName(expr), context, typeInfo);
1239
+ }
1240
+ }
1241
+ code +=
1242
+ `${this.indent()}${argsVar}.push_back(${argText});\n`;
1243
+ }
1244
+ }
1245
+ }
1246
+ code +=
1247
+ `${this.indent()} return ${derefExpr}.construct(${argsVar}, ${name});\n`;
1248
+ this.indentationLevel--;
1249
+ code += `${this.indent()}})()`;
1250
+ return code;
1251
+ }
820
1252
  }
821
1253
  export function visitTypeOfExpression(node, context) {
822
1254
  const typeOfExpr = node;