@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.
- package/dist/analysis/typeAnalyzer.js +56 -24
- package/dist/ast/symbols.js +28 -19
- package/dist/cli/index.js +1 -1
- package/dist/core/codegen/class-handlers.js +8 -8
- package/dist/core/codegen/control-flow-handlers.js +17 -7
- package/dist/core/codegen/declaration-handlers.js +21 -9
- package/dist/core/codegen/expression-handlers.js +558 -126
- package/dist/core/codegen/function-handlers.js +101 -108
- package/dist/core/codegen/helpers.js +28 -7
- package/dist/core/codegen/index.js +6 -4
- package/dist/core/codegen/literal-handlers.js +4 -2
- package/dist/core/codegen/statement-handlers.js +39 -19
- package/package.json +1 -1
- package/scripts/precompile-headers.ts +8 -1
- package/src/prelude/any_value.hpp +89 -59
- package/src/prelude/any_value_access.hpp +1 -1
- package/src/prelude/any_value_helpers.hpp +85 -43
- package/src/prelude/index.hpp +1 -0
- package/src/prelude/library/array.hpp +3 -2
- package/src/prelude/types.hpp +8 -8
- package/src/prelude/utils/access.hpp +62 -6
- package/src/prelude/utils/assignment_operators.hpp +14 -14
- package/src/prelude/utils/log_any_value/array.hpp +0 -15
- package/src/prelude/utils/log_any_value/primitives.hpp +2 -0
- package/src/prelude/utils/operators.hpp +117 -474
- package/src/prelude/utils/operators_primitive.hpp +337 -0
- package/src/prelude/values/helpers/array.hpp +4 -4
- package/src/prelude/values/helpers/async_iterator.hpp +2 -2
- package/src/prelude/values/helpers/function.hpp +3 -3
- package/src/prelude/values/helpers/iterator.hpp +2 -2
- package/src/prelude/values/helpers/object.hpp +3 -3
- package/src/prelude/values/helpers/promise.hpp +1 -1
- package/src/prelude/values/helpers/string.hpp +1 -1
- package/src/prelude/values/helpers/symbol.hpp +1 -1
- package/src/prelude/values/prototypes/array.hpp +1125 -853
- package/src/prelude/values/prototypes/async_iterator.hpp +32 -14
- package/src/prelude/values/prototypes/function.hpp +30 -18
- package/src/prelude/values/prototypes/iterator.hpp +40 -17
- package/src/prelude/values/prototypes/number.hpp +119 -62
- package/src/prelude/values/prototypes/object.hpp +10 -4
- package/src/prelude/values/prototypes/promise.hpp +167 -109
- package/src/prelude/values/prototypes/string.hpp +407 -231
- 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 (!
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
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
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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
|
|
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::
|
|
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::
|
|
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.
|
|
459
|
-
|
|
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.
|
|
464
|
-
|
|
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
|
-
|
|
537
|
-
|
|
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
|
-
|
|
540
|
-
|
|
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
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
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
|
-
|
|
612
|
-
.
|
|
613
|
-
|
|
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
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
const
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
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
|
-
|
|
625
|
-
|
|
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
|
|
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
|
-
|
|
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 (
|
|
661
|
-
|
|
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 (
|
|
696
|
-
|
|
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
|
-
|
|
705
|
-
if (!
|
|
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 (
|
|
1003
|
+
if (calleeTypeInfo?.isBuiltin) {
|
|
709
1004
|
derefCallee = calleeCode;
|
|
710
1005
|
}
|
|
711
|
-
else if (
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
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
|
-
|
|
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 (
|
|
744
|
-
|
|
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
|
|
1155
|
+
result = `jspp::add(${result}, ${finalExpr})`;
|
|
773
1156
|
if (span.literal.text) {
|
|
774
|
-
result
|
|
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
|
-
|
|
797
|
-
|
|
1181
|
+
if (!hasSpread) {
|
|
1182
|
+
const argsArray = flattened
|
|
798
1183
|
.map((arg) => {
|
|
799
|
-
const
|
|
800
|
-
|
|
801
|
-
|
|
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(
|
|
1189
|
+
.lookupFromScope(expr.text, scope);
|
|
804
1190
|
if (!typeInfo) {
|
|
805
|
-
return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(
|
|
1191
|
+
return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(expr)})`;
|
|
806
1192
|
}
|
|
807
|
-
if (typeInfo && !typeInfo.isParameter &&
|
|
808
|
-
|
|
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
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
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;
|