@ugo-studio/jspp 0.1.4 → 0.1.6
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/scope.js +17 -0
- package/dist/analysis/typeAnalyzer.js +7 -1
- package/dist/ast/symbols.js +32 -0
- package/dist/ast/types.js +0 -6
- 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 -30
- package/dist/core/codegen/class-handlers.js +10 -6
- package/dist/core/codegen/control-flow-handlers.js +57 -28
- package/dist/core/codegen/declaration-handlers.js +10 -6
- package/dist/core/codegen/expression-handlers.js +206 -61
- package/dist/core/codegen/function-handlers.js +203 -76
- package/dist/core/codegen/helpers.js +125 -28
- package/dist/core/codegen/index.js +23 -15
- package/dist/core/codegen/literal-handlers.js +15 -6
- package/dist/core/codegen/statement-handlers.js +282 -84
- package/dist/core/codegen/visitor.js +3 -1
- package/package.json +1 -1
- package/src/prelude/any_value.hpp +221 -342
- package/src/prelude/any_value_access.hpp +168 -81
- package/src/prelude/any_value_defines.hpp +74 -35
- package/src/prelude/any_value_helpers.hpp +75 -180
- package/src/prelude/exception.hpp +1 -0
- package/src/prelude/exception_helpers.hpp +4 -4
- package/src/prelude/index.hpp +12 -2
- package/src/prelude/library/array.hpp +190 -0
- package/src/prelude/library/console.hpp +6 -5
- package/src/prelude/library/error.hpp +10 -8
- package/src/prelude/library/function.hpp +10 -0
- package/src/prelude/library/global.hpp +20 -0
- package/src/prelude/library/math.hpp +308 -0
- package/src/prelude/library/object.hpp +288 -0
- package/src/prelude/library/performance.hpp +1 -1
- package/src/prelude/library/process.hpp +39 -0
- package/src/prelude/library/promise.hpp +57 -55
- package/src/prelude/library/symbol.hpp +45 -57
- package/src/prelude/library/timer.hpp +6 -6
- package/src/prelude/types.hpp +54 -0
- package/src/prelude/utils/access.hpp +215 -11
- package/src/prelude/utils/assignment_operators.hpp +99 -0
- package/src/prelude/utils/log_any_value/array.hpp +8 -8
- package/src/prelude/utils/log_any_value/function.hpp +6 -4
- package/src/prelude/utils/log_any_value/object.hpp +41 -24
- package/src/prelude/utils/log_any_value/primitives.hpp +3 -1
- package/src/prelude/utils/operators.hpp +750 -274
- package/src/prelude/utils/well_known_symbols.hpp +12 -0
- package/src/prelude/values/array.hpp +8 -6
- package/src/prelude/values/async_iterator.hpp +79 -0
- package/src/prelude/values/descriptors.hpp +2 -2
- package/src/prelude/values/function.hpp +72 -62
- package/src/prelude/values/helpers/array.hpp +64 -28
- package/src/prelude/values/helpers/async_iterator.hpp +275 -0
- package/src/prelude/values/helpers/function.hpp +81 -92
- package/src/prelude/values/helpers/iterator.hpp +3 -3
- package/src/prelude/values/helpers/object.hpp +54 -9
- package/src/prelude/values/helpers/promise.hpp +13 -6
- package/src/prelude/values/iterator.hpp +1 -1
- package/src/prelude/values/object.hpp +10 -3
- package/src/prelude/values/promise.hpp +7 -11
- package/src/prelude/values/prototypes/array.hpp +851 -12
- package/src/prelude/values/prototypes/async_iterator.hpp +50 -0
- package/src/prelude/values/prototypes/function.hpp +2 -2
- package/src/prelude/values/prototypes/iterator.hpp +5 -5
- package/src/prelude/values/prototypes/number.hpp +153 -0
- package/src/prelude/values/prototypes/object.hpp +2 -2
- package/src/prelude/values/prototypes/promise.hpp +40 -30
- package/src/prelude/values/prototypes/string.hpp +28 -28
- package/src/prelude/values/prototypes/symbol.hpp +20 -3
- package/src/prelude/values/shape.hpp +52 -0
|
@@ -2,14 +2,19 @@ import ts from "typescript";
|
|
|
2
2
|
import { CodeGenerator } from "./";
|
|
3
3
|
export function visitIdentifier(node) {
|
|
4
4
|
if (node.text === "NaN") {
|
|
5
|
-
return "jspp::AnyValue::make_nan()";
|
|
5
|
+
// return "jspp::AnyValue::make_nan()";
|
|
6
|
+
return "jspp::Constants::NaN";
|
|
6
7
|
}
|
|
7
8
|
if (node.text === "undefined") {
|
|
8
|
-
return
|
|
9
|
+
return visitUndefinedKeyword();
|
|
9
10
|
}
|
|
10
11
|
return node.text;
|
|
11
12
|
}
|
|
12
13
|
export function visitNumericLiteral(node) {
|
|
14
|
+
if (node.text === "0")
|
|
15
|
+
return "jspp::Constants::ZERO";
|
|
16
|
+
if (node.text === "1")
|
|
17
|
+
return "jspp::Constants::ONE";
|
|
13
18
|
return `jspp::AnyValue::make_number(${this.escapeString(node.text)})`;
|
|
14
19
|
}
|
|
15
20
|
export function visitStringLiteral(node) {
|
|
@@ -19,16 +24,20 @@ export function visitNoSubstitutionTemplateLiteral(node) {
|
|
|
19
24
|
return `jspp::AnyValue::make_string("${this.escapeString(node.text)}")`;
|
|
20
25
|
}
|
|
21
26
|
export function visitTrueKeyword() {
|
|
22
|
-
return "jspp::AnyValue::make_boolean(true)";
|
|
27
|
+
// return "jspp::AnyValue::make_boolean(true)";
|
|
28
|
+
return "jspp::Constants::TRUE";
|
|
23
29
|
}
|
|
24
30
|
export function visitFalseKeyword() {
|
|
25
|
-
return "jspp::AnyValue::make_boolean(false)";
|
|
31
|
+
// return "jspp::AnyValue::make_boolean(false)";
|
|
32
|
+
return "jspp::Constants::FALSE";
|
|
26
33
|
}
|
|
27
34
|
export function visitUndefinedKeyword() {
|
|
28
|
-
return "jspp::AnyValue::make_undefined()";
|
|
35
|
+
// return "jspp::AnyValue::make_undefined()";
|
|
36
|
+
return "jspp::Constants::UNDEFINED";
|
|
29
37
|
}
|
|
30
38
|
export function visitNullKeyword() {
|
|
31
|
-
return "jspp::AnyValue::make_null()";
|
|
39
|
+
// return "jspp::AnyValue::make_null()";
|
|
40
|
+
return "jspp::Constants::Null";
|
|
32
41
|
}
|
|
33
42
|
export function visitThisKeyword() {
|
|
34
43
|
return `${this.globalThisVar}`;
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import ts from "typescript";
|
|
2
|
+
import { DeclaredSymbols } from "../../ast/symbols";
|
|
2
3
|
import { CodeGenerator } from "./";
|
|
4
|
+
import { collectBlockScopedDeclarations, collectFunctionScopedDeclarations, } from "./helpers";
|
|
3
5
|
export function visitSourceFile(node, context) {
|
|
4
6
|
const sourceFile = node;
|
|
5
7
|
let code = "";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
// 1. Collect all var declarations (recursively) + top-level let/const
|
|
9
|
+
const varDecls = collectFunctionScopedDeclarations(sourceFile);
|
|
10
|
+
const topLevelLetConst = collectBlockScopedDeclarations(sourceFile.statements);
|
|
9
11
|
const funcDecls = sourceFile.statements.filter(ts.isFunctionDeclaration);
|
|
10
12
|
const classDecls = sourceFile.statements.filter(ts.isClassDeclaration);
|
|
11
|
-
const hoistedSymbols = new
|
|
12
|
-
//
|
|
13
|
+
const hoistedSymbols = new DeclaredSymbols();
|
|
14
|
+
// Hoist function declarations
|
|
13
15
|
funcDecls.forEach((func) => {
|
|
14
16
|
code += this.hoistDeclaration(func, hoistedSymbols);
|
|
15
17
|
});
|
|
@@ -17,19 +19,27 @@ export function visitSourceFile(node, context) {
|
|
|
17
19
|
classDecls.forEach((cls) => {
|
|
18
20
|
code += this.hoistDeclaration(cls, hoistedSymbols);
|
|
19
21
|
});
|
|
20
|
-
// Hoist variable declarations
|
|
22
|
+
// Hoist variable declarations (var)
|
|
21
23
|
varDecls.forEach((decl) => {
|
|
22
24
|
code += this.hoistDeclaration(decl, hoistedSymbols);
|
|
23
25
|
});
|
|
26
|
+
// Hoist top-level let/const
|
|
27
|
+
topLevelLetConst.forEach((decl) => {
|
|
28
|
+
code += this.hoistDeclaration(decl, hoistedSymbols);
|
|
29
|
+
});
|
|
30
|
+
// Compile symbols for other statements (excluding function)
|
|
31
|
+
const topLevelScopeSymbols = this.prepareScopeSymbolsForVisit(context.topLevelScopeSymbols, context.localScopeSymbols);
|
|
32
|
+
const localScopeSymbols = new DeclaredSymbols(hoistedSymbols); // hoistedSymbols becomes new local
|
|
24
33
|
// 2. Assign all hoisted functions first
|
|
25
34
|
const contextForFunctions = {
|
|
26
35
|
...context,
|
|
27
|
-
|
|
36
|
+
localScopeSymbols: new DeclaredSymbols(context.localScopeSymbols, hoistedSymbols),
|
|
28
37
|
};
|
|
29
|
-
hoistedSymbols.forEach((v, k) => contextForFunctions.currentScopeSymbols.set(k, v));
|
|
30
38
|
funcDecls.forEach((stmt) => {
|
|
31
39
|
const funcName = stmt.name?.getText();
|
|
32
40
|
if (funcName) {
|
|
41
|
+
this.markSymbolAsChecked(funcName, contextForFunctions.topLevelScopeSymbols, contextForFunctions.localScopeSymbols);
|
|
42
|
+
this.markSymbolAsChecked(funcName, topLevelScopeSymbols, localScopeSymbols);
|
|
33
43
|
const lambda = this.generateLambda(stmt, contextForFunctions, {
|
|
34
44
|
isAssignment: true,
|
|
35
45
|
});
|
|
@@ -38,8 +48,6 @@ export function visitSourceFile(node, context) {
|
|
|
38
48
|
});
|
|
39
49
|
// 3. Process other statements
|
|
40
50
|
sourceFile.statements.forEach((stmt) => {
|
|
41
|
-
const topLevelScopeSymbols = this.prepareScopeSymbolsForVisit(context.topLevelScopeSymbols, context.currentScopeSymbols);
|
|
42
|
-
const currentScopeSymbols = hoistedSymbols; // hoistedSymbols becomes new local
|
|
43
51
|
if (ts.isFunctionDeclaration(stmt)) {
|
|
44
52
|
// Already handled
|
|
45
53
|
}
|
|
@@ -50,7 +58,7 @@ export function visitSourceFile(node, context) {
|
|
|
50
58
|
const contextForVisit = {
|
|
51
59
|
...context,
|
|
52
60
|
topLevelScopeSymbols,
|
|
53
|
-
|
|
61
|
+
localScopeSymbols,
|
|
54
62
|
isAssignmentOnly: !isLetOrConst,
|
|
55
63
|
};
|
|
56
64
|
const assignments = this.visit(stmt.declarationList, contextForVisit);
|
|
@@ -63,24 +71,21 @@ export function visitSourceFile(node, context) {
|
|
|
63
71
|
...context,
|
|
64
72
|
isFunctionBody: false,
|
|
65
73
|
topLevelScopeSymbols,
|
|
66
|
-
|
|
67
|
-
// currentScopeSymbols: undefined, // clear the currentScopeSymbols for nested visit
|
|
68
|
-
// topLevelScopeSymbols: undefined, // clear the topLevelScopeSymbols for nested visit
|
|
74
|
+
localScopeSymbols,
|
|
69
75
|
});
|
|
70
76
|
}
|
|
71
77
|
});
|
|
72
78
|
return code;
|
|
73
79
|
}
|
|
74
80
|
export function visitBlock(node, context) {
|
|
75
|
-
let code =
|
|
81
|
+
let code = `${this.indent()}{\n`;
|
|
76
82
|
this.indentationLevel++;
|
|
77
83
|
const block = node;
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
.flatMap((stmt) => stmt.declarationList.declarations);
|
|
84
|
+
// Collect ONLY block-scoped declarations (let/const)
|
|
85
|
+
const blockScopedDecls = collectBlockScopedDeclarations(block.statements);
|
|
81
86
|
const funcDecls = block.statements.filter(ts.isFunctionDeclaration);
|
|
82
87
|
const classDecls = block.statements.filter(ts.isClassDeclaration);
|
|
83
|
-
const hoistedSymbols = new
|
|
88
|
+
const hoistedSymbols = new DeclaredSymbols();
|
|
84
89
|
// 1. Hoist all function declarations
|
|
85
90
|
funcDecls.forEach((func) => {
|
|
86
91
|
code += this.hoistDeclaration(func, hoistedSymbols);
|
|
@@ -89,19 +94,23 @@ export function visitBlock(node, context) {
|
|
|
89
94
|
classDecls.forEach((cls) => {
|
|
90
95
|
code += this.hoistDeclaration(cls, hoistedSymbols);
|
|
91
96
|
});
|
|
92
|
-
// Hoist variable declarations
|
|
93
|
-
|
|
97
|
+
// Hoist variable declarations (let/const only)
|
|
98
|
+
blockScopedDecls.forEach((decl) => {
|
|
94
99
|
code += this.hoistDeclaration(decl, hoistedSymbols);
|
|
95
100
|
});
|
|
101
|
+
// Compile symbols for other statements (excluding function)
|
|
102
|
+
const topLevelScopeSymbols = this.prepareScopeSymbolsForVisit(context.topLevelScopeSymbols, context.localScopeSymbols);
|
|
103
|
+
const localScopeSymbols = new DeclaredSymbols(hoistedSymbols); // hoistedSymbols becomes new local
|
|
96
104
|
// 2. Assign all hoisted functions first
|
|
97
105
|
const contextForFunctions = {
|
|
98
106
|
...context,
|
|
99
|
-
|
|
107
|
+
localScopeSymbols: new DeclaredSymbols(context.localScopeSymbols, hoistedSymbols),
|
|
100
108
|
};
|
|
101
|
-
hoistedSymbols.forEach((v, k) => contextForFunctions.currentScopeSymbols.set(k, v));
|
|
102
109
|
funcDecls.forEach((stmt) => {
|
|
103
110
|
const funcName = stmt.name?.getText();
|
|
104
111
|
if (funcName) {
|
|
112
|
+
this.markSymbolAsChecked(funcName, contextForFunctions.topLevelScopeSymbols, contextForFunctions.localScopeSymbols);
|
|
113
|
+
this.markSymbolAsChecked(funcName, topLevelScopeSymbols, localScopeSymbols);
|
|
105
114
|
const lambda = this.generateLambda(stmt, contextForFunctions, {
|
|
106
115
|
isAssignment: true,
|
|
107
116
|
});
|
|
@@ -110,8 +119,6 @@ export function visitBlock(node, context) {
|
|
|
110
119
|
});
|
|
111
120
|
// 3. Process other statements
|
|
112
121
|
block.statements.forEach((stmt) => {
|
|
113
|
-
const topLevelScopeSymbols = this.prepareScopeSymbolsForVisit(context.topLevelScopeSymbols, context.currentScopeSymbols);
|
|
114
|
-
const currentScopeSymbols = hoistedSymbols; // hoistedSymbols becomes new local
|
|
115
122
|
if (ts.isFunctionDeclaration(stmt)) {
|
|
116
123
|
// Do nothing, already handled
|
|
117
124
|
}
|
|
@@ -122,7 +129,7 @@ export function visitBlock(node, context) {
|
|
|
122
129
|
const contextForVisit = {
|
|
123
130
|
...context,
|
|
124
131
|
topLevelScopeSymbols,
|
|
125
|
-
|
|
132
|
+
localScopeSymbols,
|
|
126
133
|
isAssignmentOnly: !isLetOrConst,
|
|
127
134
|
};
|
|
128
135
|
const assignments = this.visit(stmt.declarationList, contextForVisit);
|
|
@@ -135,16 +142,14 @@ export function visitBlock(node, context) {
|
|
|
135
142
|
...context,
|
|
136
143
|
isFunctionBody: false,
|
|
137
144
|
topLevelScopeSymbols,
|
|
138
|
-
|
|
139
|
-
// currentScopeSymbols: undefined, // clear the currentScopeSymbols for nested visit
|
|
140
|
-
// topLevelScopeSymbols: undefined, // clear the topLevelScopeSymbols for nested visit
|
|
145
|
+
localScopeSymbols,
|
|
141
146
|
});
|
|
142
147
|
}
|
|
143
148
|
});
|
|
144
149
|
if (context.isFunctionBody) {
|
|
145
150
|
const lastStatement = block.statements[block.statements.length - 1];
|
|
146
151
|
if (!lastStatement || !ts.isReturnStatement(lastStatement)) {
|
|
147
|
-
code += `${this.indent()}${this.getReturnCommand(context)} jspp::
|
|
152
|
+
code += `${this.indent()}${this.getReturnCommand(context)} jspp::Constants::UNDEFINED;\n`;
|
|
148
153
|
}
|
|
149
154
|
}
|
|
150
155
|
this.indentationLevel--;
|
|
@@ -212,7 +217,7 @@ export function visitIfStatement(node, context) {
|
|
|
212
217
|
isFunctionBody: false,
|
|
213
218
|
});
|
|
214
219
|
}
|
|
215
|
-
return `${this.indent()}if ((${condition})
|
|
220
|
+
return `${this.indent()}if (is_truthy(${condition})) ${thenStmt}${elseStmt}`;
|
|
216
221
|
}
|
|
217
222
|
export function visitExpressionStatement(node, context) {
|
|
218
223
|
return (this.indent() +
|
|
@@ -222,35 +227,206 @@ export function visitExpressionStatement(node, context) {
|
|
|
222
227
|
export function visitThrowStatement(node, context) {
|
|
223
228
|
const throwStmt = node;
|
|
224
229
|
const expr = this.visit(throwStmt.expression, context);
|
|
225
|
-
return `${this.indent()}throw jspp::Exception(${expr});
|
|
230
|
+
return `${this.indent()}throw jspp::Exception(${expr});
|
|
226
231
|
`;
|
|
227
232
|
}
|
|
228
233
|
export function visitTryStatement(node, context) {
|
|
229
234
|
const tryStmt = node;
|
|
235
|
+
if (context.isInsideAsyncFunction) {
|
|
236
|
+
if (tryStmt.finallyBlock) {
|
|
237
|
+
const declaredSymbols = new Set(context.topLevelScopeSymbols.toSet());
|
|
238
|
+
this.getDeclaredSymbols(tryStmt.tryBlock).forEach((s) => declaredSymbols.add(s));
|
|
239
|
+
if (tryStmt.catchClause) {
|
|
240
|
+
this.getDeclaredSymbols(tryStmt.catchClause).forEach((s) => declaredSymbols.add(s));
|
|
241
|
+
}
|
|
242
|
+
this.getDeclaredSymbols(tryStmt.finallyBlock).forEach((s) => declaredSymbols.add(s));
|
|
243
|
+
const resultVarName = this.generateUniqueName("__try_result_", declaredSymbols);
|
|
244
|
+
const hasReturnedFlagName = this.generateUniqueName("__try_has_returned_", declaredSymbols);
|
|
245
|
+
const catchAllExPtrName = this.generateUniqueName("__catch_all_exptr", declaredSymbols);
|
|
246
|
+
let code = `${this.indent()}{\n`;
|
|
247
|
+
this.indentationLevel++;
|
|
248
|
+
code += `${this.indent()}jspp::AnyValue ${resultVarName};\n`;
|
|
249
|
+
code +=
|
|
250
|
+
`${this.indent()}std::exception_ptr ${catchAllExPtrName} = nullptr;\n`;
|
|
251
|
+
code += `${this.indent()}bool ${hasReturnedFlagName} = false;\n`;
|
|
252
|
+
const returnType = "jspp::JsPromise";
|
|
253
|
+
const returnCmd = "co_return";
|
|
254
|
+
const callPrefix = "co_await ";
|
|
255
|
+
code += `${this.indent()}try {\n`;
|
|
256
|
+
this.indentationLevel++;
|
|
257
|
+
code +=
|
|
258
|
+
`${this.indent()}${resultVarName} = ${callPrefix}([=, &${hasReturnedFlagName}]() -> ${returnType} {\n`;
|
|
259
|
+
this.indentationLevel++;
|
|
260
|
+
const innerContext = {
|
|
261
|
+
...context,
|
|
262
|
+
isFunctionBody: false,
|
|
263
|
+
isInsideTryCatchLambda: true,
|
|
264
|
+
hasReturnedFlag: hasReturnedFlagName,
|
|
265
|
+
};
|
|
266
|
+
const exPtr = this.generateUniqueName("__ex_ptr");
|
|
267
|
+
code += `${this.indent()}std::exception_ptr ${exPtr} = nullptr;\n`;
|
|
268
|
+
code += `${this.indent()}try {\n`;
|
|
269
|
+
this.indentationLevel++;
|
|
270
|
+
code += this.visit(tryStmt.tryBlock, innerContext);
|
|
271
|
+
this.indentationLevel--;
|
|
272
|
+
code +=
|
|
273
|
+
`${this.indent()}} catch (...) { ${exPtr} = std::current_exception(); }\n`;
|
|
274
|
+
if (tryStmt.catchClause) {
|
|
275
|
+
const exceptionName = this.generateUniqueExceptionName(tryStmt.catchClause.variableDeclaration?.name.getText(), declaredSymbols);
|
|
276
|
+
const caughtValVar = this.generateUniqueName("__caught_val");
|
|
277
|
+
const caughtFlagVar = this.generateUniqueName("__caught_flag");
|
|
278
|
+
code +=
|
|
279
|
+
`${this.indent()}jspp::AnyValue ${caughtValVar} = jspp::Constants::UNDEFINED;\n`;
|
|
280
|
+
code += `${this.indent()}bool ${caughtFlagVar} = false;\n`;
|
|
281
|
+
code += `${this.indent()}if (${exPtr}) {\n`;
|
|
282
|
+
this.indentationLevel++;
|
|
283
|
+
code +=
|
|
284
|
+
`${this.indent()}try { std::rethrow_exception(${exPtr}); } catch (const std::exception& ${exceptionName}) {\n`;
|
|
285
|
+
this.indentationLevel++;
|
|
286
|
+
code +=
|
|
287
|
+
`${this.indent()}${caughtValVar} = jspp::Exception::exception_to_any_value(${exceptionName});\n`;
|
|
288
|
+
code += `${this.indent()}${caughtFlagVar} = true;\n`;
|
|
289
|
+
this.indentationLevel--;
|
|
290
|
+
code += `${this.indent()}} catch (...) {\n`;
|
|
291
|
+
this.indentationLevel++;
|
|
292
|
+
code +=
|
|
293
|
+
`${this.indent()}${caughtValVar} = jspp::AnyValue::make_string("Unknown native exception");\n`;
|
|
294
|
+
code += `${this.indent()}${caughtFlagVar} = true;\n`;
|
|
295
|
+
this.indentationLevel--;
|
|
296
|
+
code += `${this.indent()}}\n`;
|
|
297
|
+
this.indentationLevel--;
|
|
298
|
+
code += `${this.indent()}}\n`;
|
|
299
|
+
code += `${this.indent()}if (${caughtFlagVar}) {\n`;
|
|
300
|
+
this.indentationLevel++;
|
|
301
|
+
code += `${this.indent()}{\n`; // Block scope
|
|
302
|
+
this.indentationLevel++;
|
|
303
|
+
if (tryStmt.catchClause.variableDeclaration) {
|
|
304
|
+
const varName = tryStmt.catchClause.variableDeclaration.name
|
|
305
|
+
.getText();
|
|
306
|
+
code +=
|
|
307
|
+
`${this.indent()}jspp::AnyValue ${varName} = ${caughtValVar};\n`;
|
|
308
|
+
}
|
|
309
|
+
const catchContext = { ...innerContext, exceptionName };
|
|
310
|
+
code += this.visit(tryStmt.catchClause.block, catchContext);
|
|
311
|
+
this.indentationLevel--;
|
|
312
|
+
code += `${this.indent()}}\n`;
|
|
313
|
+
this.indentationLevel--;
|
|
314
|
+
code += `${this.indent()}}\n`;
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
code +=
|
|
318
|
+
`${this.indent()}if (${exPtr}) { std::rethrow_exception(${exPtr}); }\n`;
|
|
319
|
+
}
|
|
320
|
+
code +=
|
|
321
|
+
`${this.indent()}${returnCmd} jspp::Constants::UNDEFINED;\n`;
|
|
322
|
+
this.indentationLevel--;
|
|
323
|
+
code += `${this.indent()}})();\n`;
|
|
324
|
+
this.indentationLevel--;
|
|
325
|
+
code +=
|
|
326
|
+
`${this.indent()}} catch (...) { ${catchAllExPtrName} = std::current_exception(); }\n`;
|
|
327
|
+
code += `${this.indent()}// finally block\n`;
|
|
328
|
+
code += this.visit(tryStmt.finallyBlock, {
|
|
329
|
+
...context,
|
|
330
|
+
isFunctionBody: false,
|
|
331
|
+
});
|
|
332
|
+
code += `${this.indent()}// re-throw or return\n`;
|
|
333
|
+
code +=
|
|
334
|
+
`${this.indent()}if (${catchAllExPtrName}) { std::rethrow_exception(${catchAllExPtrName}); }\n`;
|
|
335
|
+
code +=
|
|
336
|
+
`${this.indent()}if (${hasReturnedFlagName}) { ${returnCmd} ${resultVarName}; }\n`;
|
|
337
|
+
this.indentationLevel--;
|
|
338
|
+
code += `${this.indent()}}\n`;
|
|
339
|
+
return code;
|
|
340
|
+
}
|
|
341
|
+
else {
|
|
342
|
+
const exceptionName = this.generateUniqueExceptionName(tryStmt.catchClause?.variableDeclaration?.name.getText(), context.topLevelScopeSymbols, context.localScopeSymbols);
|
|
343
|
+
const newContext = {
|
|
344
|
+
...context,
|
|
345
|
+
isFunctionBody: false,
|
|
346
|
+
exceptionName,
|
|
347
|
+
};
|
|
348
|
+
const exPtr = this.generateUniqueName("__ex_ptr");
|
|
349
|
+
let code = `${this.indent()}{\n`;
|
|
350
|
+
this.indentationLevel++;
|
|
351
|
+
code += `${this.indent()}std::exception_ptr ${exPtr} = nullptr;\n`;
|
|
352
|
+
code += `${this.indent()}try {\n`;
|
|
353
|
+
this.indentationLevel++;
|
|
354
|
+
code += this.visit(tryStmt.tryBlock, newContext);
|
|
355
|
+
this.indentationLevel--;
|
|
356
|
+
code +=
|
|
357
|
+
`${this.indent()}} catch (...) { ${exPtr} = std::current_exception(); }\n`;
|
|
358
|
+
if (tryStmt.catchClause) {
|
|
359
|
+
const caughtValVar = this.generateUniqueName("__caught_val");
|
|
360
|
+
const caughtFlagVar = this.generateUniqueName("__caught_flag");
|
|
361
|
+
code +=
|
|
362
|
+
`${this.indent()}jspp::AnyValue ${caughtValVar} = jspp::Constants::UNDEFINED;\n`;
|
|
363
|
+
code += `${this.indent()}bool ${caughtFlagVar} = false;\n`;
|
|
364
|
+
code += `${this.indent()}if (${exPtr}) {\n`;
|
|
365
|
+
this.indentationLevel++;
|
|
366
|
+
code +=
|
|
367
|
+
`${this.indent()}try { std::rethrow_exception(${exPtr}); } catch (const std::exception& ${exceptionName}) {\n`;
|
|
368
|
+
this.indentationLevel++;
|
|
369
|
+
code +=
|
|
370
|
+
`${this.indent()}${caughtValVar} = jspp::Exception::exception_to_any_value(${exceptionName});\n`;
|
|
371
|
+
code += `${this.indent()}${caughtFlagVar} = true;\n`;
|
|
372
|
+
this.indentationLevel--;
|
|
373
|
+
code += `${this.indent()}} catch (...) {\n`;
|
|
374
|
+
this.indentationLevel++;
|
|
375
|
+
code +=
|
|
376
|
+
`${this.indent()}${caughtValVar} = jspp::AnyValue::make_string("Unknown native exception");\n`;
|
|
377
|
+
code += `${this.indent()}${caughtFlagVar} = true;\n`;
|
|
378
|
+
this.indentationLevel--;
|
|
379
|
+
code += `${this.indent()}}\n`;
|
|
380
|
+
this.indentationLevel--;
|
|
381
|
+
code += `${this.indent()}}\n`;
|
|
382
|
+
code += `${this.indent()}if (${caughtFlagVar}) {\n`;
|
|
383
|
+
this.indentationLevel++;
|
|
384
|
+
code += `${this.indent()}{\n`; // Block scope
|
|
385
|
+
this.indentationLevel++;
|
|
386
|
+
if (tryStmt.catchClause.variableDeclaration) {
|
|
387
|
+
const varName = tryStmt.catchClause.variableDeclaration.name
|
|
388
|
+
.getText();
|
|
389
|
+
code +=
|
|
390
|
+
`${this.indent()}jspp::AnyValue ${varName} = ${caughtValVar};\n`;
|
|
391
|
+
}
|
|
392
|
+
code += this.visit(tryStmt.catchClause.block, newContext);
|
|
393
|
+
this.indentationLevel--;
|
|
394
|
+
code += `${this.indent()}}\n`;
|
|
395
|
+
this.indentationLevel--;
|
|
396
|
+
code += `${this.indent()}}\n`;
|
|
397
|
+
}
|
|
398
|
+
else {
|
|
399
|
+
code +=
|
|
400
|
+
`${this.indent()}if (${exPtr}) { std::rethrow_exception(${exPtr}); }\n`;
|
|
401
|
+
}
|
|
402
|
+
this.indentationLevel--;
|
|
403
|
+
code += `${this.indent()}}\n`;
|
|
404
|
+
return code;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
230
407
|
if (tryStmt.finallyBlock) {
|
|
231
|
-
const declaredSymbols = new Set();
|
|
408
|
+
const declaredSymbols = new Set(context.topLevelScopeSymbols.toSet());
|
|
232
409
|
this.getDeclaredSymbols(tryStmt.tryBlock).forEach((s) => declaredSymbols.add(s));
|
|
233
410
|
if (tryStmt.catchClause) {
|
|
234
411
|
this.getDeclaredSymbols(tryStmt.catchClause).forEach((s) => declaredSymbols.add(s));
|
|
235
412
|
}
|
|
236
413
|
this.getDeclaredSymbols(tryStmt.finallyBlock).forEach((s) => declaredSymbols.add(s));
|
|
237
|
-
const finallyLambdaName = this.generateUniqueName("__finally_", declaredSymbols);
|
|
238
414
|
const resultVarName = this.generateUniqueName("__try_result_", declaredSymbols);
|
|
239
415
|
const hasReturnedFlagName = this.generateUniqueName("__try_has_returned_", declaredSymbols);
|
|
416
|
+
const catchAllExPtrName = this.generateUniqueName("__catch_all_exptr", declaredSymbols);
|
|
240
417
|
let code = `${this.indent()}{\n`;
|
|
241
418
|
this.indentationLevel++;
|
|
242
419
|
code += `${this.indent()}jspp::AnyValue ${resultVarName};\n`;
|
|
243
|
-
code += `${this.indent()}bool ${hasReturnedFlagName} = false;\n`;
|
|
244
|
-
const finallyBlockCode = this.visit(tryStmt.finallyBlock, {
|
|
245
|
-
...context,
|
|
246
|
-
isFunctionBody: false,
|
|
247
|
-
});
|
|
248
420
|
code +=
|
|
249
|
-
`${this.indent()}
|
|
421
|
+
`${this.indent()}std::exception_ptr ${catchAllExPtrName} = nullptr;\n`;
|
|
422
|
+
code += `${this.indent()}bool ${hasReturnedFlagName} = false;\n`;
|
|
423
|
+
const returnType = "jspp::AnyValue";
|
|
424
|
+
const returnCmd = "return";
|
|
425
|
+
const callPrefix = "";
|
|
250
426
|
code += `${this.indent()}try {\n`;
|
|
251
427
|
this.indentationLevel++;
|
|
252
428
|
code +=
|
|
253
|
-
`${this.indent()}${resultVarName} = ([=, &${hasReturnedFlagName}]() ->
|
|
429
|
+
`${this.indent()}${resultVarName} = ${callPrefix}([=, &${hasReturnedFlagName}]() -> ${returnType} {\n`;
|
|
254
430
|
this.indentationLevel++;
|
|
255
431
|
const innerContext = {
|
|
256
432
|
...context,
|
|
@@ -264,11 +440,14 @@ export function visitTryStatement(node, context) {
|
|
|
264
440
|
this.indentationLevel--;
|
|
265
441
|
code += `${this.indent()}}\n`;
|
|
266
442
|
if (tryStmt.catchClause) {
|
|
267
|
-
const exceptionName = this.generateUniqueExceptionName(tryStmt.catchClause.variableDeclaration?.name.getText());
|
|
443
|
+
const exceptionName = this.generateUniqueExceptionName(tryStmt.catchClause.variableDeclaration?.name.getText(), context.topLevelScopeSymbols, context.localScopeSymbols);
|
|
268
444
|
const catchContext = { ...innerContext, exceptionName };
|
|
269
445
|
code +=
|
|
270
446
|
`${this.indent()}catch (const std::exception& ${exceptionName}) {\n`;
|
|
271
447
|
this.indentationLevel++;
|
|
448
|
+
// We cannot co_await here. For now, let's just visit the catch block.
|
|
449
|
+
// If the catch block contains await, it will fail to compile.
|
|
450
|
+
// TODO: properly handle async catch by moving it out of native C++ catch.
|
|
272
451
|
code += this.visit(tryStmt.catchClause.block, catchContext);
|
|
273
452
|
this.indentationLevel--;
|
|
274
453
|
code += `${this.indent()}}\n`;
|
|
@@ -276,28 +455,28 @@ export function visitTryStatement(node, context) {
|
|
|
276
455
|
else {
|
|
277
456
|
code += `${this.indent()}catch (...) { throw; }\n`;
|
|
278
457
|
}
|
|
279
|
-
code += `${this.indent()}${
|
|
458
|
+
code += `${this.indent()}${returnCmd} jspp::Constants::UNDEFINED;\n`;
|
|
280
459
|
this.indentationLevel--;
|
|
281
460
|
code += `${this.indent()}})();\n`;
|
|
282
461
|
this.indentationLevel--;
|
|
283
|
-
code +=
|
|
284
|
-
|
|
285
|
-
code += `${this.indent()}
|
|
286
|
-
code +=
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
code += `${this.indent()}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
462
|
+
code +=
|
|
463
|
+
`${this.indent()}} catch (...) { ${catchAllExPtrName} = std::current_exception(); }\n`;
|
|
464
|
+
code += `${this.indent()}// finally block\n`;
|
|
465
|
+
code += this.visit(tryStmt.finallyBlock, {
|
|
466
|
+
...context,
|
|
467
|
+
isFunctionBody: false,
|
|
468
|
+
});
|
|
469
|
+
code += `${this.indent()}// re-throw or return\n`;
|
|
470
|
+
code +=
|
|
471
|
+
`${this.indent()}if (${catchAllExPtrName}) { std::rethrow_exception(${catchAllExPtrName}); }\n`;
|
|
472
|
+
code +=
|
|
473
|
+
`${this.indent()}if (${hasReturnedFlagName}) { ${returnCmd} ${resultVarName}; }\n`;
|
|
295
474
|
this.indentationLevel--;
|
|
296
475
|
code += `${this.indent()}}\n`;
|
|
297
476
|
return code;
|
|
298
477
|
}
|
|
299
478
|
else {
|
|
300
|
-
const exceptionName = this.generateUniqueExceptionName(tryStmt.catchClause?.variableDeclaration?.name.getText());
|
|
479
|
+
const exceptionName = this.generateUniqueExceptionName(tryStmt.catchClause?.variableDeclaration?.name.getText(), context.topLevelScopeSymbols, context.localScopeSymbols);
|
|
301
480
|
const newContext = {
|
|
302
481
|
...context,
|
|
303
482
|
isFunctionBody: false,
|
|
@@ -306,7 +485,8 @@ export function visitTryStatement(node, context) {
|
|
|
306
485
|
let code = `${this.indent()}try `;
|
|
307
486
|
code += this.visit(tryStmt.tryBlock, newContext);
|
|
308
487
|
if (tryStmt.catchClause) {
|
|
309
|
-
code +=
|
|
488
|
+
code +=
|
|
489
|
+
`${this.indent()}catch (const std::exception& ${exceptionName}) `;
|
|
310
490
|
code += this.visit(tryStmt.catchClause, newContext);
|
|
311
491
|
}
|
|
312
492
|
return code;
|
|
@@ -323,21 +503,12 @@ export function visitCatchClause(node, context) {
|
|
|
323
503
|
const varName = catchClause.variableDeclaration.name.getText();
|
|
324
504
|
let code = `{\n`;
|
|
325
505
|
this.indentationLevel++;
|
|
326
|
-
code += `${this.indent()}{\n`;
|
|
327
|
-
this.indentationLevel++;
|
|
328
506
|
// The JS exception variable is always local to the catch block
|
|
329
507
|
code +=
|
|
330
508
|
`${this.indent()}jspp::AnyValue ${varName} = jspp::Exception::exception_to_any_value(${exceptionName});\n`;
|
|
331
|
-
// Shadow the C++ exception variable *only if* the names don't clash.
|
|
332
|
-
if (varName !== exceptionName) {
|
|
333
|
-
code +=
|
|
334
|
-
`${this.indent()}auto ${exceptionName} = std::make_shared<jspp::AnyValue>(jspp::AnyValue::make_undefined());\n`;
|
|
335
|
-
}
|
|
336
509
|
code += this.visit(catchClause.block, context);
|
|
337
510
|
this.indentationLevel--;
|
|
338
511
|
code += `${this.indent()}}\n`;
|
|
339
|
-
this.indentationLevel--;
|
|
340
|
-
code += `${this.indent()}}\n`;
|
|
341
512
|
return code;
|
|
342
513
|
}
|
|
343
514
|
else {
|
|
@@ -361,7 +532,7 @@ export function visitYieldExpression(node, context) {
|
|
|
361
532
|
if (typeInfo &&
|
|
362
533
|
!typeInfo.isParameter &&
|
|
363
534
|
!typeInfo.isBuiltin) {
|
|
364
|
-
exprText = this.getDerefCode(exprText, this.getJsVarName(expr), typeInfo);
|
|
535
|
+
exprText = this.getDerefCode(exprText, this.getJsVarName(expr), context, typeInfo);
|
|
365
536
|
}
|
|
366
537
|
}
|
|
367
538
|
// Handle `yield*` expression
|
|
@@ -369,32 +540,59 @@ export function visitYieldExpression(node, context) {
|
|
|
369
540
|
let code = `${this.indent()}{\n`;
|
|
370
541
|
this.indentationLevel++;
|
|
371
542
|
const declaredSymbols = this.getDeclaredSymbols(expr);
|
|
543
|
+
context.topLevelScopeSymbols.toSet().forEach((s) => declaredSymbols.add(s));
|
|
372
544
|
const iterableRef = this.generateUniqueName("__iter_ref", declaredSymbols);
|
|
373
545
|
const iterator = this.generateUniqueName("__iter", declaredSymbols);
|
|
374
546
|
const nextFunc = this.generateUniqueName("__next_func", declaredSymbols);
|
|
375
547
|
const nextRes = this.generateUniqueName("__next_res", declaredSymbols);
|
|
376
548
|
const varName = this.getJsVarName(expr);
|
|
377
549
|
code += `${this.indent()}auto ${iterableRef} = ${exprText};\n`;
|
|
550
|
+
if (context.isInsideAsyncFunction) {
|
|
551
|
+
code +=
|
|
552
|
+
`${this.indent()}auto ${iterator} = jspp::Access::get_async_object_value_iterator(${iterableRef}, ${varName});\n`;
|
|
553
|
+
}
|
|
554
|
+
else {
|
|
555
|
+
code +=
|
|
556
|
+
`${this.indent()}auto ${iterator} = jspp::Access::get_object_value_iterator(${iterableRef}, ${varName});\n`;
|
|
557
|
+
}
|
|
378
558
|
code +=
|
|
379
|
-
`${this.indent()}auto ${
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
559
|
+
`${this.indent()}auto ${nextFunc} = ${iterator}.get_own_property("next");\n`;
|
|
560
|
+
if (context.isInsideAsyncFunction) {
|
|
561
|
+
code +=
|
|
562
|
+
`${this.indent()}auto ${nextRes} = co_await ${nextFunc}.call(${iterator}, {}, "next");\n`;
|
|
563
|
+
}
|
|
564
|
+
else {
|
|
565
|
+
code +=
|
|
566
|
+
`${this.indent()}auto ${nextRes} = ${nextFunc}.call(${iterator}, {}, "next");\n`;
|
|
567
|
+
}
|
|
384
568
|
code +=
|
|
385
|
-
`${this.indent()}while (
|
|
569
|
+
`${this.indent()}while (!is_truthy(${nextRes}.get_own_property("done"))) {\n`;
|
|
386
570
|
this.indentationLevel++;
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
571
|
+
if (context.isInsideAsyncFunction) {
|
|
572
|
+
code +=
|
|
573
|
+
`${this.indent()}co_yield co_await ${nextRes}.get_own_property("value");\n`;
|
|
574
|
+
}
|
|
575
|
+
else {
|
|
576
|
+
code +=
|
|
577
|
+
`${this.indent()}co_yield ${nextRes}.get_own_property("value");\n`;
|
|
578
|
+
}
|
|
579
|
+
if (context.isInsideAsyncFunction) {
|
|
580
|
+
code +=
|
|
581
|
+
`${this.indent()}${nextRes} = co_await ${nextFunc}.call(${iterator}, {}, "next");\n`;
|
|
582
|
+
}
|
|
583
|
+
else {
|
|
584
|
+
code +=
|
|
585
|
+
`${this.indent()}${nextRes} = ${nextFunc}.call(${iterator}, {}, "next");\n`;
|
|
586
|
+
}
|
|
391
587
|
this.indentationLevel--;
|
|
392
|
-
code += `${this.indent()}}
|
|
588
|
+
code += `${this.indent()}}\n`;
|
|
393
589
|
return code;
|
|
394
590
|
}
|
|
395
|
-
|
|
591
|
+
const awaitPart = context.isInsideAsyncFunction ? "co_await " : "";
|
|
592
|
+
return `${this.indent()}co_yield ${awaitPart}${exprText}`;
|
|
396
593
|
}
|
|
397
|
-
|
|
594
|
+
const awaitPart = context.isInsideAsyncFunction ? "co_await " : "";
|
|
595
|
+
return `${this.indent()}co_yield ${awaitPart}jspp::Constants::UNDEFINED`;
|
|
398
596
|
}
|
|
399
597
|
export function visitReturnStatement(node, context) {
|
|
400
598
|
if (context.isMainContext) {
|
|
@@ -418,14 +616,14 @@ export function visitReturnStatement(node, context) {
|
|
|
418
616
|
else if (typeInfo &&
|
|
419
617
|
!typeInfo.isParameter &&
|
|
420
618
|
!typeInfo.isBuiltin) {
|
|
421
|
-
finalExpr = this.getDerefCode(exprText, this.getJsVarName(expr), typeInfo);
|
|
619
|
+
finalExpr = this.getDerefCode(exprText, this.getJsVarName(expr), context, typeInfo);
|
|
422
620
|
}
|
|
423
621
|
}
|
|
424
622
|
returnCode += `${this.indent()}${returnCmd} ${finalExpr};\n`;
|
|
425
623
|
}
|
|
426
624
|
else {
|
|
427
625
|
returnCode +=
|
|
428
|
-
`${this.indent()}${returnCmd} jspp::
|
|
626
|
+
`${this.indent()}${returnCmd} jspp::Constants::UNDEFINED;\n`;
|
|
429
627
|
}
|
|
430
628
|
return returnCode;
|
|
431
629
|
}
|
|
@@ -442,10 +640,10 @@ export function visitReturnStatement(node, context) {
|
|
|
442
640
|
if (typeInfo &&
|
|
443
641
|
!typeInfo.isParameter &&
|
|
444
642
|
!typeInfo.isBuiltin) {
|
|
445
|
-
finalExpr = this.getDerefCode(exprText, this.getJsVarName(expr), typeInfo);
|
|
643
|
+
finalExpr = this.getDerefCode(exprText, this.getJsVarName(expr), context, typeInfo);
|
|
446
644
|
}
|
|
447
645
|
}
|
|
448
646
|
return `${this.indent()}${returnCmd} ${finalExpr};\n`;
|
|
449
647
|
}
|
|
450
|
-
return `${this.indent()}${returnCmd} jspp::
|
|
648
|
+
return `${this.indent()}${returnCmd} jspp::Constants::UNDEFINED;\n`;
|
|
451
649
|
}
|
|
@@ -3,7 +3,7 @@ import { CodeGenerator } from "./";
|
|
|
3
3
|
import { visitClassDeclaration } from "./class-handlers";
|
|
4
4
|
import { visitCaseClause, visitDefaultClause, visitDoStatement, visitForInStatement, visitForOfStatement, visitForStatement, visitSwitchStatement, visitWhileStatement, } from "./control-flow-handlers";
|
|
5
5
|
import { visitVariableDeclaration, visitVariableDeclarationList, } from "./declaration-handlers";
|
|
6
|
-
import { visitArrayLiteralExpression, visitAwaitExpression, visitBinaryExpression, visitCallExpression, visitConditionalExpression, visitElementAccessExpression, visitNewExpression, visitObjectLiteralExpression, visitParenthesizedExpression, visitPostfixUnaryExpression, visitPrefixUnaryExpression, visitPropertyAccessExpression, visitTemplateExpression, visitTypeOfExpression, visitVoidExpression, } from "./expression-handlers";
|
|
6
|
+
import { visitArrayLiteralExpression, visitAwaitExpression, visitBinaryExpression, visitCallExpression, visitConditionalExpression, visitDeleteExpression, visitElementAccessExpression, visitNewExpression, visitObjectLiteralExpression, visitParenthesizedExpression, visitPostfixUnaryExpression, visitPrefixUnaryExpression, visitPropertyAccessExpression, visitTemplateExpression, visitTypeOfExpression, visitVoidExpression, } from "./expression-handlers";
|
|
7
7
|
import { visitArrowFunction, visitFunctionDeclaration, visitFunctionExpression, } from "./function-handlers";
|
|
8
8
|
import { visitFalseKeyword, visitIdentifier, visitNoSubstitutionTemplateLiteral, visitNullKeyword, visitNumericLiteral, visitStringLiteral, visitThisKeyword, visitTrueKeyword, visitUndefinedKeyword, } from "./literal-handlers";
|
|
9
9
|
import { visitBlock, visitBreakStatement, visitCatchClause, visitContinueStatement, visitExpressionStatement, visitIfStatement, visitLabeledStatement, visitReturnStatement, visitSourceFile, visitThrowStatement, visitTryStatement, visitVariableStatement, visitYieldExpression, } from "./statement-handlers";
|
|
@@ -72,6 +72,8 @@ export function visit(node, context) {
|
|
|
72
72
|
return visitBinaryExpression.call(this, node, context);
|
|
73
73
|
case ts.SyntaxKind.ConditionalExpression:
|
|
74
74
|
return visitConditionalExpression.call(this, node, context);
|
|
75
|
+
case ts.SyntaxKind.DeleteExpression:
|
|
76
|
+
return visitDeleteExpression.call(this, node, context);
|
|
75
77
|
case ts.SyntaxKind.ThrowStatement:
|
|
76
78
|
return visitThrowStatement.call(this, node, context);
|
|
77
79
|
case ts.SyntaxKind.TryStatement:
|