@ugo-studio/jspp 0.1.3 → 0.1.5
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/README.md +2 -2
- package/dist/analysis/scope.js +33 -4
- package/dist/analysis/typeAnalyzer.js +260 -21
- package/dist/ast/symbols.js +29 -0
- package/dist/cli-utils/args.js +57 -0
- package/dist/cli-utils/colors.js +9 -0
- package/dist/cli-utils/file-utils.js +20 -0
- package/dist/cli-utils/spinner.js +55 -0
- package/dist/cli.js +105 -31
- package/dist/core/codegen/class-handlers.js +131 -0
- package/dist/core/codegen/control-flow-handlers.js +474 -0
- package/dist/core/codegen/declaration-handlers.js +36 -15
- package/dist/core/codegen/expression-handlers.js +579 -125
- package/dist/core/codegen/function-handlers.js +222 -37
- package/dist/core/codegen/helpers.js +158 -4
- package/dist/core/codegen/index.js +20 -8
- package/dist/core/codegen/literal-handlers.js +18 -6
- package/dist/core/codegen/statement-handlers.js +171 -228
- package/dist/core/codegen/visitor.js +31 -3
- package/package.json +3 -3
- package/src/prelude/any_value.hpp +510 -633
- package/src/prelude/any_value_access.hpp +151 -0
- package/src/prelude/any_value_defines.hpp +190 -0
- package/src/prelude/any_value_helpers.hpp +139 -225
- package/src/prelude/exception.hpp +32 -0
- package/src/prelude/exception_helpers.hpp +49 -0
- package/src/prelude/index.hpp +25 -9
- package/src/prelude/library/array.hpp +190 -0
- package/src/prelude/library/console.hpp +14 -13
- package/src/prelude/library/error.hpp +113 -0
- package/src/prelude/library/function.hpp +10 -0
- package/src/prelude/library/global.hpp +35 -4
- package/src/prelude/library/math.hpp +308 -0
- package/src/prelude/library/object.hpp +288 -0
- package/src/prelude/library/performance.hpp +2 -2
- package/src/prelude/library/process.hpp +39 -0
- package/src/prelude/library/promise.hpp +131 -0
- package/src/prelude/library/symbol.hpp +46 -59
- package/src/prelude/library/timer.hpp +92 -0
- package/src/prelude/scheduler.hpp +145 -0
- package/src/prelude/types.hpp +58 -1
- package/src/prelude/utils/access.hpp +345 -0
- package/src/prelude/utils/assignment_operators.hpp +99 -0
- package/src/prelude/utils/log_any_value/array.hpp +245 -0
- package/src/prelude/utils/log_any_value/config.hpp +32 -0
- package/src/prelude/utils/log_any_value/function.hpp +39 -0
- package/src/prelude/utils/log_any_value/fwd.hpp +15 -0
- package/src/prelude/utils/log_any_value/helpers.hpp +62 -0
- package/src/prelude/utils/log_any_value/log_any_value.hpp +94 -0
- package/src/prelude/utils/log_any_value/object.hpp +136 -0
- package/src/prelude/utils/log_any_value/primitives.hpp +43 -0
- package/src/prelude/utils/operators.hpp +751 -0
- package/src/prelude/utils/well_known_symbols.hpp +25 -0
- package/src/prelude/values/array.hpp +10 -7
- package/src/prelude/{descriptors.hpp → values/descriptors.hpp} +2 -2
- package/src/prelude/values/function.hpp +85 -51
- package/src/prelude/values/helpers/array.hpp +80 -35
- package/src/prelude/values/helpers/function.hpp +110 -77
- package/src/prelude/values/helpers/iterator.hpp +16 -10
- package/src/prelude/values/helpers/object.hpp +85 -10
- package/src/prelude/values/helpers/promise.hpp +181 -0
- package/src/prelude/values/helpers/string.hpp +3 -3
- package/src/prelude/values/helpers/symbol.hpp +2 -2
- package/src/prelude/values/iterator.hpp +14 -6
- package/src/prelude/values/object.hpp +14 -3
- package/src/prelude/values/promise.hpp +73 -0
- package/src/prelude/values/prototypes/array.hpp +855 -16
- package/src/prelude/values/prototypes/function.hpp +4 -4
- package/src/prelude/values/prototypes/iterator.hpp +11 -10
- package/src/prelude/values/prototypes/number.hpp +153 -0
- package/src/prelude/values/prototypes/object.hpp +26 -0
- package/src/prelude/values/prototypes/promise.hpp +134 -0
- package/src/prelude/values/prototypes/string.hpp +29 -29
- package/src/prelude/values/prototypes/symbol.hpp +22 -3
- package/src/prelude/values/shape.hpp +52 -0
- package/src/prelude/values/string.hpp +1 -1
- package/src/prelude/values/symbol.hpp +1 -1
- package/src/prelude/access.hpp +0 -91
- package/src/prelude/error.hpp +0 -31
- package/src/prelude/error_helpers.hpp +0 -59
- package/src/prelude/log_string.hpp +0 -407
- package/src/prelude/operators.hpp +0 -256
- package/src/prelude/well_known_symbols.hpp +0 -14
|
@@ -1,64 +1,159 @@
|
|
|
1
1
|
import ts from "typescript";
|
|
2
2
|
import { CodeGenerator } from "./";
|
|
3
|
-
function visitObjectPropertyName(node, context) {
|
|
3
|
+
export function visitObjectPropertyName(node, context) {
|
|
4
4
|
if (ts.isNumericLiteral(node)) {
|
|
5
|
-
return
|
|
5
|
+
return context.isBracketNotationPropertyAccess
|
|
6
|
+
? node.getText()
|
|
7
|
+
: `"${node.getText()}"`;
|
|
6
8
|
}
|
|
7
|
-
|
|
9
|
+
if (ts.isStringLiteral(node)) {
|
|
8
10
|
return `"${node.getText().substring(1, node.getText().length - 1) // remove trailing "' from original name
|
|
9
11
|
}"`;
|
|
10
12
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
if (ts.isComputedPropertyName(node)) {
|
|
14
|
+
const compExpr = node.expression;
|
|
15
|
+
let propName = this.visit(compExpr, context);
|
|
16
|
+
if (ts.isIdentifier(compExpr)) {
|
|
17
|
+
const scope = this.getScopeForNode(compExpr);
|
|
18
|
+
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(compExpr.getText(), scope);
|
|
19
|
+
propName = this.getDerefCode(propName, this.getJsVarName(compExpr), context, typeInfo);
|
|
20
|
+
}
|
|
21
|
+
return propName;
|
|
17
22
|
}
|
|
18
|
-
|
|
23
|
+
if (context.isBracketNotationPropertyAccess) {
|
|
19
24
|
return this.visit(node, context);
|
|
20
25
|
}
|
|
21
|
-
|
|
22
|
-
return `"${node.getText()}"`;
|
|
23
|
-
}
|
|
26
|
+
return `"${node.getText()}"`;
|
|
24
27
|
}
|
|
25
28
|
export function visitObjectLiteralExpression(node, context) {
|
|
26
29
|
const obj = node;
|
|
27
|
-
|
|
30
|
+
const objVar = this.generateUniqueName("__obj_", this.getDeclaredSymbols(node));
|
|
31
|
+
let code = `([&]() {
|
|
32
|
+
${this.indent()} auto ${objVar} = jspp::AnyValue::make_object_with_proto({}, ::Object.get_own_property("prototype"));\n`;
|
|
33
|
+
this.indentationLevel++;
|
|
28
34
|
for (const prop of obj.properties) {
|
|
29
|
-
// console.log("Property kind:", ts.SyntaxKind[prop.kind]);
|
|
30
35
|
if (ts.isPropertyAssignment(prop)) {
|
|
31
|
-
const key = visitObjectPropertyName.call(this, prop.name,
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
+
const key = visitObjectPropertyName.call(this, prop.name, {
|
|
37
|
+
...context,
|
|
38
|
+
isObjectLiteralExpression: true,
|
|
39
|
+
});
|
|
40
|
+
const initializer = prop.initializer;
|
|
41
|
+
let value = this.visit(initializer, context);
|
|
42
|
+
if (ts.isIdentifier(initializer)) {
|
|
43
|
+
const scope = this.getScopeForNode(initializer);
|
|
44
|
+
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(initializer.text, scope);
|
|
45
|
+
if (typeInfo && !typeInfo.isBuiltin && !typeInfo.isParameter) {
|
|
46
|
+
value = this.getDerefCode(value, this.getJsVarName(initializer), context, typeInfo);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
code +=
|
|
50
|
+
`${this.indent()}${objVar}.define_data_property(${key}, ${value});\n`;
|
|
36
51
|
}
|
|
37
52
|
else if (ts.isShorthandPropertyAssignment(prop)) {
|
|
38
|
-
const key = visitObjectPropertyName.call(this, prop.name,
|
|
39
|
-
|
|
40
|
-
|
|
53
|
+
const key = visitObjectPropertyName.call(this, prop.name, {
|
|
54
|
+
...context,
|
|
55
|
+
isObjectLiteralExpression: true,
|
|
56
|
+
});
|
|
57
|
+
const scope = this.getScopeForNode(prop.name);
|
|
58
|
+
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(prop.name.text, scope);
|
|
59
|
+
let value = this.visit(prop.name, context);
|
|
60
|
+
if (typeInfo && !typeInfo.isBuiltin && !typeInfo.isParameter) {
|
|
61
|
+
value = this.getDerefCode(value, this.getJsVarName(prop.name), context, typeInfo);
|
|
62
|
+
}
|
|
63
|
+
code +=
|
|
64
|
+
`${this.indent()}${objVar}.define_data_property(${key}, ${value});\n`;
|
|
65
|
+
}
|
|
66
|
+
else if (ts.isMethodDeclaration(prop)) {
|
|
67
|
+
const key = visitObjectPropertyName.call(this, prop.name, {
|
|
68
|
+
...context,
|
|
69
|
+
isObjectLiteralExpression: true,
|
|
70
|
+
});
|
|
71
|
+
const lambda = this.generateLambda(prop, {
|
|
72
|
+
...context,
|
|
73
|
+
isInsideFunction: true,
|
|
74
|
+
});
|
|
75
|
+
code +=
|
|
76
|
+
`${this.indent()}${objVar}.define_data_property(${key}, ${lambda});\n`;
|
|
77
|
+
}
|
|
78
|
+
else if (ts.isGetAccessor(prop)) {
|
|
79
|
+
const key = visitObjectPropertyName.call(this, prop.name, {
|
|
80
|
+
...context,
|
|
81
|
+
isObjectLiteralExpression: true,
|
|
82
|
+
});
|
|
83
|
+
const lambda = this.generateLambda(prop, {
|
|
84
|
+
...context,
|
|
85
|
+
isInsideFunction: true,
|
|
86
|
+
});
|
|
87
|
+
code +=
|
|
88
|
+
`${this.indent()}${objVar}.define_getter(${key}, ${lambda});\n`;
|
|
89
|
+
}
|
|
90
|
+
else if (ts.isSetAccessor(prop)) {
|
|
91
|
+
const key = visitObjectPropertyName.call(this, prop.name, {
|
|
92
|
+
...context,
|
|
93
|
+
isObjectLiteralExpression: true,
|
|
94
|
+
});
|
|
95
|
+
const lambda = this.generateLambda(prop, {
|
|
96
|
+
...context,
|
|
97
|
+
isInsideFunction: true,
|
|
98
|
+
});
|
|
99
|
+
code +=
|
|
100
|
+
`${this.indent()}${objVar}.define_setter(${key}, ${lambda});\n`;
|
|
41
101
|
}
|
|
42
102
|
}
|
|
43
|
-
|
|
103
|
+
this.indentationLevel--;
|
|
104
|
+
code += `${this.indent()} return ${objVar};\n${this.indent()}})()`;
|
|
105
|
+
return code;
|
|
44
106
|
}
|
|
45
107
|
export function visitArrayLiteralExpression(node, context) {
|
|
46
108
|
const elements = node.elements
|
|
47
|
-
.map((elem) =>
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
109
|
+
.map((elem) => {
|
|
110
|
+
let elemText = this.visit(elem, context);
|
|
111
|
+
if (ts.isIdentifier(elem)) {
|
|
112
|
+
const scope = this.getScopeForNode(elem);
|
|
113
|
+
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(elem.text, scope);
|
|
114
|
+
if (typeInfo && !typeInfo.isBuiltin && !typeInfo.isParameter) {
|
|
115
|
+
elemText = this.getDerefCode(elemText, this.getJsVarName(elem), context, typeInfo);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return elemText;
|
|
119
|
+
});
|
|
120
|
+
const elementsJoined = elements.join(", ");
|
|
121
|
+
const elementsSpan = elements.length > 0
|
|
122
|
+
? `std::span<const jspp::AnyValue>((const jspp::AnyValue[]){${elementsJoined}}, ${elements.length})`
|
|
123
|
+
: "std::span<const jspp::AnyValue>{}";
|
|
124
|
+
return `jspp::AnyValue::make_array_with_proto(${elementsSpan}, ::Array.get_own_property("prototype"))`;
|
|
52
125
|
}
|
|
53
126
|
export function visitPrefixUnaryExpression(node, context) {
|
|
54
127
|
const prefixUnaryExpr = node;
|
|
55
128
|
const operand = this.visit(prefixUnaryExpr.operand, context);
|
|
56
129
|
const operator = ts.tokenToString(prefixUnaryExpr.operator);
|
|
57
130
|
if (operator === "++" || operator === "--") {
|
|
58
|
-
|
|
131
|
+
let target = operand;
|
|
132
|
+
if (ts.isIdentifier(prefixUnaryExpr.operand)) {
|
|
133
|
+
const scope = this.getScopeForNode(prefixUnaryExpr.operand);
|
|
134
|
+
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(prefixUnaryExpr.operand.getText(), scope);
|
|
135
|
+
if (context.derefBeforeAssignment) {
|
|
136
|
+
target = this.getDerefCode(operand, operand, context, typeInfo);
|
|
137
|
+
}
|
|
138
|
+
else if (typeInfo.needsHeapAllocation) {
|
|
139
|
+
target = `*${operand}`;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return `${operator}(${target})`;
|
|
59
143
|
}
|
|
60
144
|
if (operator === "~") {
|
|
61
|
-
|
|
145
|
+
let target = operand;
|
|
146
|
+
if (ts.isIdentifier(prefixUnaryExpr.operand)) {
|
|
147
|
+
const scope = this.getScopeForNode(prefixUnaryExpr.operand);
|
|
148
|
+
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(prefixUnaryExpr.operand.getText(), scope);
|
|
149
|
+
if (context.derefBeforeAssignment) {
|
|
150
|
+
target = this.getDerefCode(operand, operand, context, typeInfo);
|
|
151
|
+
}
|
|
152
|
+
else if (typeInfo.needsHeapAllocation) {
|
|
153
|
+
target = `*${operand}`;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return `${operator}(${target})`;
|
|
62
157
|
}
|
|
63
158
|
return `${operator}${operand}`;
|
|
64
159
|
}
|
|
@@ -66,7 +161,18 @@ export function visitPostfixUnaryExpression(node, context) {
|
|
|
66
161
|
const postfixUnaryExpr = node;
|
|
67
162
|
const operand = this.visit(postfixUnaryExpr.operand, context);
|
|
68
163
|
const operator = ts.tokenToString(postfixUnaryExpr.operator);
|
|
69
|
-
|
|
164
|
+
let target = operand;
|
|
165
|
+
if (ts.isIdentifier(postfixUnaryExpr.operand)) {
|
|
166
|
+
const scope = this.getScopeForNode(postfixUnaryExpr.operand);
|
|
167
|
+
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(postfixUnaryExpr.operand.getText(), scope);
|
|
168
|
+
if (context.derefBeforeAssignment) {
|
|
169
|
+
target = this.getDerefCode(operand, operand, context, typeInfo);
|
|
170
|
+
}
|
|
171
|
+
else if (typeInfo.needsHeapAllocation) {
|
|
172
|
+
target = `*${operand}`;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return `(${target})${operator}`;
|
|
70
176
|
}
|
|
71
177
|
export function visitParenthesizedExpression(node, context) {
|
|
72
178
|
const parenExpr = node;
|
|
@@ -74,6 +180,13 @@ export function visitParenthesizedExpression(node, context) {
|
|
|
74
180
|
}
|
|
75
181
|
export function visitPropertyAccessExpression(node, context) {
|
|
76
182
|
const propAccess = node;
|
|
183
|
+
if (propAccess.expression.kind === ts.SyntaxKind.SuperKeyword) {
|
|
184
|
+
if (!context.superClassVar) {
|
|
185
|
+
throw new Error("super.prop accessed but no super class variable found in context");
|
|
186
|
+
}
|
|
187
|
+
const propName = propAccess.name.getText();
|
|
188
|
+
return `jspp::AnyValue::resolve_property_for_read((${context.superClassVar}).get_own_property("prototype").get_own_property("${propName}"), ${this.globalThisVar}, "${this.escapeString(propName)}")`;
|
|
189
|
+
}
|
|
77
190
|
const exprText = this.visit(propAccess.expression, context);
|
|
78
191
|
const propName = propAccess.name.getText();
|
|
79
192
|
const scope = this.getScopeForNode(propAccess.expression);
|
|
@@ -82,21 +195,24 @@ export function visitPropertyAccessExpression(node, context) {
|
|
|
82
195
|
: null;
|
|
83
196
|
if (ts.isIdentifier(propAccess.expression) && !typeInfo &&
|
|
84
197
|
!this.isBuiltinObject(propAccess.expression)) {
|
|
85
|
-
return `jspp::
|
|
198
|
+
return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(propAccess.expression)})`;
|
|
86
199
|
}
|
|
87
|
-
let finalExpr =
|
|
88
|
-
if (typeInfo &&
|
|
89
|
-
|
|
200
|
+
let finalExpr = exprText;
|
|
201
|
+
if (typeInfo &&
|
|
202
|
+
!typeInfo.isParameter &&
|
|
203
|
+
!typeInfo.isBuiltin &&
|
|
204
|
+
ts.isIdentifier(propAccess.expression)) {
|
|
205
|
+
finalExpr = this.getDerefCode(exprText, this.getJsVarName(propAccess.expression), context, typeInfo);
|
|
90
206
|
}
|
|
91
|
-
|
|
92
|
-
finalExpr
|
|
207
|
+
if (propAccess.questionDotToken) {
|
|
208
|
+
return `jspp::Access::optional_get_property(${finalExpr}, "${propName}")`;
|
|
93
209
|
}
|
|
94
210
|
return `${finalExpr}.get_own_property("${propName}")`;
|
|
95
211
|
}
|
|
96
212
|
export function visitElementAccessExpression(node, context) {
|
|
97
213
|
const elemAccess = node;
|
|
98
214
|
const exprText = this.visit(elemAccess.expression, context);
|
|
99
|
-
let argText = visitObjectPropertyName.call(this, elemAccess.argumentExpression, { ...context,
|
|
215
|
+
let argText = visitObjectPropertyName.call(this, elemAccess.argumentExpression, { ...context, isBracketNotationPropertyAccess: true });
|
|
100
216
|
// Dereference the expression being accessed
|
|
101
217
|
const exprScope = this.getScopeForNode(elemAccess.expression);
|
|
102
218
|
const exprTypeInfo = ts.isIdentifier(elemAccess.expression)
|
|
@@ -104,50 +220,159 @@ export function visitElementAccessExpression(node, context) {
|
|
|
104
220
|
: null;
|
|
105
221
|
if (ts.isIdentifier(elemAccess.expression) && !exprTypeInfo &&
|
|
106
222
|
!this.isBuiltinObject(elemAccess.expression)) {
|
|
107
|
-
return `jspp::
|
|
223
|
+
return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(elemAccess.expression)})`;
|
|
224
|
+
}
|
|
225
|
+
let finalExpr = exprText;
|
|
226
|
+
if (exprTypeInfo &&
|
|
227
|
+
!exprTypeInfo.isParameter &&
|
|
228
|
+
!exprTypeInfo.isBuiltin &&
|
|
229
|
+
ts.isIdentifier(elemAccess.expression)) {
|
|
230
|
+
finalExpr = this.getDerefCode(exprText, this.getJsVarName(elemAccess.expression), context, exprTypeInfo);
|
|
108
231
|
}
|
|
109
|
-
const finalExpr = exprTypeInfo && !exprTypeInfo.isParameter && !exprTypeInfo.isBuiltin
|
|
110
|
-
? `jspp::Access::deref(${exprText}, ${this.getJsVarName(elemAccess.expression)})`
|
|
111
|
-
: exprText;
|
|
112
232
|
// Dereference the argument expression if it's an identifier
|
|
113
233
|
if (ts.isIdentifier(elemAccess.argumentExpression)) {
|
|
114
234
|
const argScope = this.getScopeForNode(elemAccess.argumentExpression);
|
|
115
235
|
const argTypeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(elemAccess.argumentExpression.getText(), argScope);
|
|
116
236
|
if (!argTypeInfo && !this.isBuiltinObject(elemAccess.argumentExpression)) {
|
|
117
|
-
return `jspp::
|
|
237
|
+
return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(elemAccess.argumentExpression)})`;
|
|
118
238
|
}
|
|
119
|
-
if (argTypeInfo &&
|
|
120
|
-
|
|
239
|
+
if (argTypeInfo &&
|
|
240
|
+
!argTypeInfo.isParameter &&
|
|
241
|
+
!argTypeInfo.isBuiltin) {
|
|
242
|
+
argText = this.getDerefCode(argText, this.getJsVarName(elemAccess.argumentExpression), context, argTypeInfo);
|
|
121
243
|
}
|
|
122
244
|
}
|
|
245
|
+
if (elemAccess.questionDotToken) {
|
|
246
|
+
return `jspp::Access::optional_get_element(${finalExpr}, ${argText})`;
|
|
247
|
+
}
|
|
123
248
|
return `${finalExpr}.get_own_property(${argText})`;
|
|
124
249
|
}
|
|
125
250
|
export function visitBinaryExpression(node, context) {
|
|
126
251
|
const binExpr = node;
|
|
127
252
|
const opToken = binExpr.operatorToken;
|
|
128
253
|
let op = opToken.getText();
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
254
|
+
const assignmentOperators = [
|
|
255
|
+
ts.SyntaxKind.PlusEqualsToken,
|
|
256
|
+
ts.SyntaxKind.MinusEqualsToken,
|
|
257
|
+
ts.SyntaxKind.AsteriskEqualsToken,
|
|
258
|
+
ts.SyntaxKind.SlashEqualsToken,
|
|
259
|
+
ts.SyntaxKind.PercentEqualsToken,
|
|
260
|
+
ts.SyntaxKind.AsteriskAsteriskEqualsToken,
|
|
261
|
+
ts.SyntaxKind.LessThanLessThanEqualsToken,
|
|
262
|
+
ts.SyntaxKind.GreaterThanGreaterThanEqualsToken,
|
|
263
|
+
ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken,
|
|
264
|
+
ts.SyntaxKind.AmpersandEqualsToken,
|
|
265
|
+
ts.SyntaxKind.BarEqualsToken,
|
|
266
|
+
ts.SyntaxKind.CaretEqualsToken,
|
|
267
|
+
ts.SyntaxKind.AmpersandAmpersandEqualsToken,
|
|
268
|
+
ts.SyntaxKind.BarBarEqualsToken,
|
|
269
|
+
ts.SyntaxKind.QuestionQuestionEqualsToken,
|
|
270
|
+
];
|
|
271
|
+
if (assignmentOperators.includes(opToken.kind)) {
|
|
272
|
+
if (opToken.kind ===
|
|
273
|
+
ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken) {
|
|
274
|
+
const leftText = this.visit(binExpr.left, context);
|
|
275
|
+
const rightText = this.visit(binExpr.right, context);
|
|
276
|
+
let target = leftText;
|
|
277
|
+
if (ts.isIdentifier(binExpr.left)) {
|
|
278
|
+
const scope = this.getScopeForNode(binExpr.left);
|
|
279
|
+
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.left.text, scope);
|
|
280
|
+
target = typeInfo.needsHeapAllocation
|
|
281
|
+
? `*${leftText}`
|
|
282
|
+
: leftText;
|
|
283
|
+
return `${target} = jspp::unsigned_right_shift(${target}, ${rightText})`;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
if (opToken.kind === ts.SyntaxKind.AsteriskAsteriskEqualsToken) {
|
|
287
|
+
const leftText = this.visit(binExpr.left, context);
|
|
288
|
+
const rightText = this.visit(binExpr.right, context);
|
|
289
|
+
let target = leftText;
|
|
290
|
+
if (ts.isIdentifier(binExpr.left)) {
|
|
291
|
+
const scope = this.getScopeForNode(binExpr.left);
|
|
292
|
+
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.left.text, scope);
|
|
293
|
+
target = typeInfo.needsHeapAllocation
|
|
294
|
+
? `*${leftText}`
|
|
295
|
+
: leftText;
|
|
296
|
+
return `${target} = jspp::pow(${target}, ${rightText})`;
|
|
297
|
+
}
|
|
298
|
+
// For complex LHS, we need a different approach, but this is a start.
|
|
299
|
+
}
|
|
300
|
+
if (opToken.kind === ts.SyntaxKind.AmpersandAmpersandEqualsToken ||
|
|
301
|
+
opToken.kind === ts.SyntaxKind.BarBarEqualsToken ||
|
|
302
|
+
opToken.kind === ts.SyntaxKind.QuestionQuestionEqualsToken) {
|
|
303
|
+
const leftText = this.visit(binExpr.left, context);
|
|
304
|
+
const rightText = this.visit(binExpr.right, context);
|
|
305
|
+
let target = leftText;
|
|
306
|
+
if (ts.isIdentifier(binExpr.left)) {
|
|
307
|
+
const scope = this.getScopeForNode(binExpr.left);
|
|
308
|
+
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.left.text, scope);
|
|
309
|
+
target = typeInfo.needsHeapAllocation
|
|
310
|
+
? `*${leftText}`
|
|
311
|
+
: leftText;
|
|
312
|
+
if (opToken.kind === ts.SyntaxKind.AmpersandAmpersandEqualsToken) {
|
|
313
|
+
return `jspp::logical_and_assign(${target}, ${rightText})`;
|
|
314
|
+
}
|
|
315
|
+
else if (opToken.kind === ts.SyntaxKind.BarBarEqualsToken) {
|
|
316
|
+
return `jspp::logical_or_assign(${target}, ${rightText})`;
|
|
317
|
+
}
|
|
318
|
+
else {
|
|
319
|
+
return `jspp::nullish_coalesce_assign(${target}, ${rightText})`;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
134
323
|
const leftText = this.visit(binExpr.left, context);
|
|
135
|
-
|
|
136
|
-
|
|
324
|
+
let rightText = ts.isNumericLiteral(binExpr.right)
|
|
325
|
+
? binExpr.right.getText()
|
|
326
|
+
: this.visit(binExpr.right, context);
|
|
327
|
+
if (ts.isIdentifier(binExpr.right)) {
|
|
328
|
+
const scope = this.getScopeForNode(binExpr.right);
|
|
329
|
+
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.right.getText(), scope);
|
|
330
|
+
rightText = this.getDerefCode(rightText, this.getJsVarName(binExpr.right), context, typeInfo);
|
|
331
|
+
}
|
|
332
|
+
let target = leftText;
|
|
333
|
+
if (ts.isIdentifier(binExpr.left)) {
|
|
334
|
+
const scope = this.getScopeForNode(binExpr.left);
|
|
335
|
+
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.left.getText(), scope);
|
|
336
|
+
if (context.derefBeforeAssignment) {
|
|
337
|
+
target = this.getDerefCode(leftText, leftText, context, typeInfo);
|
|
338
|
+
}
|
|
339
|
+
else if (typeInfo.needsHeapAllocation) {
|
|
340
|
+
target = `*${leftText}`;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return `${target} ${op} ${rightText}`;
|
|
137
344
|
}
|
|
138
345
|
if (opToken.kind === ts.SyntaxKind.EqualsToken) {
|
|
139
|
-
|
|
346
|
+
let rightText = this.visit(binExpr.right, context);
|
|
140
347
|
if (ts.isPropertyAccessExpression(binExpr.left)) {
|
|
141
348
|
const propAccess = binExpr.left;
|
|
349
|
+
if (propAccess.expression.kind === ts.SyntaxKind.SuperKeyword) {
|
|
350
|
+
const propName = propAccess.name.getText();
|
|
351
|
+
let finalRightText = rightText;
|
|
352
|
+
// Deref right text logic...
|
|
353
|
+
if (ts.isIdentifier(binExpr.right)) {
|
|
354
|
+
const rightScope = this.getScopeForNode(binExpr.right);
|
|
355
|
+
const rightTypeInfo = this.typeAnalyzer.scopeManager
|
|
356
|
+
.lookupFromScope(binExpr.right.getText(), rightScope);
|
|
357
|
+
if (rightTypeInfo &&
|
|
358
|
+
!rightTypeInfo.isParameter &&
|
|
359
|
+
!rightTypeInfo.isBuiltin) {
|
|
360
|
+
finalRightText = this.getDerefCode(rightText, this.getJsVarName(binExpr.right), context, rightTypeInfo);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
// Approximate super assignment as setting property on 'this'
|
|
364
|
+
return `(${this.globalThisVar}).set_own_property("${propName}", ${finalRightText})`;
|
|
365
|
+
}
|
|
142
366
|
const objExprText = this.visit(propAccess.expression, context);
|
|
143
367
|
const propName = propAccess.name.getText();
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
368
|
+
let finalObjExpr = objExprText;
|
|
369
|
+
if (ts.isIdentifier(propAccess.expression)) {
|
|
370
|
+
const scope = this.getScopeForNode(propAccess.expression);
|
|
371
|
+
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(propAccess.expression.getText(), scope);
|
|
372
|
+
if (typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin) {
|
|
373
|
+
finalObjExpr = this.getDerefCode(objExprText, this.getJsVarName(propAccess.expression), context, typeInfo);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
151
376
|
let finalRightText = rightText;
|
|
152
377
|
if (ts.isIdentifier(binExpr.right)) {
|
|
153
378
|
const rightScope = this.getScopeForNode(binExpr.right);
|
|
@@ -156,23 +381,23 @@ export function visitBinaryExpression(node, context) {
|
|
|
156
381
|
if (rightTypeInfo &&
|
|
157
382
|
!rightTypeInfo.isParameter &&
|
|
158
383
|
!rightTypeInfo.isBuiltin) {
|
|
159
|
-
finalRightText =
|
|
384
|
+
finalRightText = this.getDerefCode(rightText, this.getJsVarName(binExpr.right), context, rightTypeInfo);
|
|
160
385
|
}
|
|
161
386
|
}
|
|
162
387
|
return `${finalObjExpr}.set_own_property("${propName}", ${finalRightText})`;
|
|
163
|
-
// return `${finalObjExpr}["${propName}"] = ${finalRightText}`;
|
|
164
388
|
}
|
|
165
389
|
else if (ts.isElementAccessExpression(binExpr.left)) {
|
|
166
390
|
const elemAccess = binExpr.left;
|
|
167
391
|
const objExprText = this.visit(elemAccess.expression, context);
|
|
168
|
-
let argText = visitObjectPropertyName.call(this, elemAccess.argumentExpression, { ...context,
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
392
|
+
let argText = visitObjectPropertyName.call(this, elemAccess.argumentExpression, { ...context, isBracketNotationPropertyAccess: true });
|
|
393
|
+
let finalObjExpr = objExprText;
|
|
394
|
+
if (ts.isIdentifier(elemAccess.expression)) {
|
|
395
|
+
const scope = this.getScopeForNode(elemAccess.expression);
|
|
396
|
+
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(elemAccess.expression.getText(), scope);
|
|
397
|
+
if (typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin) {
|
|
398
|
+
finalObjExpr = this.getDerefCode(objExprText, this.getJsVarName(elemAccess.expression), context, typeInfo);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
176
401
|
if (ts.isIdentifier(elemAccess.argumentExpression)) {
|
|
177
402
|
const argScope = this.getScopeForNode(elemAccess.argumentExpression);
|
|
178
403
|
const argTypeInfo = this.typeAnalyzer.scopeManager
|
|
@@ -180,7 +405,7 @@ export function visitBinaryExpression(node, context) {
|
|
|
180
405
|
if (argTypeInfo &&
|
|
181
406
|
!argTypeInfo.isParameter &&
|
|
182
407
|
!argTypeInfo.isBuiltin) {
|
|
183
|
-
argText =
|
|
408
|
+
argText = this.getDerefCode(argText, this.getJsVarName(elemAccess.argumentExpression), context, argTypeInfo);
|
|
184
409
|
}
|
|
185
410
|
}
|
|
186
411
|
let finalRightText = rightText;
|
|
@@ -191,65 +416,97 @@ export function visitBinaryExpression(node, context) {
|
|
|
191
416
|
if (rightTypeInfo &&
|
|
192
417
|
!rightTypeInfo.isParameter &&
|
|
193
418
|
!rightTypeInfo.isBuiltin) {
|
|
194
|
-
finalRightText =
|
|
419
|
+
finalRightText = this.getDerefCode(rightText, this.getJsVarName(binExpr.right), context, rightTypeInfo);
|
|
195
420
|
}
|
|
196
421
|
}
|
|
197
422
|
return `${finalObjExpr}.set_own_property(${argText}, ${finalRightText})`;
|
|
198
|
-
// return `${finalObjExpr}[${argText}] = ${finalRightText}`;
|
|
199
423
|
}
|
|
200
424
|
const leftText = this.visit(binExpr.left, context);
|
|
201
425
|
const scope = this.getScopeForNode(binExpr.left);
|
|
202
426
|
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.left.text, scope);
|
|
203
427
|
if (!typeInfo && !this.isBuiltinObject(binExpr.left)) {
|
|
204
|
-
return `jspp::
|
|
428
|
+
return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(binExpr.left)})`;
|
|
205
429
|
}
|
|
206
430
|
if (typeInfo?.isConst) {
|
|
207
|
-
return `jspp::
|
|
431
|
+
return `jspp::Exception::throw_immutable_assignment()`;
|
|
432
|
+
}
|
|
433
|
+
if (ts.isNumericLiteral(binExpr.right)) {
|
|
434
|
+
rightText = binExpr.right.getText();
|
|
208
435
|
}
|
|
209
|
-
|
|
436
|
+
const target = context.derefBeforeAssignment
|
|
437
|
+
? this.getDerefCode(leftText, leftText, context, typeInfo)
|
|
438
|
+
: (typeInfo.needsHeapAllocation ? `*${leftText}` : leftText);
|
|
439
|
+
return `${target} ${op} ${rightText}`;
|
|
210
440
|
}
|
|
211
441
|
const leftText = this.visit(binExpr.left, context);
|
|
212
442
|
const rightText = this.visit(binExpr.right, context);
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
443
|
+
let finalLeft = leftText;
|
|
444
|
+
if (ts.isIdentifier(binExpr.left)) {
|
|
445
|
+
const scope = this.getScopeForNode(binExpr.left);
|
|
446
|
+
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.left.getText(), scope);
|
|
447
|
+
if (!typeInfo && !this.isBuiltinObject(binExpr.left)) {
|
|
448
|
+
return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(binExpr.left)})`;
|
|
449
|
+
}
|
|
450
|
+
if (typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin) {
|
|
451
|
+
finalLeft = this.getDerefCode(leftText, this.getJsVarName(binExpr.left), context, typeInfo);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
let finalRight = rightText;
|
|
455
|
+
if (ts.isIdentifier(binExpr.right)) {
|
|
456
|
+
const scope = this.getScopeForNode(binExpr.right);
|
|
457
|
+
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.right.getText(), scope);
|
|
458
|
+
if (!typeInfo &&
|
|
459
|
+
!this.isBuiltinObject(binExpr.right)) {
|
|
460
|
+
return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(binExpr.right)})`;
|
|
461
|
+
}
|
|
462
|
+
if (typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin) {
|
|
463
|
+
finalRight = this.getDerefCode(rightText, this.getJsVarName(binExpr.right), context, typeInfo);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
if (opToken.kind === ts.SyntaxKind.InKeyword) {
|
|
467
|
+
return `jspp::Access::in(${finalLeft}, ${finalRight})`;
|
|
468
|
+
}
|
|
469
|
+
if (opToken.kind === ts.SyntaxKind.InstanceOfKeyword) {
|
|
470
|
+
return `jspp::Access::instance_of(${finalLeft}, ${finalRight})`;
|
|
471
|
+
}
|
|
472
|
+
if (opToken.kind === ts.SyntaxKind.AmpersandAmpersandToken) {
|
|
473
|
+
return `jspp::logical_and(${finalLeft}, ${finalRight})`;
|
|
474
|
+
}
|
|
475
|
+
if (opToken.kind === ts.SyntaxKind.BarBarToken) {
|
|
476
|
+
return `jspp::logical_or(${finalLeft}, ${finalRight})`;
|
|
477
|
+
}
|
|
478
|
+
if (opToken.kind === ts.SyntaxKind.QuestionQuestionToken) {
|
|
479
|
+
return `jspp::nullish_coalesce(${finalLeft}, ${finalRight})`;
|
|
480
|
+
}
|
|
481
|
+
// Optimizations to prevent calling make_number multiple times
|
|
482
|
+
if (ts.isNumericLiteral(binExpr.left)) {
|
|
483
|
+
finalLeft = binExpr.left.getText();
|
|
484
|
+
}
|
|
485
|
+
if (ts.isNumericLiteral(binExpr.right)) {
|
|
486
|
+
finalRight = binExpr.right.getText();
|
|
237
487
|
}
|
|
238
488
|
if (opToken.kind === ts.SyntaxKind.EqualsEqualsEqualsToken) {
|
|
239
|
-
return
|
|
489
|
+
return `jspp::is_strictly_equal_to(${finalLeft}, ${finalRight})`;
|
|
240
490
|
}
|
|
241
491
|
if (opToken.kind === ts.SyntaxKind.EqualsEqualsToken) {
|
|
242
|
-
return
|
|
492
|
+
return `jspp::is_equal_to(${finalLeft}, ${finalRight})`;
|
|
243
493
|
}
|
|
244
494
|
if (opToken.kind === ts.SyntaxKind.ExclamationEqualsEqualsToken) {
|
|
245
|
-
return
|
|
495
|
+
return `jspp::not_strictly_equal_to(${finalLeft}, ${finalRight})`;
|
|
246
496
|
}
|
|
247
497
|
if (opToken.kind === ts.SyntaxKind.ExclamationEqualsToken) {
|
|
248
|
-
return
|
|
498
|
+
return `jspp::not_equal_to(${finalLeft}, ${finalRight})`;
|
|
249
499
|
}
|
|
250
500
|
if (opToken.kind === ts.SyntaxKind.AsteriskAsteriskToken) {
|
|
251
501
|
return `jspp::pow(${finalLeft}, ${finalRight})`;
|
|
252
502
|
}
|
|
503
|
+
if (opToken.kind === ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken) {
|
|
504
|
+
return `jspp::unsigned_right_shift(${finalLeft}, ${finalRight})`;
|
|
505
|
+
}
|
|
506
|
+
// Optimizations to prevent calling make_number multiple times
|
|
507
|
+
if (ts.isNumericLiteral(binExpr.left) && ts.isNumericLiteral(binExpr.right)) {
|
|
508
|
+
return `jspp::AnyValue::make_number(${finalLeft} ${op} ${finalRight})`;
|
|
509
|
+
}
|
|
253
510
|
if (op === "+" ||
|
|
254
511
|
op === "-" ||
|
|
255
512
|
op === "*" ||
|
|
@@ -257,56 +514,159 @@ export function visitBinaryExpression(node, context) {
|
|
|
257
514
|
op === "%" ||
|
|
258
515
|
op === "^" ||
|
|
259
516
|
op === "&" ||
|
|
260
|
-
op === "|"
|
|
517
|
+
op === "|" ||
|
|
518
|
+
op === "<<" ||
|
|
519
|
+
op === ">>") {
|
|
261
520
|
return `(${finalLeft} ${op} ${finalRight})`;
|
|
262
521
|
}
|
|
263
522
|
return `${finalLeft} ${op} ${finalRight}`;
|
|
264
523
|
}
|
|
524
|
+
export function visitConditionalExpression(node, context) {
|
|
525
|
+
const condExpr = node;
|
|
526
|
+
const condition = this.visit(condExpr.condition, context);
|
|
527
|
+
const whenTrueStmt = this.visit(condExpr.whenTrue, {
|
|
528
|
+
...context,
|
|
529
|
+
isFunctionBody: false,
|
|
530
|
+
});
|
|
531
|
+
const whenFalseStmt = this.visit(condExpr.whenFalse, {
|
|
532
|
+
...context,
|
|
533
|
+
isFunctionBody: false,
|
|
534
|
+
});
|
|
535
|
+
return `is_truthy(${condition}) ? ${whenTrueStmt} : ${whenFalseStmt}`;
|
|
536
|
+
}
|
|
265
537
|
export function visitCallExpression(node, context) {
|
|
266
538
|
const callExpr = node;
|
|
267
539
|
const callee = callExpr.expression;
|
|
268
|
-
|
|
269
|
-
|
|
540
|
+
if (callee.kind === ts.SyntaxKind.SuperKeyword) {
|
|
541
|
+
if (!context.superClassVar) {
|
|
542
|
+
throw new Error("super() called but no super class variable found in context");
|
|
543
|
+
}
|
|
544
|
+
const args = callExpr.arguments.map((arg) => this.visit(arg, context))
|
|
545
|
+
.join(", ");
|
|
546
|
+
return `(${context.superClassVar}).call(${this.globalThisVar}, (const jspp::AnyValue[]){${args}}, "super")`;
|
|
547
|
+
}
|
|
548
|
+
const argsArray = callExpr.arguments
|
|
270
549
|
.map((arg) => {
|
|
271
550
|
const argText = this.visit(arg, context);
|
|
272
551
|
if (ts.isIdentifier(arg)) {
|
|
273
552
|
const scope = this.getScopeForNode(arg);
|
|
274
553
|
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(arg.text, scope);
|
|
275
554
|
if (!typeInfo) {
|
|
276
|
-
return `jspp::
|
|
555
|
+
return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(arg)})`;
|
|
277
556
|
}
|
|
278
|
-
if (typeInfo && !typeInfo.
|
|
279
|
-
return
|
|
557
|
+
if (typeInfo && !typeInfo.isBuiltin) {
|
|
558
|
+
return this.getDerefCode(argText, this.getJsVarName(arg), context, typeInfo);
|
|
280
559
|
}
|
|
281
560
|
}
|
|
282
561
|
return argText;
|
|
283
|
-
})
|
|
284
|
-
|
|
562
|
+
});
|
|
563
|
+
const args = argsArray.join(", ");
|
|
564
|
+
const argsCount = argsArray.length;
|
|
565
|
+
const argsSpan = argsCount > 0
|
|
566
|
+
? `std::span<const jspp::AnyValue>((const jspp::AnyValue[]){${args}}, ${argsCount})`
|
|
567
|
+
: "std::span<const jspp::AnyValue>{}";
|
|
568
|
+
// Handle obj.method() -> pass obj as 'this'
|
|
569
|
+
if (ts.isPropertyAccessExpression(callee)) {
|
|
570
|
+
const propAccess = callee;
|
|
571
|
+
if (propAccess.expression.kind === ts.SyntaxKind.SuperKeyword) {
|
|
572
|
+
if (!context.superClassVar) {
|
|
573
|
+
throw new Error("super.method() called but no super class variable found in context");
|
|
574
|
+
}
|
|
575
|
+
const propName = propAccess.name.getText();
|
|
576
|
+
return `(${context.superClassVar}).get_own_property("prototype").get_own_property("${propName}").call(${this.globalThisVar}, ${argsSpan}, "${this.escapeString(propName)}")`;
|
|
577
|
+
}
|
|
578
|
+
const objExpr = propAccess.expression;
|
|
579
|
+
const propName = propAccess.name.getText();
|
|
580
|
+
const objCode = this.visit(objExpr, context);
|
|
581
|
+
// We need to dereference the object expression if it's a variable
|
|
582
|
+
let derefObj = objCode;
|
|
583
|
+
if (ts.isIdentifier(objExpr)) {
|
|
584
|
+
const scope = this.getScopeForNode(objExpr);
|
|
585
|
+
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(objExpr.getText(), scope);
|
|
586
|
+
if (!typeInfo && !this.isBuiltinObject(objExpr)) {
|
|
587
|
+
return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(objExpr)})`;
|
|
588
|
+
}
|
|
589
|
+
if (typeInfo && !typeInfo.isBuiltin && !typeInfo.isParameter) {
|
|
590
|
+
derefObj = this.getDerefCode(objCode, this.getJsVarName(objExpr), context, typeInfo);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
if (callExpr.questionDotToken) {
|
|
594
|
+
return `jspp::Access::optional_call(${derefObj}.get_own_property("${propName}"), ${derefObj}, ${argsSpan}, "${this.escapeString(propName)}")`;
|
|
595
|
+
}
|
|
596
|
+
return `([&](){ auto __obj = ${derefObj}; return __obj.get_own_property("${propName}").call(__obj, ${argsSpan}, "${this.escapeString(propName)}"); })()`;
|
|
597
|
+
}
|
|
598
|
+
// Handle obj[method]() -> pass obj as 'this'
|
|
599
|
+
if (ts.isElementAccessExpression(callee)) {
|
|
600
|
+
const elemAccess = callee;
|
|
601
|
+
const objExpr = elemAccess.expression;
|
|
602
|
+
const objCode = this.visit(objExpr, context);
|
|
603
|
+
let derefObj = objCode;
|
|
604
|
+
if (ts.isIdentifier(objExpr)) {
|
|
605
|
+
const scope = this.getScopeForNode(objExpr);
|
|
606
|
+
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(objExpr.getText(), scope);
|
|
607
|
+
if (!typeInfo && !this.isBuiltinObject(objExpr)) {
|
|
608
|
+
return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(objExpr)})`;
|
|
609
|
+
}
|
|
610
|
+
if (typeInfo && !typeInfo.isBuiltin && !typeInfo.isParameter) {
|
|
611
|
+
derefObj = this.getDerefCode(objCode, this.getJsVarName(objExpr), context, typeInfo);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
let argText = visitObjectPropertyName.call(this, elemAccess.argumentExpression, { ...context, isBracketNotationPropertyAccess: true });
|
|
615
|
+
// Dereference argument if needed (logic copied from visitElementAccessExpression)
|
|
616
|
+
if (ts.isIdentifier(elemAccess.argumentExpression)) {
|
|
617
|
+
const argScope = this.getScopeForNode(elemAccess.argumentExpression);
|
|
618
|
+
const argTypeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(elemAccess.argumentExpression.getText(), argScope);
|
|
619
|
+
if (!argTypeInfo &&
|
|
620
|
+
!this.isBuiltinObject(elemAccess.argumentExpression)) {
|
|
621
|
+
return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(elemAccess.argumentExpression)})`;
|
|
622
|
+
}
|
|
623
|
+
if (argTypeInfo && !argTypeInfo.isParameter &&
|
|
624
|
+
!argTypeInfo.isBuiltin) {
|
|
625
|
+
argText = this.getDerefCode(argText, this.getJsVarName(elemAccess.argumentExpression), context, argTypeInfo);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
if (callExpr.questionDotToken) {
|
|
629
|
+
return `jspp::Access::optional_call(${derefObj}.get_own_property(${argText}), ${derefObj}, ${argsSpan})`;
|
|
630
|
+
}
|
|
631
|
+
return `([&](){ auto __obj = ${derefObj}; return __obj.get_own_property(${argText}).call(__obj, ${argsSpan}); })()`;
|
|
632
|
+
}
|
|
285
633
|
const calleeCode = this.visit(callee, context);
|
|
286
|
-
let derefCallee;
|
|
634
|
+
let derefCallee = calleeCode;
|
|
287
635
|
if (ts.isIdentifier(callee)) {
|
|
288
636
|
const scope = this.getScopeForNode(callee);
|
|
289
637
|
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(callee.text, scope);
|
|
290
638
|
if (!typeInfo && !this.isBuiltinObject(callee)) {
|
|
291
|
-
return `jspp::
|
|
639
|
+
return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(callee)})`;
|
|
292
640
|
}
|
|
293
641
|
if (typeInfo?.isBuiltin) {
|
|
294
642
|
derefCallee = calleeCode;
|
|
295
643
|
}
|
|
296
|
-
else {
|
|
297
|
-
derefCallee =
|
|
644
|
+
else if (typeInfo) {
|
|
645
|
+
derefCallee = this.getDerefCode(calleeCode, this.getJsVarName(callee), context, typeInfo);
|
|
298
646
|
}
|
|
299
647
|
}
|
|
300
|
-
|
|
301
|
-
|
|
648
|
+
let calleeName = "";
|
|
649
|
+
if (ts.isIdentifier(callee) || ts.isPropertyAccessExpression(callee)) {
|
|
650
|
+
calleeName = this.escapeString(callee.getText());
|
|
651
|
+
}
|
|
652
|
+
else if (ts.isParenthesizedExpression(callee) &&
|
|
653
|
+
ts.isFunctionExpression(callee.expression)) {
|
|
654
|
+
const funcExpr = callee.expression;
|
|
655
|
+
calleeName = this.escapeString(funcExpr.name?.getText() || "");
|
|
656
|
+
}
|
|
657
|
+
// Pass undefined as 'this' for normal function calls
|
|
658
|
+
const calleeNamePart = calleeName && calleeName.length > 0
|
|
659
|
+
? `, "${calleeName}"`
|
|
660
|
+
: "";
|
|
661
|
+
if (callExpr.questionDotToken) {
|
|
662
|
+
return `jspp::Access::optional_call(${derefCallee}, jspp::Constants::UNDEFINED, ${argsSpan}${calleeNamePart})`;
|
|
302
663
|
}
|
|
303
|
-
return `${derefCallee}.
|
|
304
|
-
// return `${derefCallee}.as_function("${calleeName}")->call({${args}})`;
|
|
664
|
+
return `${derefCallee}.call(jspp::Constants::UNDEFINED, ${argsSpan}${calleeNamePart})`;
|
|
305
665
|
}
|
|
306
666
|
export function visitVoidExpression(node, context) {
|
|
307
667
|
const voidExpr = node;
|
|
308
668
|
const exprText = this.visit(voidExpr.expression, context);
|
|
309
|
-
return `(${exprText}, jspp::
|
|
669
|
+
return `(${exprText}, jspp::Constants::UNDEFINED)`;
|
|
310
670
|
}
|
|
311
671
|
export function visitTemplateExpression(node, context) {
|
|
312
672
|
const templateExpr = node;
|
|
@@ -319,13 +679,12 @@ export function visitTemplateExpression(node, context) {
|
|
|
319
679
|
const scope = this.getScopeForNode(expr);
|
|
320
680
|
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(expr.text, scope);
|
|
321
681
|
if (!typeInfo && !this.isBuiltinObject(expr)) {
|
|
322
|
-
finalExpr =
|
|
323
|
-
`jspp::RuntimeError::throw_unresolved_reference_error(${this.getJsVarName(expr)})`;
|
|
682
|
+
finalExpr = `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(expr)})`;
|
|
324
683
|
}
|
|
325
684
|
else if (typeInfo &&
|
|
326
685
|
!typeInfo.isParameter &&
|
|
327
686
|
!typeInfo.isBuiltin) {
|
|
328
|
-
finalExpr =
|
|
687
|
+
finalExpr = this.getDerefCode(exprText, this.getJsVarName(expr), context, typeInfo);
|
|
329
688
|
}
|
|
330
689
|
}
|
|
331
690
|
result += ` + (${finalExpr})`;
|
|
@@ -335,3 +694,98 @@ export function visitTemplateExpression(node, context) {
|
|
|
335
694
|
}
|
|
336
695
|
return result;
|
|
337
696
|
}
|
|
697
|
+
export function visitNewExpression(node, context) {
|
|
698
|
+
const newExpr = node;
|
|
699
|
+
const exprText = this.visit(newExpr.expression, context);
|
|
700
|
+
let derefExpr = exprText;
|
|
701
|
+
let name = `"${exprText}"`;
|
|
702
|
+
if (ts.isIdentifier(newExpr.expression)) {
|
|
703
|
+
name = this.getJsVarName(newExpr.expression);
|
|
704
|
+
const scope = this.getScopeForNode(newExpr.expression);
|
|
705
|
+
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(newExpr.expression.getText(), scope);
|
|
706
|
+
if (!typeInfo &&
|
|
707
|
+
!this.isBuiltinObject(newExpr.expression)) {
|
|
708
|
+
return `jspp::Exception::throw_unresolved_reference(${name})`;
|
|
709
|
+
}
|
|
710
|
+
if (typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin) {
|
|
711
|
+
derefExpr = this.getDerefCode(exprText, name, context, typeInfo);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
const argsArray = newExpr.arguments
|
|
715
|
+
? newExpr.arguments
|
|
716
|
+
.map((arg) => {
|
|
717
|
+
const argText = this.visit(arg, context);
|
|
718
|
+
if (ts.isIdentifier(arg)) {
|
|
719
|
+
const scope = this.getScopeForNode(arg);
|
|
720
|
+
const typeInfo = this.typeAnalyzer.scopeManager
|
|
721
|
+
.lookupFromScope(arg.text, scope);
|
|
722
|
+
if (!typeInfo) {
|
|
723
|
+
return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(arg)})`;
|
|
724
|
+
}
|
|
725
|
+
if (typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin) {
|
|
726
|
+
return this.getDerefCode(argText, this.getJsVarName(arg), context, typeInfo);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
return argText;
|
|
730
|
+
})
|
|
731
|
+
: [];
|
|
732
|
+
const args = argsArray.join(", ");
|
|
733
|
+
const argsCount = argsArray.length;
|
|
734
|
+
const argsSpan = argsCount > 0
|
|
735
|
+
? `std::span<const jspp::AnyValue>((const jspp::AnyValue[]){${args}}, ${argsCount})`
|
|
736
|
+
: "std::span<const jspp::AnyValue>{}";
|
|
737
|
+
return `${derefExpr}.construct(${argsSpan}, ${name})`;
|
|
738
|
+
}
|
|
739
|
+
export function visitTypeOfExpression(node, context) {
|
|
740
|
+
const typeOfExpr = node;
|
|
741
|
+
const exprText = this.visit(typeOfExpr.expression, context);
|
|
742
|
+
let derefExpr = exprText;
|
|
743
|
+
if (ts.isIdentifier(typeOfExpr.expression)) {
|
|
744
|
+
const scope = this.getScopeForNode(typeOfExpr.expression);
|
|
745
|
+
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(typeOfExpr.expression.getText(), scope);
|
|
746
|
+
if (!typeInfo &&
|
|
747
|
+
!this.isBuiltinObject(typeOfExpr.expression)) {
|
|
748
|
+
derefExpr = `/* undeclared variable: ${this.getJsVarName(typeOfExpr.expression)} */`; // typeof undeclared variable is 'undefined'
|
|
749
|
+
}
|
|
750
|
+
if (typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin) {
|
|
751
|
+
derefExpr = this.getDerefCode(exprText, this.getJsVarName(typeOfExpr.expression), context, typeInfo);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
return `jspp::Access::type_of(${derefExpr})`;
|
|
755
|
+
}
|
|
756
|
+
export function visitAwaitExpression(node, context) {
|
|
757
|
+
const awaitExpr = node;
|
|
758
|
+
const exprText = this.visit(awaitExpr.expression, context);
|
|
759
|
+
let derefExpr = exprText;
|
|
760
|
+
if (ts.isIdentifier(awaitExpr.expression)) {
|
|
761
|
+
const scope = this.getScopeForNode(awaitExpr.expression);
|
|
762
|
+
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(awaitExpr.expression.getText(), scope);
|
|
763
|
+
if (!typeInfo &&
|
|
764
|
+
!this.isBuiltinObject(awaitExpr.expression)) {
|
|
765
|
+
// This assumes co_awaiting the result of throw_unresolved_reference (which throws)
|
|
766
|
+
// But throw_unresolved_reference returns AnyValue (void-ish).
|
|
767
|
+
// We can just throw before co_await.
|
|
768
|
+
// But we need to return a string expression.
|
|
769
|
+
// Using comma operator: (throw..., AnyValue())
|
|
770
|
+
return `(jspp::Exception::throw_unresolved_reference(${this.getJsVarName(awaitExpr.expression)}), co_await jspp::Constants::UNDEFINED)`;
|
|
771
|
+
}
|
|
772
|
+
if (typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin) {
|
|
773
|
+
derefExpr = this.getDerefCode(exprText, this.getJsVarName(awaitExpr.expression), context, typeInfo);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
return `co_await ${derefExpr}`;
|
|
777
|
+
}
|
|
778
|
+
export function visitDeleteExpression(node, context) {
|
|
779
|
+
const expr = node.expression;
|
|
780
|
+
if (ts.isPropertyAccessExpression(expr)) {
|
|
781
|
+
const obj = this.visit(expr.expression, context);
|
|
782
|
+
const prop = `jspp::AnyValue::make_string("${expr.name.getText()}")`;
|
|
783
|
+
return `jspp::Access::delete_property(${obj}, ${prop})`;
|
|
784
|
+
}
|
|
785
|
+
else if (ts.isElementAccessExpression(expr)) {
|
|
786
|
+
const obj = this.visit(expr.expression, context);
|
|
787
|
+
const prop = this.visit(expr.argumentExpression, context);
|
|
788
|
+
return `jspp::Access::delete_property(${obj}, ${prop})`;
|
|
789
|
+
}
|
|
790
|
+
return "jspp::Constants::TRUE"; // delete on non-property is true in JS
|
|
791
|
+
}
|