@ugo-studio/jspp 0.3.1 → 0.3.3
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/cli/args.js +22 -0
- package/dist/cli/compiler.js +53 -0
- package/dist/cli/index.js +43 -117
- package/dist/cli/pch.js +71 -0
- package/dist/cli/runner.js +23 -0
- package/dist/cli/spinner.js +27 -11
- package/dist/cli/transpiler.js +20 -0
- package/dist/cli/utils.js +25 -27
- package/dist/cli/wasm.js +70 -0
- package/dist/index.js +17 -6
- package/dist/{analysis → interpreter/analysis}/scope.js +34 -1
- package/dist/{analysis → interpreter/analysis}/typeAnalyzer.js +542 -3
- package/dist/{core → interpreter/core}/codegen/class-handlers.js +1 -1
- package/dist/{core → interpreter/core}/codegen/control-flow-handlers.js +2 -2
- package/dist/{core → interpreter/core}/codegen/declaration-handlers.js +21 -9
- package/dist/{core → interpreter/core}/codegen/destructuring-handlers.js +3 -3
- package/dist/{core → interpreter/core}/codegen/expression-handlers.js +136 -82
- package/dist/{core → interpreter/core}/codegen/function-handlers.js +108 -61
- package/dist/{core → interpreter/core}/codegen/helpers.js +79 -11
- package/dist/interpreter/core/codegen/index.js +156 -0
- package/dist/{core → interpreter/core}/codegen/literal-handlers.js +9 -0
- package/dist/{core → interpreter/core}/codegen/statement-handlers.js +39 -1
- package/package.json +6 -4
- package/scripts/precompile-headers.ts +71 -19
- package/scripts/setup-emsdk.ts +114 -0
- package/src/prelude/any_value.cpp +851 -599
- package/src/prelude/any_value.hpp +28 -30
- package/src/prelude/jspp.hpp +3 -1
- package/src/prelude/library/boolean.cpp +30 -0
- package/src/prelude/library/boolean.hpp +14 -0
- package/src/prelude/library/error.cpp +2 -2
- package/src/prelude/library/global.cpp +2 -0
- package/src/prelude/library/math.cpp +46 -43
- package/src/prelude/library/math.hpp +2 -0
- package/src/prelude/library/object.cpp +1 -1
- package/src/prelude/types.hpp +10 -9
- package/src/prelude/utils/access.hpp +2 -36
- package/src/prelude/utils/assignment_operators.hpp +136 -20
- package/src/prelude/utils/log_any_value/log_any_value.hpp +1 -1
- package/src/prelude/utils/log_any_value/primitives.hpp +1 -1
- package/src/prelude/utils/operators.hpp +123 -88
- package/src/prelude/utils/operators_native.hpp +360 -0
- package/src/prelude/utils/well_known_symbols.hpp +13 -13
- package/src/prelude/values/array.cpp +3 -3
- package/src/prelude/values/boolean.cpp +64 -0
- package/src/prelude/values/iterator.cpp +262 -210
- package/src/prelude/values/number.cpp +137 -92
- package/src/prelude/values/object.cpp +163 -122
- package/src/prelude/values/prototypes/boolean.hpp +24 -0
- package/src/prelude/values/prototypes/number.hpp +8 -1
- package/dist/cli/file-utils.js +0 -20
- package/dist/cli-utils/args.js +0 -59
- package/dist/cli-utils/colors.js +0 -9
- package/dist/cli-utils/file-utils.js +0 -20
- package/dist/cli-utils/spinner.js +0 -55
- package/dist/cli.js +0 -153
- package/dist/core/codegen/index.js +0 -88
- package/src/prelude/utils/operators_primitive.hpp +0 -337
- /package/dist/{ast → interpreter/ast}/symbols.js +0 -0
- /package/dist/{ast → interpreter/ast}/types.js +0 -0
- /package/dist/{core → interpreter/core}/codegen/visitor.js +0 -0
- /package/dist/{core → interpreter/core}/constants.js +0 -0
- /package/dist/{core → interpreter/core}/error.js +0 -0
- /package/dist/{core → interpreter/core}/parser.js +0 -0
- /package/dist/{core → interpreter/core}/traverser.js +0 -0
|
@@ -5,8 +5,18 @@ import { CodeGenerator } from "./index.js";
|
|
|
5
5
|
export function generateLambdaComponents(node, context, options) {
|
|
6
6
|
const declaredSymbols = this.getDeclaredSymbols(node);
|
|
7
7
|
const argsName = this.generateUniqueName("__args_", declaredSymbols, context.globalScopeSymbols, context.localScopeSymbols);
|
|
8
|
-
const nativeExcessArgsName = this.generateUniqueName(
|
|
9
|
-
|
|
8
|
+
// const nativeExcessArgsName = this.generateUniqueName(
|
|
9
|
+
// "__excess_args",
|
|
10
|
+
// declaredSymbols,
|
|
11
|
+
// context.globalScopeSymbols,
|
|
12
|
+
// context.localScopeSymbols,
|
|
13
|
+
// );
|
|
14
|
+
// const nativeTotalArgsSizeName = this.generateUniqueName(
|
|
15
|
+
// "__total_args_size",
|
|
16
|
+
// declaredSymbols,
|
|
17
|
+
// context.globalScopeSymbols,
|
|
18
|
+
// context.localScopeSymbols,
|
|
19
|
+
// );
|
|
10
20
|
const isInsideGeneratorFunction = this.isGeneratorFunction(node);
|
|
11
21
|
const isInsideAsyncFunction = this.isAsyncFunction(node);
|
|
12
22
|
const isArrow = ts.isArrowFunction(node);
|
|
@@ -14,15 +24,27 @@ export function generateLambdaComponents(node, context, options) {
|
|
|
14
24
|
isInsideGeneratorFunction: isInsideGeneratorFunction,
|
|
15
25
|
isInsideAsyncFunction: isInsideAsyncFunction,
|
|
16
26
|
});
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
27
|
+
const getFuncReturnType = (isInsideNativeLambda) => {
|
|
28
|
+
if (isInsideGeneratorFunction && isInsideAsyncFunction) {
|
|
29
|
+
return "jspp::JsAsyncIterator<jspp::AnyValue>";
|
|
30
|
+
}
|
|
31
|
+
else if (isInsideGeneratorFunction) {
|
|
32
|
+
return "jspp::JsIterator<jspp::AnyValue>";
|
|
33
|
+
}
|
|
34
|
+
else if (isInsideAsyncFunction)
|
|
35
|
+
return "jspp::JsPromise";
|
|
36
|
+
else {
|
|
37
|
+
const inferedReturnType = this.typeAnalyzer.inferFunctionReturnType(node);
|
|
38
|
+
if (isInsideNativeLambda && inferedReturnType === "number") {
|
|
39
|
+
return "double";
|
|
40
|
+
}
|
|
41
|
+
return "jspp::AnyValue";
|
|
42
|
+
}
|
|
43
|
+
};
|
|
22
44
|
// Lambda arguments: regular functions use std::span for performance.
|
|
23
45
|
// Generators and async functions use std::vector to ensure they are safely copied into the coroutine frame.
|
|
24
46
|
const paramThisType = "jspp::AnyValue";
|
|
25
|
-
const paramArgsType =
|
|
47
|
+
const paramArgsType = isInsideGeneratorFunction || isInsideAsyncFunction
|
|
26
48
|
? "std::vector<jspp::AnyValue>"
|
|
27
49
|
: "std::span<const jspp::AnyValue>";
|
|
28
50
|
const globalScopeSymbols = this.prepareScopeSymbolsForVisit(context.globalScopeSymbols, context.localScopeSymbols);
|
|
@@ -31,7 +53,7 @@ export function generateLambdaComponents(node, context, options) {
|
|
|
31
53
|
isMainContext: false,
|
|
32
54
|
isInsideFunction: true,
|
|
33
55
|
isFunctionBody: false,
|
|
34
|
-
|
|
56
|
+
functionName: undefined,
|
|
35
57
|
globalScopeSymbols,
|
|
36
58
|
localScopeSymbols: new DeclaredSymbols(),
|
|
37
59
|
superClassVar: context.superClassVar,
|
|
@@ -95,7 +117,8 @@ export function generateLambdaComponents(node, context, options) {
|
|
|
95
117
|
`${this.indent()}auto ${name} = std::make_shared<jspp::AnyValue>(${signatureName});\n`;
|
|
96
118
|
}
|
|
97
119
|
else {
|
|
98
|
-
nativeParamsContent += this.generateDestructuring(p.name, signatureName, visitContext) +
|
|
120
|
+
nativeParamsContent += this.generateDestructuring(p.name, signatureName, visitContext) +
|
|
121
|
+
";\n";
|
|
99
122
|
}
|
|
100
123
|
}
|
|
101
124
|
});
|
|
@@ -114,8 +137,7 @@ export function generateLambdaComponents(node, context, options) {
|
|
|
114
137
|
checks: { initialized: true },
|
|
115
138
|
});
|
|
116
139
|
const scope = this.getScopeForNode(p);
|
|
117
|
-
const typeInfo = this.typeAnalyzer.scopeManager
|
|
118
|
-
.lookupFromScope(name, scope);
|
|
140
|
+
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(name, scope);
|
|
119
141
|
// Handle rest parameters
|
|
120
142
|
if (!!p.dotDotDotToken) {
|
|
121
143
|
const initValue = `jspp::AnyValue::make_array(${argsName}.subspan(${i}))`;
|
|
@@ -148,39 +170,43 @@ export function generateLambdaComponents(node, context, options) {
|
|
|
148
170
|
const initValue = `${argsName}.size() > ${i} ? ${argsName}[${i}] : ${defaultValue}`;
|
|
149
171
|
paramsCode +=
|
|
150
172
|
`${this.indent()}auto ${tempName} = ${initValue};\n`;
|
|
151
|
-
paramsCode +=
|
|
173
|
+
paramsCode +=
|
|
174
|
+
this.generateDestructuring(p.name, tempName, visitContext) +
|
|
175
|
+
";\n";
|
|
152
176
|
}
|
|
153
177
|
});
|
|
154
178
|
return paramsCode;
|
|
155
179
|
};
|
|
156
180
|
// Generate params and function body
|
|
157
181
|
let paramsContent = "";
|
|
158
|
-
let
|
|
182
|
+
let getBlockContentWithoutOpeningBrace = () => "";
|
|
159
183
|
// Generate `arguments` variable if it used in the function body
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
}
|
|
184
|
+
// TODO: compile arguments array from parameters
|
|
185
|
+
// if (
|
|
186
|
+
// this.isVariableUsedWithoutDeclaration("arguments", node.body as ts.Node)
|
|
187
|
+
// ) {
|
|
188
|
+
// // Add span parameter for holding the excess arguments from the caller of the native lambda
|
|
189
|
+
// nativeFuncArgs +=
|
|
190
|
+
// `, const std::size_t ${nativeTotalArgsSizeName}, std::span<const jspp::AnyValue> ${nativeExcessArgsName} = {}`;
|
|
191
|
+
// // this.indentationLevel++;
|
|
192
|
+
// // nativeParamsContent +=
|
|
193
|
+
// // `${this.indent()}jspp::AnyValue arguments = jspp::Constants::UNDEFINED;\n`;
|
|
194
|
+
// // nativeParamsContent += `${this.indent()}{\n`;
|
|
195
|
+
// // this.indentationLevel++;
|
|
196
|
+
// // nativeParamsContent +=
|
|
197
|
+
// // `${this.indent()}std::vector<jspp::AnyValue> ${argsName};\n`;
|
|
198
|
+
// // this.validateFunctionParams(node.parameters).forEach(
|
|
199
|
+
// // (p, i) => {
|
|
200
|
+
// // },
|
|
201
|
+
// // );
|
|
202
|
+
// // this.indentationLevel--;
|
|
203
|
+
// // nativeParamsContent += `${this.indent()}}\n`;
|
|
204
|
+
// // this.indentationLevel--;
|
|
205
|
+
// // this.indentationLevel++;
|
|
206
|
+
// // paramsContent =
|
|
207
|
+
// // `${this.indent()}jspp::AnyValue arguments = jspp::AnyValue::make_array(${argsName});\n`;
|
|
208
|
+
// // this.indentationLevel--;
|
|
209
|
+
// }
|
|
184
210
|
if (node.body) {
|
|
185
211
|
if (ts.isBlock(node.body)) {
|
|
186
212
|
// Hoist var declarations in the function body
|
|
@@ -202,34 +228,53 @@ export function generateLambdaComponents(node, context, options) {
|
|
|
202
228
|
paramsContent += generateParamsBuilder();
|
|
203
229
|
this.indentationLevel--;
|
|
204
230
|
// The block visitor already adds braces, so we need to remove the opening brace to inject the preamble and param extraction.
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
231
|
+
const properIndentationLevel = this.indentationLevel;
|
|
232
|
+
const nodeBody = node.body;
|
|
233
|
+
getBlockContentWithoutOpeningBrace = (isInsideNativeLambda) => {
|
|
234
|
+
const currentIndentationLevel = this.indentationLevel;
|
|
235
|
+
this.indentationLevel = properIndentationLevel;
|
|
236
|
+
const code = this.visit(nodeBody, {
|
|
237
|
+
...visitContext,
|
|
238
|
+
isMainContext: false,
|
|
239
|
+
isInsideFunction: true,
|
|
240
|
+
isFunctionBody: true,
|
|
241
|
+
isInsideGeneratorFunction,
|
|
242
|
+
isInsideAsyncFunction,
|
|
243
|
+
isInsideNativeLambda,
|
|
244
|
+
})
|
|
245
|
+
.trim()
|
|
246
|
+
.substring(2);
|
|
247
|
+
this.indentationLevel = currentIndentationLevel;
|
|
248
|
+
return code;
|
|
249
|
+
};
|
|
213
250
|
}
|
|
214
251
|
else {
|
|
215
252
|
this.indentationLevel++;
|
|
216
253
|
paramsContent += generateParamsBuilder();
|
|
217
|
-
|
|
218
|
-
|
|
254
|
+
const properIndentationLevel = this.indentationLevel;
|
|
255
|
+
const nodeBody = node.body;
|
|
256
|
+
getBlockContentWithoutOpeningBrace = (isInsideNativeLambda) => {
|
|
257
|
+
const currentIndentationLevel = this.indentationLevel;
|
|
258
|
+
this.indentationLevel = properIndentationLevel;
|
|
259
|
+
let code = `${this.indent()}${returnCommand} ${this.visit(nodeBody, {
|
|
219
260
|
...visitContext,
|
|
220
261
|
isMainContext: false,
|
|
221
262
|
isInsideFunction: true,
|
|
222
263
|
isFunctionBody: false,
|
|
223
|
-
isInsideGeneratorFunction
|
|
224
|
-
isInsideAsyncFunction
|
|
264
|
+
isInsideGeneratorFunction,
|
|
265
|
+
isInsideAsyncFunction,
|
|
266
|
+
isInsideNativeLambda,
|
|
225
267
|
})};\n`;
|
|
268
|
+
this.indentationLevel--;
|
|
269
|
+
code += `${this.indent()}}`;
|
|
270
|
+
this.indentationLevel = currentIndentationLevel;
|
|
271
|
+
return code;
|
|
272
|
+
};
|
|
226
273
|
this.indentationLevel--;
|
|
227
|
-
blockContentWithoutOpeningBrace += `${this.indent()}}`;
|
|
228
274
|
}
|
|
229
275
|
}
|
|
230
276
|
else {
|
|
231
|
-
|
|
232
|
-
`${returnCommand} jspp::Constants::UNDEFINED; }\n`;
|
|
277
|
+
getBlockContentWithoutOpeningBrace = () => `${returnCommand} jspp::Constants::UNDEFINED; }`;
|
|
233
278
|
}
|
|
234
279
|
return {
|
|
235
280
|
node,
|
|
@@ -239,37 +284,39 @@ export function generateLambdaComponents(node, context, options) {
|
|
|
239
284
|
thisArgParam,
|
|
240
285
|
funcArgs,
|
|
241
286
|
nativeFuncArgs,
|
|
242
|
-
|
|
287
|
+
getFuncReturnType,
|
|
243
288
|
preamble,
|
|
244
289
|
nativePreamble,
|
|
245
290
|
paramsContent,
|
|
246
291
|
nativeParamsContent,
|
|
247
|
-
|
|
292
|
+
getBlockContentWithoutOpeningBrace,
|
|
248
293
|
isInsideGeneratorFunction,
|
|
249
294
|
isInsideAsyncFunction,
|
|
250
295
|
};
|
|
251
296
|
}
|
|
252
297
|
export function generateNativeLambda(comps) {
|
|
253
|
-
const { options, thisArgParam, nativeFuncArgs,
|
|
298
|
+
const { options, thisArgParam, nativeFuncArgs, getFuncReturnType, nativePreamble, nativeParamsContent, getBlockContentWithoutOpeningBrace, } = comps;
|
|
254
299
|
const capture = options?.capture || "[=]";
|
|
255
300
|
const nativeName = options?.nativeName;
|
|
256
301
|
const selfParam = nativeName ? `this auto&& ${nativeName}, ` : "";
|
|
257
302
|
const mutableLabel = nativeName ? "" : "mutable ";
|
|
303
|
+
const funcReturnType = getFuncReturnType(true);
|
|
258
304
|
let lambda = `${capture}(${selfParam}${thisArgParam}${nativeFuncArgs}) ${mutableLabel}-> ${funcReturnType} {\n`;
|
|
259
305
|
lambda += nativePreamble;
|
|
260
306
|
lambda += nativeParamsContent;
|
|
261
|
-
lambda +=
|
|
307
|
+
lambda += getBlockContentWithoutOpeningBrace(true);
|
|
262
308
|
return lambda;
|
|
263
309
|
}
|
|
264
310
|
export function generateWrappedLambda(comps) {
|
|
265
|
-
const { node, context, options, thisArgParam, funcArgs,
|
|
311
|
+
const { node, context, options, thisArgParam, funcArgs, getFuncReturnType, preamble, paramsContent, getBlockContentWithoutOpeningBrace, isInsideGeneratorFunction, isInsideAsyncFunction, } = comps;
|
|
266
312
|
const capture = options?.capture || "[=]";
|
|
267
313
|
const isAssignment = options?.isAssignment || false;
|
|
314
|
+
const funcReturnType = getFuncReturnType(false);
|
|
268
315
|
const noTypeSignature = options?.noTypeSignature || false;
|
|
269
316
|
let lambda = `${capture}(${thisArgParam}, ${funcArgs}) mutable -> ${funcReturnType} {\n`;
|
|
270
317
|
lambda += preamble;
|
|
271
318
|
lambda += paramsContent;
|
|
272
|
-
lambda +=
|
|
319
|
+
lambda += getBlockContentWithoutOpeningBrace(false);
|
|
273
320
|
let callable = lambda;
|
|
274
321
|
let method = "";
|
|
275
322
|
// Handle generator function
|
|
@@ -308,7 +355,7 @@ export function generateWrappedLambda(comps) {
|
|
|
308
355
|
method = `jspp::AnyValue::make_function`;
|
|
309
356
|
}
|
|
310
357
|
}
|
|
311
|
-
const funcName = context?.
|
|
358
|
+
const funcName = context?.functionName || node.name?.getText();
|
|
312
359
|
const hasName = !!funcName && funcName.length > 0;
|
|
313
360
|
let args = callable;
|
|
314
361
|
const isArrow = ts.isArrowFunction(node);
|
|
@@ -378,7 +425,7 @@ export function visitFunctionExpression(node, context) {
|
|
|
378
425
|
`${this.indent()}auto ${funcName} = std::make_shared<jspp::AnyValue>();\n`;
|
|
379
426
|
const lambda = this.generateWrappedLambda(this.generateLambdaComponents(funcExpr, {
|
|
380
427
|
...context,
|
|
381
|
-
|
|
428
|
+
functionName: funcName,
|
|
382
429
|
}, {
|
|
383
430
|
isAssignment: true,
|
|
384
431
|
capture: "[=]",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import ts from "typescript";
|
|
2
2
|
import { BUILTIN_OBJECTS, Scope } from "../../analysis/scope.js";
|
|
3
|
-
import { DeclarationType, DeclaredSymbols } from "../../ast/symbols.js";
|
|
3
|
+
import { DeclarationType, DeclaredSymbols, } from "../../ast/symbols.js";
|
|
4
4
|
import { CompilerError } from "../error.js";
|
|
5
5
|
import { CodeGenerator } from "./index.js";
|
|
6
6
|
/**
|
|
@@ -250,7 +250,7 @@ export function hoistDeclaration(decl, hoistedSymbols, scopeNode) {
|
|
|
250
250
|
: ts.isEnumDeclaration(decl)
|
|
251
251
|
? DeclarationType.enum
|
|
252
252
|
: DeclarationType.var;
|
|
253
|
-
const hoistName = (nameNode) => {
|
|
253
|
+
const hoistName = (nameNode, isFromDestructuring = false) => {
|
|
254
254
|
if (ts.isIdentifier(nameNode)) {
|
|
255
255
|
const name = nameNode.text;
|
|
256
256
|
if (hoistedSymbols.has(name)) {
|
|
@@ -269,6 +269,10 @@ export function hoistDeclaration(decl, hoistedSymbols, scopeNode) {
|
|
|
269
269
|
}
|
|
270
270
|
else {
|
|
271
271
|
// Add the symbol to the hoisted symbols
|
|
272
|
+
hoistedSymbols.add(name, {
|
|
273
|
+
type: declType,
|
|
274
|
+
});
|
|
275
|
+
// Get and update symbol features
|
|
272
276
|
if (ts.isFunctionDeclaration(decl) ||
|
|
273
277
|
(ts.isVariableDeclaration(decl) && decl.initializer &&
|
|
274
278
|
nameNode === decl.name &&
|
|
@@ -278,22 +282,23 @@ export function hoistDeclaration(decl, hoistedSymbols, scopeNode) {
|
|
|
278
282
|
const funcExpr = ts.isVariableDeclaration(decl)
|
|
279
283
|
? decl.initializer
|
|
280
284
|
: decl;
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
285
|
+
// Update features
|
|
286
|
+
hoistedSymbols.update(name, {
|
|
287
|
+
features: {
|
|
288
|
+
isAsync: this.isAsyncFunction(funcExpr),
|
|
289
|
+
isGenerator: this.isGeneratorFunction(funcExpr),
|
|
290
|
+
},
|
|
286
291
|
});
|
|
287
292
|
// Don't hoist declarations not used as a variable
|
|
288
293
|
// They will be called with their native lambda/value
|
|
289
294
|
if (!this.isDeclarationUsedAsValue(decl, scopeNode) &&
|
|
290
295
|
!this.isDeclarationUsedBeforeInitialization(name, scopeNode)) {
|
|
296
|
+
hoistedSymbols.update(name, {
|
|
297
|
+
checks: { skippedHoisting: true },
|
|
298
|
+
});
|
|
291
299
|
return "";
|
|
292
300
|
}
|
|
293
301
|
}
|
|
294
|
-
else {
|
|
295
|
-
hoistedSymbols.add(name, { type: declType });
|
|
296
|
-
}
|
|
297
302
|
}
|
|
298
303
|
const scope = this.getScopeForNode(decl);
|
|
299
304
|
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(name, scope);
|
|
@@ -304,6 +309,13 @@ export function hoistDeclaration(decl, hoistedSymbols, scopeNode) {
|
|
|
304
309
|
return `${this.indent()}auto ${name} = std::make_shared<jspp::AnyValue>(${initializer});\n`;
|
|
305
310
|
}
|
|
306
311
|
else {
|
|
312
|
+
if ((isLet || isConst) &&
|
|
313
|
+
!this.isDeclarationUsedBeforeInitialization(name, scopeNode) && !isFromDestructuring) {
|
|
314
|
+
hoistedSymbols.update(name, {
|
|
315
|
+
checks: { skippedHoisting: true },
|
|
316
|
+
});
|
|
317
|
+
return "";
|
|
318
|
+
}
|
|
307
319
|
return `${this.indent()}jspp::AnyValue ${name} = ${initializer};\n`;
|
|
308
320
|
}
|
|
309
321
|
}
|
|
@@ -311,7 +323,7 @@ export function hoistDeclaration(decl, hoistedSymbols, scopeNode) {
|
|
|
311
323
|
let code = "";
|
|
312
324
|
nameNode.elements.forEach((element) => {
|
|
313
325
|
if (ts.isBindingElement(element)) {
|
|
314
|
-
code += hoistName(element.name);
|
|
326
|
+
code += hoistName(element.name, true);
|
|
315
327
|
}
|
|
316
328
|
});
|
|
317
329
|
return code;
|
|
@@ -715,6 +727,33 @@ export function isVariableUsedWithoutDeclaration(name, root) {
|
|
|
715
727
|
visit(root);
|
|
716
728
|
return isUsedIllegally;
|
|
717
729
|
}
|
|
730
|
+
/**
|
|
731
|
+
* Checks if a node (usually a SourceFile) contains any top-level await expressions.
|
|
732
|
+
* This determines if the module function should be a coroutine.
|
|
733
|
+
*/
|
|
734
|
+
export function needsTopLevelAwait(node) {
|
|
735
|
+
let found = false;
|
|
736
|
+
const visitor = (n) => {
|
|
737
|
+
if (found)
|
|
738
|
+
return;
|
|
739
|
+
if (ts.isAwaitExpression(n)) {
|
|
740
|
+
found = true;
|
|
741
|
+
return;
|
|
742
|
+
}
|
|
743
|
+
if (ts.isForOfStatement(n) && n.awaitModifier) {
|
|
744
|
+
found = true;
|
|
745
|
+
return;
|
|
746
|
+
}
|
|
747
|
+
// Nested functions/classes have their own context
|
|
748
|
+
if (ts.isFunctionLike(n) || ts.isClassLike(n) ||
|
|
749
|
+
ts.isModuleDeclaration(n)) {
|
|
750
|
+
return;
|
|
751
|
+
}
|
|
752
|
+
ts.forEachChild(n, visitor);
|
|
753
|
+
};
|
|
754
|
+
ts.forEachChild(node, visitor);
|
|
755
|
+
return found;
|
|
756
|
+
}
|
|
718
757
|
/**
|
|
719
758
|
* Validates and filters function parameters, checking for illegal "this"
|
|
720
759
|
* and correctly positioned rest parameters.
|
|
@@ -744,3 +783,32 @@ export function validateFunctionParams(parameters) {
|
|
|
744
783
|
return p;
|
|
745
784
|
}) || [];
|
|
746
785
|
}
|
|
786
|
+
/**
|
|
787
|
+
* Traverses the AST upwards from the given node to find the nearest enclosing
|
|
788
|
+
* `FunctionDeclaration` node that contains a `ReturnStatement` ancestor.
|
|
789
|
+
*
|
|
790
|
+
* This function is useful for determining the function context in which a return
|
|
791
|
+
* statement appears. It starts from the provided node and walks up the parent
|
|
792
|
+
* chain, tracking whether a return statement has been encountered. Once a
|
|
793
|
+
* function declaration is found after encountering a return statement, it is returned.
|
|
794
|
+
*
|
|
795
|
+
* @param node - The starting TypeScript AST node.
|
|
796
|
+
* @returns The enclosing `ts.FunctionDeclaration` node if found, otherwise `undefined`.
|
|
797
|
+
*/
|
|
798
|
+
export function findEnclosingFunctionDeclarationFromReturnStatement(node) {
|
|
799
|
+
let current = node;
|
|
800
|
+
let foundReturn = false;
|
|
801
|
+
while (current) {
|
|
802
|
+
if (ts.isFunctionDeclaration(current)) {
|
|
803
|
+
if (foundReturn) {
|
|
804
|
+
return current;
|
|
805
|
+
}
|
|
806
|
+
break;
|
|
807
|
+
}
|
|
808
|
+
if (ts.isReturnStatement(current)) {
|
|
809
|
+
foundReturn = true;
|
|
810
|
+
}
|
|
811
|
+
current = current.parent;
|
|
812
|
+
}
|
|
813
|
+
return undefined;
|
|
814
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
import { DeclaredSymbols } from "../../ast/symbols.js";
|
|
3
|
+
import { generateDestructuring } from "./destructuring-handlers.js";
|
|
4
|
+
import { generateLambdaComponents, generateNativeLambda, generateWrappedLambda, } from "./function-handlers.js";
|
|
5
|
+
import { escapeString, findEnclosingFunctionDeclarationFromReturnStatement, generateUniqueExceptionName, generateUniqueName, getDeclaredSymbols, getDerefCode, getJsVarName, getReturnCommand, getScopeForNode, hoistDeclaration, indent, isAsyncFunction, isBuiltinObject, isDeclarationCalledAsFunction, isDeclarationUsedAsValue, isDeclarationUsedBeforeInitialization, isGeneratorFunction, isVariableUsedWithoutDeclaration, markSymbolAsInitialized, needsTopLevelAwait, prepareScopeSymbolsForVisit, validateFunctionParams, } from "./helpers.js";
|
|
6
|
+
import { visit } from "./visitor.js";
|
|
7
|
+
export class CodeGenerator {
|
|
8
|
+
indentationLevel = 0;
|
|
9
|
+
typeAnalyzer;
|
|
10
|
+
isTypescript = false;
|
|
11
|
+
moduleFunctionName;
|
|
12
|
+
globalThisVar;
|
|
13
|
+
uniqueNameCounter = 0;
|
|
14
|
+
isWasm = false;
|
|
15
|
+
wasmExports = [];
|
|
16
|
+
// visitor
|
|
17
|
+
visit = visit;
|
|
18
|
+
// helpers
|
|
19
|
+
getDeclaredSymbols = getDeclaredSymbols;
|
|
20
|
+
generateUniqueName = generateUniqueName;
|
|
21
|
+
generateUniqueExceptionName = generateUniqueExceptionName;
|
|
22
|
+
hoistDeclaration = hoistDeclaration;
|
|
23
|
+
getScopeForNode = getScopeForNode;
|
|
24
|
+
indent = indent;
|
|
25
|
+
escapeString = escapeString;
|
|
26
|
+
getJsVarName = getJsVarName;
|
|
27
|
+
getDerefCode = getDerefCode;
|
|
28
|
+
getReturnCommand = getReturnCommand;
|
|
29
|
+
isBuiltinObject = isBuiltinObject;
|
|
30
|
+
isGeneratorFunction = isGeneratorFunction;
|
|
31
|
+
isAsyncFunction = isAsyncFunction;
|
|
32
|
+
prepareScopeSymbolsForVisit = prepareScopeSymbolsForVisit;
|
|
33
|
+
markSymbolAsInitialized = markSymbolAsInitialized;
|
|
34
|
+
isDeclarationCalledAsFunction = isDeclarationCalledAsFunction;
|
|
35
|
+
isDeclarationUsedAsValue = isDeclarationUsedAsValue;
|
|
36
|
+
isDeclarationUsedBeforeInitialization = isDeclarationUsedBeforeInitialization;
|
|
37
|
+
isVariableUsedWithoutDeclaration = isVariableUsedWithoutDeclaration;
|
|
38
|
+
validateFunctionParams = validateFunctionParams;
|
|
39
|
+
generateDestructuring = generateDestructuring;
|
|
40
|
+
findEnclosingFunctionDeclarationFromReturnStatement = findEnclosingFunctionDeclarationFromReturnStatement;
|
|
41
|
+
// function handlers
|
|
42
|
+
generateLambdaComponents = generateLambdaComponents;
|
|
43
|
+
generateNativeLambda = generateNativeLambda;
|
|
44
|
+
generateWrappedLambda = generateWrappedLambda;
|
|
45
|
+
/**
|
|
46
|
+
* Main entry point for the code generation process.
|
|
47
|
+
*/
|
|
48
|
+
generate(ast, analyzer, isTypescript, isWasm = false) {
|
|
49
|
+
this.typeAnalyzer = analyzer;
|
|
50
|
+
this.isTypescript = isTypescript;
|
|
51
|
+
this.isWasm = isWasm;
|
|
52
|
+
this.wasmExports = [];
|
|
53
|
+
this.moduleFunctionName = this.generateUniqueName("__module_entry_point_", this.getDeclaredSymbols(ast));
|
|
54
|
+
this.globalThisVar = this.generateUniqueName("__this_val__", this.getDeclaredSymbols(ast));
|
|
55
|
+
const isAsyncModule = needsTopLevelAwait(ast);
|
|
56
|
+
const moduleReturnType = isAsyncModule
|
|
57
|
+
? "jspp::JsPromise"
|
|
58
|
+
: "jspp::AnyValue";
|
|
59
|
+
const moduleReturnCommand = isAsyncModule ? "co_return" : "return";
|
|
60
|
+
let declarations = `#include "jspp.hpp"\n#include "library/global_usings.hpp"\n`;
|
|
61
|
+
if (isWasm) {
|
|
62
|
+
declarations += `#include <emscripten.h>\n`;
|
|
63
|
+
}
|
|
64
|
+
declarations += `\n`;
|
|
65
|
+
// module function code
|
|
66
|
+
let moduleCode = `${moduleReturnType} ${this.moduleFunctionName}() {\n`;
|
|
67
|
+
this.indentationLevel++;
|
|
68
|
+
moduleCode +=
|
|
69
|
+
`${this.indent()}jspp::AnyValue ${this.globalThisVar} = global;\n`;
|
|
70
|
+
const context = {
|
|
71
|
+
currentScopeNode: ast,
|
|
72
|
+
isMainContext: true,
|
|
73
|
+
isInsideFunction: true,
|
|
74
|
+
isFunctionBody: true,
|
|
75
|
+
isInsideAsyncFunction: isAsyncModule,
|
|
76
|
+
globalScopeSymbols: new DeclaredSymbols(),
|
|
77
|
+
localScopeSymbols: new DeclaredSymbols(),
|
|
78
|
+
};
|
|
79
|
+
const generatedBody = this.visit(ast, context);
|
|
80
|
+
moduleCode += generatedBody;
|
|
81
|
+
moduleCode +=
|
|
82
|
+
`${this.indent()}${moduleReturnCommand} jspp::Constants::UNDEFINED;\n`;
|
|
83
|
+
this.indentationLevel--;
|
|
84
|
+
moduleCode += "}\n\n";
|
|
85
|
+
// Wasm Exports
|
|
86
|
+
let wasmGlobalPointers = "";
|
|
87
|
+
let wasmWrappers = "";
|
|
88
|
+
if (isWasm) {
|
|
89
|
+
for (const exp of this.wasmExports) {
|
|
90
|
+
const paramTypes = exp.params.map((_, i) => `jspp::AnyValue`);
|
|
91
|
+
const paramList = paramTypes.length > 0
|
|
92
|
+
? `, ${paramTypes.join(", ")}`
|
|
93
|
+
: "";
|
|
94
|
+
const pointerName = `__wasm_export_ptr_${exp.jsName}`;
|
|
95
|
+
wasmGlobalPointers +=
|
|
96
|
+
`std::function<jspp::AnyValue(jspp::AnyValue${paramList})> ${pointerName} = nullptr;\n`;
|
|
97
|
+
const wrapperParamList = exp.params.map((_, i) => `double p${i}`).join(", ");
|
|
98
|
+
const callArgsList = exp.params.map((_, i) => `jspp::AnyValue::make_number(p${i})`);
|
|
99
|
+
const callArgs = callArgsList.length > 0
|
|
100
|
+
? `, ${callArgsList.join(", ")}`
|
|
101
|
+
: "";
|
|
102
|
+
wasmWrappers += `extern "C" EMSCRIPTEN_KEEPALIVE\n`;
|
|
103
|
+
wasmWrappers +=
|
|
104
|
+
`double wasm_export_${exp.jsName}(${wrapperParamList}) {\n`;
|
|
105
|
+
wasmWrappers += ` if (!${pointerName}) return 0;\n`;
|
|
106
|
+
wasmWrappers +=
|
|
107
|
+
` auto res = ${pointerName}(global${callArgs});\n`;
|
|
108
|
+
wasmWrappers +=
|
|
109
|
+
` return jspp::Operators_Private::ToNumber(res);\n`;
|
|
110
|
+
wasmWrappers += `}\n\n`;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// main function code
|
|
114
|
+
let mainCode = "int main(int argc, char** argv) {\n";
|
|
115
|
+
this.indentationLevel++;
|
|
116
|
+
mainCode += `${this.indent()}try {\n`;
|
|
117
|
+
this.indentationLevel++;
|
|
118
|
+
mainCode += `${this.indent()}jspp::initialize_runtime();\n`;
|
|
119
|
+
mainCode += `${this.indent()}jspp::setup_process_argv(argc, argv);\n`;
|
|
120
|
+
if (isAsyncModule) {
|
|
121
|
+
mainCode +=
|
|
122
|
+
`${this.indent()}auto p = ${this.moduleFunctionName}();\n`;
|
|
123
|
+
mainCode +=
|
|
124
|
+
`${this.indent()}p.then(nullptr, [](jspp::AnyValue err) {\n`;
|
|
125
|
+
this.indentationLevel++;
|
|
126
|
+
mainCode +=
|
|
127
|
+
`${this.indent()}auto error = std::make_shared<jspp::AnyValue>(err);\n`;
|
|
128
|
+
this.indentationLevel++;
|
|
129
|
+
mainCode +=
|
|
130
|
+
`${this.indent()}console.call_own_property("error", std::span<const jspp::AnyValue>((const jspp::AnyValue[]){*error}, 1));\n`;
|
|
131
|
+
mainCode += `${this.indent()}std::exit(1);\n`;
|
|
132
|
+
this.indentationLevel--;
|
|
133
|
+
mainCode += `${this.indent()}});\n`;
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
mainCode += `${this.indent()}${this.moduleFunctionName}();\n`;
|
|
137
|
+
}
|
|
138
|
+
mainCode += `${this.indent()}jspp::Scheduler::instance().run();\n`;
|
|
139
|
+
this.indentationLevel--;
|
|
140
|
+
mainCode += `${this.indent()}} catch (const std::exception& ex) {\n`;
|
|
141
|
+
this.indentationLevel++;
|
|
142
|
+
mainCode +=
|
|
143
|
+
`${this.indent()}auto error = std::make_shared<jspp::AnyValue>(jspp::Exception::exception_to_any_value(ex));\n`;
|
|
144
|
+
this.indentationLevel++;
|
|
145
|
+
mainCode +=
|
|
146
|
+
`${this.indent()}console.call_own_property("error", std::span<const jspp::AnyValue>((const jspp::AnyValue[]){*error}, 1));\n`;
|
|
147
|
+
mainCode += `${this.indent()}return 1;\n`;
|
|
148
|
+
this.indentationLevel--;
|
|
149
|
+
mainCode += `${this.indent()}}\n`;
|
|
150
|
+
mainCode += `${this.indent()}return 0;\n`;
|
|
151
|
+
this.indentationLevel--;
|
|
152
|
+
mainCode += `}`;
|
|
153
|
+
return declarations + wasmGlobalPointers + wasmWrappers + moduleCode +
|
|
154
|
+
mainCode;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
@@ -11,6 +11,15 @@ export function visitIdentifier(node) {
|
|
|
11
11
|
return node.text;
|
|
12
12
|
}
|
|
13
13
|
export function visitNumericLiteral(node) {
|
|
14
|
+
const getOuterExpr = (n) => !ts.isParenthesizedExpression(n) &&
|
|
15
|
+
n.kind !== ts.SyntaxKind.FirstLiteralToken
|
|
16
|
+
? n
|
|
17
|
+
: getOuterExpr(n.parent);
|
|
18
|
+
const outerExpr = getOuterExpr(node);
|
|
19
|
+
if (!ts.isPropertyAccessExpression(outerExpr) &&
|
|
20
|
+
!ts.isElementAccessExpression(outerExpr)) {
|
|
21
|
+
return this.escapeString(node.text);
|
|
22
|
+
}
|
|
14
23
|
if (node.text === "0" || (node.text.startsWith("0.") &&
|
|
15
24
|
!node.text.substring(2).split("").some((c) => c !== "0")))
|
|
16
25
|
return "jspp::Constants::ZERO";
|