@ugo-studio/jspp 0.3.0 → 0.3.2
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/LICENSE +25 -25
- package/README.md +20 -12
- package/dist/cli/args.js +22 -0
- package/dist/cli/compiler.js +53 -0
- package/dist/cli/index.js +43 -107
- 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 +59 -0
- package/dist/cli/wasm.js +70 -0
- package/dist/index.js +17 -6
- package/dist/{analysis → interpreter/analysis}/scope.js +38 -3
- package/dist/{analysis → interpreter/analysis}/typeAnalyzer.js +563 -28
- package/dist/{core → interpreter/core}/codegen/class-handlers.js +1 -1
- package/dist/{core → interpreter/core}/codegen/control-flow-handlers.js +12 -11
- package/dist/{core → interpreter/core}/codegen/declaration-handlers.js +28 -9
- package/dist/{core → interpreter/core}/codegen/destructuring-handlers.js +9 -4
- package/dist/{core → interpreter/core}/codegen/expression-handlers.js +82 -88
- package/dist/{core → interpreter/core}/codegen/function-handlers.js +159 -46
- package/dist/{core → interpreter/core}/codegen/helpers.js +170 -25
- 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 +47 -7
- package/package.json +6 -4
- package/scripts/precompile-headers.ts +293 -50
- package/scripts/setup-compiler.ts +63 -63
- package/scripts/setup-emsdk.ts +114 -0
- package/src/prelude/any_value.cpp +888 -0
- package/src/prelude/any_value.hpp +29 -24
- package/src/prelude/{exception_helpers.hpp → exception.cpp} +53 -53
- package/src/prelude/exception.hpp +27 -27
- package/src/prelude/iterator_instantiations.hpp +10 -0
- package/src/prelude/{index.hpp → jspp.hpp} +13 -17
- package/src/prelude/library/array.cpp +191 -0
- package/src/prelude/library/array.hpp +5 -178
- package/src/prelude/library/boolean.cpp +30 -0
- package/src/prelude/library/boolean.hpp +14 -0
- package/src/prelude/library/console.cpp +125 -0
- package/src/prelude/library/console.hpp +9 -97
- package/src/prelude/library/error.cpp +100 -0
- package/src/prelude/library/error.hpp +8 -108
- package/src/prelude/library/function.cpp +69 -0
- package/src/prelude/library/function.hpp +6 -5
- package/src/prelude/library/global.cpp +98 -0
- package/src/prelude/library/global.hpp +12 -28
- package/src/prelude/library/global_usings.hpp +15 -0
- package/src/prelude/library/math.cpp +261 -0
- package/src/prelude/library/math.hpp +8 -288
- package/src/prelude/library/object.cpp +379 -0
- package/src/prelude/library/object.hpp +5 -267
- package/src/prelude/library/performance.cpp +21 -0
- package/src/prelude/library/performance.hpp +5 -20
- package/src/prelude/library/process.cpp +38 -0
- package/src/prelude/library/process.hpp +3 -31
- package/src/prelude/library/promise.cpp +131 -0
- package/src/prelude/library/promise.hpp +5 -116
- package/src/prelude/library/symbol.cpp +56 -0
- package/src/prelude/library/symbol.hpp +5 -46
- package/src/prelude/library/timer.cpp +88 -0
- package/src/prelude/library/timer.hpp +11 -87
- package/src/prelude/runtime.cpp +19 -0
- package/src/prelude/types.hpp +26 -20
- package/src/prelude/utils/access.hpp +123 -32
- package/src/prelude/utils/assignment_operators.hpp +119 -99
- package/src/prelude/utils/log_any_value/array.hpp +61 -40
- package/src/prelude/utils/log_any_value/function.hpp +39 -39
- package/src/prelude/utils/log_any_value/log_any_value.hpp +1 -1
- package/src/prelude/utils/log_any_value/object.hpp +60 -3
- package/src/prelude/utils/log_any_value/primitives.hpp +1 -1
- package/src/prelude/utils/operators.hpp +109 -94
- package/src/prelude/utils/operators_native.hpp +349 -0
- package/src/prelude/utils/well_known_symbols.hpp +24 -24
- package/src/prelude/values/array.cpp +1399 -0
- package/src/prelude/values/array.hpp +4 -0
- package/src/prelude/values/async_iterator.cpp +251 -0
- package/src/prelude/values/async_iterator.hpp +60 -32
- package/src/prelude/values/boolean.cpp +64 -0
- package/src/prelude/values/function.cpp +262 -0
- package/src/prelude/values/function.hpp +10 -30
- package/src/prelude/values/iterator.cpp +309 -0
- package/src/prelude/values/iterator.hpp +33 -64
- package/src/prelude/values/number.cpp +221 -0
- package/src/prelude/values/object.cpp +200 -0
- package/src/prelude/values/object.hpp +4 -0
- package/src/prelude/values/promise.cpp +479 -0
- package/src/prelude/values/promise.hpp +9 -2
- package/src/prelude/values/prototypes/array.hpp +46 -1348
- package/src/prelude/values/prototypes/async_iterator.hpp +19 -61
- package/src/prelude/values/prototypes/boolean.hpp +24 -0
- package/src/prelude/values/prototypes/function.hpp +7 -46
- package/src/prelude/values/prototypes/iterator.hpp +15 -191
- package/src/prelude/values/prototypes/number.hpp +30 -210
- package/src/prelude/values/prototypes/object.hpp +7 -23
- package/src/prelude/values/prototypes/promise.hpp +8 -186
- package/src/prelude/values/prototypes/string.hpp +28 -553
- package/src/prelude/values/prototypes/symbol.hpp +9 -70
- package/src/prelude/values/shape.hpp +52 -52
- package/src/prelude/values/string.cpp +485 -0
- package/src/prelude/values/symbol.cpp +89 -0
- package/src/prelude/values/symbol.hpp +101 -101
- 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 -86
- package/src/prelude/any_value_access.hpp +0 -170
- package/src/prelude/any_value_defines.hpp +0 -190
- package/src/prelude/any_value_helpers.hpp +0 -374
- package/src/prelude/utils/operators_primitive.hpp +0 -337
- package/src/prelude/values/helpers/array.hpp +0 -199
- package/src/prelude/values/helpers/async_iterator.hpp +0 -275
- package/src/prelude/values/helpers/function.hpp +0 -109
- package/src/prelude/values/helpers/iterator.hpp +0 -145
- package/src/prelude/values/helpers/object.hpp +0 -104
- package/src/prelude/values/helpers/promise.hpp +0 -254
- package/src/prelude/values/helpers/string.hpp +0 -37
- package/src/prelude/values/helpers/symbol.hpp +0 -21
- /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,6 +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
|
+
// "__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
|
+
// );
|
|
8
20
|
const isInsideGeneratorFunction = this.isGeneratorFunction(node);
|
|
9
21
|
const isInsideAsyncFunction = this.isAsyncFunction(node);
|
|
10
22
|
const isArrow = ts.isArrowFunction(node);
|
|
@@ -12,15 +24,27 @@ export function generateLambdaComponents(node, context, options) {
|
|
|
12
24
|
isInsideGeneratorFunction: isInsideGeneratorFunction,
|
|
13
25
|
isInsideAsyncFunction: isInsideAsyncFunction,
|
|
14
26
|
});
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
+
};
|
|
20
44
|
// Lambda arguments: regular functions use std::span for performance.
|
|
21
45
|
// Generators and async functions use std::vector to ensure they are safely copied into the coroutine frame.
|
|
22
46
|
const paramThisType = "jspp::AnyValue";
|
|
23
|
-
const paramArgsType =
|
|
47
|
+
const paramArgsType = isInsideGeneratorFunction || isInsideAsyncFunction
|
|
24
48
|
? "std::vector<jspp::AnyValue>"
|
|
25
49
|
: "std::span<const jspp::AnyValue>";
|
|
26
50
|
const globalScopeSymbols = this.prepareScopeSymbolsForVisit(context.globalScopeSymbols, context.localScopeSymbols);
|
|
@@ -29,7 +53,7 @@ export function generateLambdaComponents(node, context, options) {
|
|
|
29
53
|
isMainContext: false,
|
|
30
54
|
isInsideFunction: true,
|
|
31
55
|
isFunctionBody: false,
|
|
32
|
-
|
|
56
|
+
functionName: undefined,
|
|
33
57
|
globalScopeSymbols,
|
|
34
58
|
localScopeSymbols: new DeclaredSymbols(),
|
|
35
59
|
superClassVar: context.superClassVar,
|
|
@@ -59,7 +83,7 @@ export function generateLambdaComponents(node, context, options) {
|
|
|
59
83
|
nativePreamble +=
|
|
60
84
|
`${this.indent()}jspp::AnyValue ${this.globalThisVar} = ${finalThisParamName};\n`;
|
|
61
85
|
}
|
|
62
|
-
// Note: Arguments are now automatically copied into the coroutine frame because
|
|
86
|
+
// Note: Arguments are now automatically copied into the coroutine frame because
|
|
63
87
|
// the lambda parameter is passed by value (std::vector).
|
|
64
88
|
// We just need to define argsName as a reference to the parameter.
|
|
65
89
|
preamble +=
|
|
@@ -69,17 +93,33 @@ export function generateLambdaComponents(node, context, options) {
|
|
|
69
93
|
let nativeFuncArgs = "";
|
|
70
94
|
let nativeParamsContent = "";
|
|
71
95
|
this.validateFunctionParams(node.parameters).forEach((p, i) => {
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
: this.generateUniqueName(`__param_native_${i}_`, visitContext.localScopeSymbols);
|
|
96
|
+
const isIdentifier = ts.isIdentifier(p.name);
|
|
97
|
+
const name = isIdentifier ? p.name.text : this.generateUniqueName(`__param_native_${i}_`, visitContext.localScopeSymbols);
|
|
75
98
|
const defaultValue = p.initializer
|
|
76
99
|
? this.visit(p.initializer, visitContext)
|
|
77
100
|
: !!p.dotDotDotToken
|
|
78
101
|
? "jspp::AnyValue::make_array(std::vector<jspp::AnyValue>{})"
|
|
79
102
|
: "jspp::Constants::UNDEFINED";
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
103
|
+
let signatureName = name;
|
|
104
|
+
let needsTemp = !isIdentifier;
|
|
105
|
+
if (isIdentifier) {
|
|
106
|
+
const scope = this.getScopeForNode(p);
|
|
107
|
+
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(name, scope);
|
|
108
|
+
if (typeInfo?.needsHeapAllocation) {
|
|
109
|
+
needsTemp = true;
|
|
110
|
+
signatureName = this.generateUniqueName(`__${name}_param_`, visitContext.localScopeSymbols);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
nativeFuncArgs += `, jspp::AnyValue ${signatureName} = ${defaultValue}`;
|
|
114
|
+
if (needsTemp) {
|
|
115
|
+
if (isIdentifier) {
|
|
116
|
+
nativeParamsContent +=
|
|
117
|
+
`${this.indent()}auto ${name} = std::make_shared<jspp::AnyValue>(${signatureName});\n`;
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
nativeParamsContent += this.generateDestructuring(p.name, signatureName, visitContext) +
|
|
121
|
+
";\n";
|
|
122
|
+
}
|
|
83
123
|
}
|
|
84
124
|
});
|
|
85
125
|
// Extract lambda parameters from arguments span/vector
|
|
@@ -97,12 +137,11 @@ export function generateLambdaComponents(node, context, options) {
|
|
|
97
137
|
checks: { initialized: true },
|
|
98
138
|
});
|
|
99
139
|
const scope = this.getScopeForNode(p);
|
|
100
|
-
const typeInfo = this.typeAnalyzer.scopeManager
|
|
101
|
-
.lookupFromScope(name, scope);
|
|
140
|
+
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(name, scope);
|
|
102
141
|
// Handle rest parameters
|
|
103
142
|
if (!!p.dotDotDotToken) {
|
|
104
143
|
const initValue = `jspp::AnyValue::make_array(${argsName}.subspan(${i}))`;
|
|
105
|
-
if (typeInfo
|
|
144
|
+
if (typeInfo?.needsHeapAllocation) {
|
|
106
145
|
paramsCode +=
|
|
107
146
|
`${this.indent()}auto ${name} = std::make_shared<jspp::AnyValue>(${initValue});\n`;
|
|
108
147
|
}
|
|
@@ -114,7 +153,7 @@ export function generateLambdaComponents(node, context, options) {
|
|
|
114
153
|
}
|
|
115
154
|
// Normal parameter
|
|
116
155
|
const initValue = `${argsName}.size() > ${i} ? ${argsName}[${i}] : ${defaultValue}`;
|
|
117
|
-
if (typeInfo
|
|
156
|
+
if (typeInfo?.needsHeapAllocation) {
|
|
118
157
|
paramsCode +=
|
|
119
158
|
`${this.indent()}auto ${name} = std::make_shared<jspp::AnyValue>(${initValue});\n`;
|
|
120
159
|
}
|
|
@@ -131,14 +170,43 @@ export function generateLambdaComponents(node, context, options) {
|
|
|
131
170
|
const initValue = `${argsName}.size() > ${i} ? ${argsName}[${i}] : ${defaultValue}`;
|
|
132
171
|
paramsCode +=
|
|
133
172
|
`${this.indent()}auto ${tempName} = ${initValue};\n`;
|
|
134
|
-
paramsCode +=
|
|
173
|
+
paramsCode +=
|
|
174
|
+
this.generateDestructuring(p.name, tempName, visitContext) +
|
|
175
|
+
";\n";
|
|
135
176
|
}
|
|
136
177
|
});
|
|
137
178
|
return paramsCode;
|
|
138
179
|
};
|
|
139
180
|
// Generate params and function body
|
|
140
181
|
let paramsContent = "";
|
|
141
|
-
let
|
|
182
|
+
let getBlockContentWithoutOpeningBrace = () => "";
|
|
183
|
+
// Generate `arguments` variable if it used in the function body
|
|
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
|
+
// }
|
|
142
210
|
if (node.body) {
|
|
143
211
|
if (ts.isBlock(node.body)) {
|
|
144
212
|
// Hoist var declarations in the function body
|
|
@@ -148,38 +216,65 @@ export function generateLambdaComponents(node, context, options) {
|
|
|
148
216
|
preamble += hoistCode;
|
|
149
217
|
nativePreamble += hoistCode;
|
|
150
218
|
});
|
|
219
|
+
// Hoist parameters (especially for destructuring)
|
|
220
|
+
node.parameters.forEach((p) => {
|
|
221
|
+
if (!ts.isIdentifier(p.name)) {
|
|
222
|
+
const hoistCode = this.hoistDeclaration(p, visitContext.localScopeSymbols, node.body);
|
|
223
|
+
preamble += hoistCode;
|
|
224
|
+
nativePreamble += hoistCode;
|
|
225
|
+
}
|
|
226
|
+
});
|
|
151
227
|
this.indentationLevel++;
|
|
152
|
-
paramsContent
|
|
228
|
+
paramsContent += generateParamsBuilder();
|
|
153
229
|
this.indentationLevel--;
|
|
154
230
|
// The block visitor already adds braces, so we need to remove the opening brace to inject the preamble and param extraction.
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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
|
+
};
|
|
163
250
|
}
|
|
164
251
|
else {
|
|
165
252
|
this.indentationLevel++;
|
|
166
|
-
paramsContent
|
|
167
|
-
|
|
168
|
-
|
|
253
|
+
paramsContent += generateParamsBuilder();
|
|
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, {
|
|
169
260
|
...visitContext,
|
|
170
261
|
isMainContext: false,
|
|
171
262
|
isInsideFunction: true,
|
|
172
263
|
isFunctionBody: false,
|
|
173
|
-
isInsideGeneratorFunction
|
|
174
|
-
isInsideAsyncFunction
|
|
264
|
+
isInsideGeneratorFunction,
|
|
265
|
+
isInsideAsyncFunction,
|
|
266
|
+
isInsideNativeLambda,
|
|
175
267
|
})};\n`;
|
|
268
|
+
this.indentationLevel--;
|
|
269
|
+
code += `${this.indent()}}`;
|
|
270
|
+
this.indentationLevel = currentIndentationLevel;
|
|
271
|
+
return code;
|
|
272
|
+
};
|
|
176
273
|
this.indentationLevel--;
|
|
177
|
-
blockContentWithoutOpeningBrace += `${this.indent()}}`;
|
|
178
274
|
}
|
|
179
275
|
}
|
|
180
276
|
else {
|
|
181
|
-
|
|
182
|
-
`${returnCommand} jspp::Constants::UNDEFINED; }\n`;
|
|
277
|
+
getBlockContentWithoutOpeningBrace = () => `${returnCommand} jspp::Constants::UNDEFINED; }`;
|
|
183
278
|
}
|
|
184
279
|
return {
|
|
185
280
|
node,
|
|
@@ -189,37 +284,39 @@ export function generateLambdaComponents(node, context, options) {
|
|
|
189
284
|
thisArgParam,
|
|
190
285
|
funcArgs,
|
|
191
286
|
nativeFuncArgs,
|
|
192
|
-
|
|
287
|
+
getFuncReturnType,
|
|
193
288
|
preamble,
|
|
194
289
|
nativePreamble,
|
|
195
290
|
paramsContent,
|
|
196
291
|
nativeParamsContent,
|
|
197
|
-
|
|
292
|
+
getBlockContentWithoutOpeningBrace,
|
|
198
293
|
isInsideGeneratorFunction,
|
|
199
294
|
isInsideAsyncFunction,
|
|
200
295
|
};
|
|
201
296
|
}
|
|
202
297
|
export function generateNativeLambda(comps) {
|
|
203
|
-
const { options, thisArgParam, nativeFuncArgs,
|
|
298
|
+
const { options, thisArgParam, nativeFuncArgs, getFuncReturnType, nativePreamble, nativeParamsContent, getBlockContentWithoutOpeningBrace, } = comps;
|
|
204
299
|
const capture = options?.capture || "[=]";
|
|
205
300
|
const nativeName = options?.nativeName;
|
|
206
301
|
const selfParam = nativeName ? `this auto&& ${nativeName}, ` : "";
|
|
207
302
|
const mutableLabel = nativeName ? "" : "mutable ";
|
|
303
|
+
const funcReturnType = getFuncReturnType(true);
|
|
208
304
|
let lambda = `${capture}(${selfParam}${thisArgParam}${nativeFuncArgs}) ${mutableLabel}-> ${funcReturnType} {\n`;
|
|
209
305
|
lambda += nativePreamble;
|
|
210
306
|
lambda += nativeParamsContent;
|
|
211
|
-
lambda +=
|
|
307
|
+
lambda += getBlockContentWithoutOpeningBrace(true);
|
|
212
308
|
return lambda;
|
|
213
309
|
}
|
|
214
310
|
export function generateWrappedLambda(comps) {
|
|
215
|
-
const { node, context, options, thisArgParam, funcArgs,
|
|
311
|
+
const { node, context, options, thisArgParam, funcArgs, getFuncReturnType, preamble, paramsContent, getBlockContentWithoutOpeningBrace, isInsideGeneratorFunction, isInsideAsyncFunction, } = comps;
|
|
216
312
|
const capture = options?.capture || "[=]";
|
|
217
313
|
const isAssignment = options?.isAssignment || false;
|
|
314
|
+
const funcReturnType = getFuncReturnType(false);
|
|
218
315
|
const noTypeSignature = options?.noTypeSignature || false;
|
|
219
316
|
let lambda = `${capture}(${thisArgParam}, ${funcArgs}) mutable -> ${funcReturnType} {\n`;
|
|
220
317
|
lambda += preamble;
|
|
221
318
|
lambda += paramsContent;
|
|
222
|
-
lambda +=
|
|
319
|
+
lambda += getBlockContentWithoutOpeningBrace(false);
|
|
223
320
|
let callable = lambda;
|
|
224
321
|
let method = "";
|
|
225
322
|
// Handle generator function
|
|
@@ -258,7 +355,7 @@ export function generateWrappedLambda(comps) {
|
|
|
258
355
|
method = `jspp::AnyValue::make_function`;
|
|
259
356
|
}
|
|
260
357
|
}
|
|
261
|
-
const funcName = context?.
|
|
358
|
+
const funcName = context?.functionName || node.name?.getText();
|
|
262
359
|
const hasName = !!funcName && funcName.length > 0;
|
|
263
360
|
let args = callable;
|
|
264
361
|
const isArrow = ts.isArrowFunction(node);
|
|
@@ -283,7 +380,23 @@ export function generateWrappedLambda(comps) {
|
|
|
283
380
|
args += `, "${funcName}"`;
|
|
284
381
|
}
|
|
285
382
|
}
|
|
286
|
-
|
|
383
|
+
let prototypeExpr = "";
|
|
384
|
+
if (isInsideGeneratorFunction) {
|
|
385
|
+
if (isInsideAsyncFunction) {
|
|
386
|
+
prototypeExpr =
|
|
387
|
+
'::AsyncGeneratorFunction.get_own_property("prototype")';
|
|
388
|
+
}
|
|
389
|
+
else {
|
|
390
|
+
prototypeExpr = '::GeneratorFunction.get_own_property("prototype")';
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
else if (isInsideAsyncFunction) {
|
|
394
|
+
prototypeExpr = '::AsyncFunction.get_own_property("prototype")';
|
|
395
|
+
}
|
|
396
|
+
else {
|
|
397
|
+
prototypeExpr = '::Function.get_own_property("prototype")';
|
|
398
|
+
}
|
|
399
|
+
const fullExpression = `${method}(${args}).set_prototype(${prototypeExpr})`;
|
|
287
400
|
if (ts.isFunctionDeclaration(node) && !isAssignment && node.name) {
|
|
288
401
|
const funcName = node.name?.getText();
|
|
289
402
|
return `${this.indent()}auto ${funcName} = ${fullExpression};\n`;
|
|
@@ -312,7 +425,7 @@ export function visitFunctionExpression(node, context) {
|
|
|
312
425
|
`${this.indent()}auto ${funcName} = std::make_shared<jspp::AnyValue>();\n`;
|
|
313
426
|
const lambda = this.generateWrappedLambda(this.generateLambdaComponents(funcExpr, {
|
|
314
427
|
...context,
|
|
315
|
-
|
|
428
|
+
functionName: funcName,
|
|
316
429
|
}, {
|
|
317
430
|
isAssignment: true,
|
|
318
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
|
/**
|
|
@@ -161,6 +161,9 @@ export function getJsVarName(node) {
|
|
|
161
161
|
* @returns The C++ code for accessing the variable's value.
|
|
162
162
|
*/
|
|
163
163
|
export function getDerefCode(nodeText, varName, context, typeInfo) {
|
|
164
|
+
if (typeInfo?.isBuiltin) {
|
|
165
|
+
return nodeText;
|
|
166
|
+
}
|
|
164
167
|
// Make sure varName is incased in quotes
|
|
165
168
|
if (!varName.startsWith('"'))
|
|
166
169
|
varName = '"' + varName;
|
|
@@ -230,22 +233,24 @@ export function getReturnCommand(context) {
|
|
|
230
233
|
* @throws CompilerError if a duplicate declaration is found.
|
|
231
234
|
*/
|
|
232
235
|
export function hoistDeclaration(decl, hoistedSymbols, scopeNode) {
|
|
233
|
-
const isLet = ts.isVariableDeclaration(decl) &&
|
|
234
|
-
(decl.parent.flags & (ts.NodeFlags.Let)) !== 0;
|
|
236
|
+
const isLet = ts.isParameter(decl) || (ts.isVariableDeclaration(decl) &&
|
|
237
|
+
(decl.parent.flags & (ts.NodeFlags.Let)) !== 0);
|
|
235
238
|
const isConst = ts.isVariableDeclaration(decl) &&
|
|
236
239
|
(decl.parent.flags & (ts.NodeFlags.Const)) !== 0;
|
|
237
|
-
const declType =
|
|
240
|
+
const declType = ts.isParameter(decl)
|
|
238
241
|
? DeclarationType.let
|
|
239
|
-
:
|
|
240
|
-
? DeclarationType.
|
|
241
|
-
:
|
|
242
|
-
? DeclarationType.
|
|
243
|
-
: ts.
|
|
244
|
-
? DeclarationType.
|
|
245
|
-
: ts.
|
|
246
|
-
? DeclarationType.
|
|
247
|
-
:
|
|
248
|
-
|
|
242
|
+
: isLet
|
|
243
|
+
? DeclarationType.let
|
|
244
|
+
: isConst
|
|
245
|
+
? DeclarationType.const
|
|
246
|
+
: ts.isFunctionDeclaration(decl)
|
|
247
|
+
? DeclarationType.function
|
|
248
|
+
: ts.isClassDeclaration(decl)
|
|
249
|
+
? DeclarationType.class
|
|
250
|
+
: ts.isEnumDeclaration(decl)
|
|
251
|
+
? DeclarationType.enum
|
|
252
|
+
: DeclarationType.var;
|
|
253
|
+
const hoistName = (nameNode, isFromDestructuring = false) => {
|
|
249
254
|
if (ts.isIdentifier(nameNode)) {
|
|
250
255
|
const name = nameNode.text;
|
|
251
256
|
if (hoistedSymbols.has(name)) {
|
|
@@ -264,40 +269,53 @@ export function hoistDeclaration(decl, hoistedSymbols, scopeNode) {
|
|
|
264
269
|
}
|
|
265
270
|
else {
|
|
266
271
|
// Add the symbol to the hoisted symbols
|
|
272
|
+
hoistedSymbols.add(name, {
|
|
273
|
+
type: declType,
|
|
274
|
+
});
|
|
275
|
+
// Get and update symbol features
|
|
267
276
|
if (ts.isFunctionDeclaration(decl) ||
|
|
268
277
|
(ts.isVariableDeclaration(decl) && decl.initializer &&
|
|
269
278
|
nameNode === decl.name &&
|
|
270
279
|
(ts.isArrowFunction(decl.initializer) ||
|
|
271
|
-
ts.isFunctionExpression(decl.initializer)
|
|
280
|
+
(ts.isFunctionExpression(decl.initializer) &&
|
|
281
|
+
!decl.initializer.name)))) {
|
|
272
282
|
const funcExpr = ts.isVariableDeclaration(decl)
|
|
273
283
|
? decl.initializer
|
|
274
284
|
: decl;
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
285
|
+
// Update features
|
|
286
|
+
hoistedSymbols.update(name, {
|
|
287
|
+
features: {
|
|
288
|
+
isAsync: this.isAsyncFunction(funcExpr),
|
|
289
|
+
isGenerator: this.isGeneratorFunction(funcExpr),
|
|
290
|
+
},
|
|
280
291
|
});
|
|
281
292
|
// Don't hoist declarations not used as a variable
|
|
282
293
|
// They will be called with their native lambda/value
|
|
283
294
|
if (!this.isDeclarationUsedAsValue(decl, scopeNode) &&
|
|
284
295
|
!this.isDeclarationUsedBeforeInitialization(name, scopeNode)) {
|
|
296
|
+
hoistedSymbols.update(name, {
|
|
297
|
+
checks: { skippedHoisting: true },
|
|
298
|
+
});
|
|
285
299
|
return "";
|
|
286
300
|
}
|
|
287
301
|
}
|
|
288
|
-
else {
|
|
289
|
-
hoistedSymbols.add(name, { type: declType });
|
|
290
|
-
}
|
|
291
302
|
}
|
|
292
303
|
const scope = this.getScopeForNode(decl);
|
|
293
304
|
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(name, scope);
|
|
294
305
|
const initializer = isLet || isConst || ts.isClassDeclaration(decl)
|
|
295
306
|
? "jspp::Constants::UNINITIALIZED"
|
|
296
307
|
: "jspp::Constants::UNDEFINED";
|
|
297
|
-
if (typeInfo
|
|
308
|
+
if (typeInfo?.needsHeapAllocation) {
|
|
298
309
|
return `${this.indent()}auto ${name} = std::make_shared<jspp::AnyValue>(${initializer});\n`;
|
|
299
310
|
}
|
|
300
311
|
else {
|
|
312
|
+
if ((isLet || isConst) &&
|
|
313
|
+
!this.isDeclarationUsedBeforeInitialization(name, scopeNode) && !isFromDestructuring) {
|
|
314
|
+
hoistedSymbols.update(name, {
|
|
315
|
+
checks: { skippedHoisting: true },
|
|
316
|
+
});
|
|
317
|
+
return "";
|
|
318
|
+
}
|
|
301
319
|
return `${this.indent()}jspp::AnyValue ${name} = ${initializer};\n`;
|
|
302
320
|
}
|
|
303
321
|
}
|
|
@@ -305,7 +323,7 @@ export function hoistDeclaration(decl, hoistedSymbols, scopeNode) {
|
|
|
305
323
|
let code = "";
|
|
306
324
|
nameNode.elements.forEach((element) => {
|
|
307
325
|
if (ts.isBindingElement(element)) {
|
|
308
|
-
code += hoistName(element.name);
|
|
326
|
+
code += hoistName(element.name, true);
|
|
309
327
|
}
|
|
310
328
|
});
|
|
311
329
|
return code;
|
|
@@ -638,6 +656,104 @@ export function isDeclarationUsedBeforeInitialization(name, root) {
|
|
|
638
656
|
visit(root);
|
|
639
657
|
return isUsedBefore;
|
|
640
658
|
}
|
|
659
|
+
/**
|
|
660
|
+
* Checks if a variable name is used within a node subtree without a preceding declaration.
|
|
661
|
+
* Returns true if:
|
|
662
|
+
* 1. The variable is used but never declared within the provided root node.
|
|
663
|
+
* 2. The variable is used lexically before its declaration within the root node.
|
|
664
|
+
*
|
|
665
|
+
* @param name The variable name to check.
|
|
666
|
+
* @param root The root node to search within.
|
|
667
|
+
* @returns True if used without/before declaration.
|
|
668
|
+
*/
|
|
669
|
+
export function isVariableUsedWithoutDeclaration(name, root) {
|
|
670
|
+
let declPos = -1;
|
|
671
|
+
let foundDecl = false;
|
|
672
|
+
// 1. Find the declaration position within this root
|
|
673
|
+
const findDecl = (node) => {
|
|
674
|
+
if (foundDecl)
|
|
675
|
+
return;
|
|
676
|
+
if ((ts.isFunctionDeclaration(node) ||
|
|
677
|
+
ts.isClassDeclaration(node) ||
|
|
678
|
+
ts.isVariableDeclaration(node) ||
|
|
679
|
+
ts.isEnumDeclaration(node) ||
|
|
680
|
+
ts.isParameter(node)) &&
|
|
681
|
+
node.name && ts.isIdentifier(node.name) && node.name.text === name) {
|
|
682
|
+
declPos = node.getStart();
|
|
683
|
+
foundDecl = true;
|
|
684
|
+
}
|
|
685
|
+
else {
|
|
686
|
+
ts.forEachChild(node, findDecl);
|
|
687
|
+
}
|
|
688
|
+
};
|
|
689
|
+
findDecl(root);
|
|
690
|
+
let isUsedIllegally = false;
|
|
691
|
+
// 2. Traverse for usages
|
|
692
|
+
const visit = (node) => {
|
|
693
|
+
if (isUsedIllegally)
|
|
694
|
+
return;
|
|
695
|
+
if (ts.isIdentifier(node) && node.text === name) {
|
|
696
|
+
const parent = node.parent;
|
|
697
|
+
// Skip if this identifier IS the declaration site
|
|
698
|
+
const isDeclarationSite = (ts.isFunctionDeclaration(parent) ||
|
|
699
|
+
ts.isVariableDeclaration(parent) ||
|
|
700
|
+
ts.isClassDeclaration(parent) ||
|
|
701
|
+
ts.isMethodDeclaration(parent) ||
|
|
702
|
+
ts.isEnumDeclaration(parent) ||
|
|
703
|
+
ts.isEnumMember(parent) ||
|
|
704
|
+
ts.isParameter(parent) ||
|
|
705
|
+
ts.isImportSpecifier(parent)) &&
|
|
706
|
+
parent.name === node;
|
|
707
|
+
// Skip if it's a property access (e.g., obj.name)
|
|
708
|
+
const isProperty = (ts.isPropertyAccessExpression(parent) &&
|
|
709
|
+
parent.name === node) ||
|
|
710
|
+
(ts.isPropertyAssignment(parent) && parent.name === node);
|
|
711
|
+
if (!isDeclarationSite && !isProperty) {
|
|
712
|
+
const usagePos = node.getStart();
|
|
713
|
+
// Case 1: Used but not declared in this node
|
|
714
|
+
if (!foundDecl) {
|
|
715
|
+
isUsedIllegally = true;
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
718
|
+
// Case 2: Used lexically before declaration
|
|
719
|
+
if (usagePos < declPos) {
|
|
720
|
+
isUsedIllegally = true;
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
ts.forEachChild(node, visit);
|
|
726
|
+
};
|
|
727
|
+
visit(root);
|
|
728
|
+
return isUsedIllegally;
|
|
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
|
+
}
|
|
641
757
|
/**
|
|
642
758
|
* Validates and filters function parameters, checking for illegal "this"
|
|
643
759
|
* and correctly positioned rest parameters.
|
|
@@ -667,3 +783,32 @@ export function validateFunctionParams(parameters) {
|
|
|
667
783
|
return p;
|
|
668
784
|
}) || [];
|
|
669
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
|
+
}
|