@ugo-studio/jspp 0.1.5 → 0.1.7

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 CHANGED
@@ -1,21 +1,25 @@
1
- MIT License
2
-
3
- Copyright (c) 2025 Emmanuel
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the “Software”), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ # License
2
+
3
+ JSPP is licensed under the [MIT License](#mit-license).
4
+
5
+ ## MIT License
6
+
7
+ Copyright (c) 2025 Emmanuel
8
+
9
+ Permission is hereby granted, free of charge, to any person obtaining a copy
10
+ of this software and associated documentation files (the "Software"), to deal
11
+ in the Software without restriction, including without limitation the rights
12
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
+ copies of the Software, and to permit persons to whom the Software is
14
+ furnished to do so, subject to the following conditions:
15
+
16
+ The above copyright notice and this permission notice shall be included in all
17
+ copies or substantial portions of the Software.
18
+
19
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25
+ SOFTWARE.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # JSPP (JavaScript++)
2
2
 
3
- [![Build Status](https://img.shields.io/badge/build-passing-brightgreen)](https://github.com/ugo-studio/jspp)
3
+ [![CI](https://github.com/ugo-studio/jspp/actions/workflows/ci.yml/badge.svg)](https://github.com/ugo-studio/jspp/actions/workflows/ci.yml)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
5
 
6
6
  **JSPP is a modern, experimental transpiler that converts JavaScript code into high-performance, standard C++23.**
@@ -33,24 +33,10 @@ JSPP reserves certain keywords to avoid conflicts with the generated C++ code an
33
33
 
34
34
  - `std`: Reserved to prevent conflicts with the C++ standard library namespace.
35
35
  - `jspp`: Reserved for internal use by the JSPP transpiler.
36
- - `co_yield`: Reserved to prevent conflicts with the c++ generator functions.
37
- - `co_return`: Reserved to prevent conflicts with the c++ generator functions.
36
+ - `co_yield`,`co_return`,`co_await`: Reserved to prevent conflicts with the c++ couroutine keywords.
38
37
 
39
38
  Using these keywords as variable names will result in a `SyntaxError`.
40
39
 
41
- ## How It Works
42
-
43
- The transpilation process is a classic three-stage pipeline:
44
-
45
- 1. **Parsing:** The incoming JavaScript code is parsed into an Abstract Syntax Tree (AST) using the powerful TypeScript compiler API. This gives us a structured, traversable representation of the code.
46
-
47
- 2. **Analysis:** The `TypeAnalyzer` traverses the AST. While it doesn't perform traditional static type checking, it plays a crucial role in understanding the code's structure. It identifies scopes (global, function, block) and detects when variables are "captured" by closures.
48
-
49
- 3. **Code Generation:** The `CodeGenerator` performs a final traversal of the AST. It translates each node into its C++ equivalent.
50
- - All variables are declared as `std::shared_ptr<AnyValue>` (where `AnyValue` uses a [Tagged Union](https://en.wikipedia.org/wiki/Tagged_union) method for managing dynamic types like JavaScript). This approach elegantly mimics JavaScript's dynamic types and reference-based memory model.
51
- - Closures are implemented as C++ lambdas that capture `shared_ptr`s by value, ensuring variable lifetimes are correctly extended beyond their original scope.
52
- - The entire script is wrapped into a single `main` function, with hoisting logic carefully replicated to ensure correct execution order.
53
-
54
40
  ## Installation
55
41
 
56
42
  To use JSPP as a command-line tool, install it globally via npm:
@@ -26,4 +26,7 @@ export class DeclaredSymbols {
26
26
  return this.symbols.set(name, newValue);
27
27
  }
28
28
  }
29
+ toSet() {
30
+ return new Set(this.symbols.keys());
31
+ }
29
32
  }
package/dist/cli.js CHANGED
@@ -27,8 +27,11 @@ async function main() {
27
27
  console.log(`${COLORS.bold}JSPP Compiler${COLORS.reset} ${COLORS.dim}v${pkg.version}${COLORS.reset}`);
28
28
  console.log(`Mode: ${isRelease ? COLORS.green : COLORS.yellow}${mode.toUpperCase()}${COLORS.reset}\n`);
29
29
  const flags = isRelease
30
- ? ["-O3", "-DNDEBUG", "-Wa,-mbig-obj"]
31
- : ["-O0", "-Wa,-mbig-obj"];
30
+ ? ["-O3", "-DNDEBUG"]
31
+ : ["-O0"];
32
+ if (process.platform === "win32") {
33
+ flags.push("-Wa,-mbig-obj");
34
+ }
32
35
  const pchDir = path.resolve(process.cwd(), "prelude-build", mode);
33
36
  const spinner = new Spinner("Initializing...");
34
37
  try {
@@ -42,7 +45,7 @@ async function main() {
42
45
  // Ensure directory for cpp file exists (should exist as it's source dir, but for safety if we change logic)
43
46
  await fs.mkdir(path.dirname(cppFilePath), { recursive: true });
44
47
  await fs.writeFile(cppFilePath, cppCode);
45
- spinner.succeed(`Generated ${COLORS.dim}${path.basename(cppFilePath)}${COLORS.reset}`);
48
+ spinner.succeed(`Generated cpp`);
46
49
  // 2. Precompiled Header Check
47
50
  spinner.text = "Checking precompiled headers...";
48
51
  spinner.start();
@@ -76,7 +79,7 @@ async function main() {
76
79
  spinner.succeed("Precompiled headers updated");
77
80
  }
78
81
  else {
79
- spinner.succeed("Precompiled headers up-to-date");
82
+ spinner.succeed("Precompiled headers");
80
83
  }
81
84
  // 3. Compilation Phase
82
85
  spinner.text = `Compiling binary...`;
@@ -70,7 +70,7 @@ export function visitForStatement(node, context) {
70
70
  ...context,
71
71
  currentLabel: undefined,
72
72
  isFunctionBody: false,
73
- });
73
+ }).trim();
74
74
  if (ts.isBlock(node.statement)) {
75
75
  let blockContent = statementCode.substring(1, statementCode.length - 2); // remove curly braces
76
76
  if (context.currentLabel) {
@@ -218,14 +218,27 @@ export function visitForOfStatement(node, context) {
218
218
  const iterator = this.generateUniqueName("__iter", declaredSymbols);
219
219
  const nextFunc = this.generateUniqueName("__next_func", declaredSymbols);
220
220
  const nextRes = this.generateUniqueName("__next_res", declaredSymbols);
221
+ const isAwait = forOf.awaitModifier !== undefined;
221
222
  const varName = this.getJsVarName(forOf.expression);
222
223
  code += `${this.indent()}auto ${iterableRef} = ${derefIterable};\n`;
223
- code +=
224
- `${this.indent()}auto ${iterator} = jspp::Access::get_object_value_iterator(${iterableRef}, ${varName});\n`;
224
+ if (isAwait) {
225
+ code +=
226
+ `${this.indent()}auto ${iterator} = jspp::Access::get_async_object_value_iterator(${iterableRef}, ${varName});\n`;
227
+ }
228
+ else {
229
+ code +=
230
+ `${this.indent()}auto ${iterator} = jspp::Access::get_object_value_iterator(${iterableRef}, ${varName});\n`;
231
+ }
225
232
  code +=
226
233
  `${this.indent()}auto ${nextFunc} = ${iterator}.get_own_property("next");\n`;
227
- code +=
228
- `${this.indent()}auto ${nextRes} = ${nextFunc}.call(${iterator}, {}, "next");\n`;
234
+ if (isAwait) {
235
+ code +=
236
+ `${this.indent()}auto ${nextRes} = co_await ${nextFunc}.call(${iterator}, {}, "next");\n`;
237
+ }
238
+ else {
239
+ code +=
240
+ `${this.indent()}auto ${nextRes} = ${nextFunc}.call(${iterator}, {}, "next");\n`;
241
+ }
229
242
  code +=
230
243
  `${this.indent()}while (!is_truthy(${nextRes}.get_own_property("done"))) {\n`;
231
244
  this.indentationLevel++;
@@ -239,8 +252,14 @@ export function visitForOfStatement(node, context) {
239
252
  if (context.currentLabel) {
240
253
  code += `${this.indent()}${context.currentLabel}_continue:;\n`;
241
254
  }
242
- code +=
243
- `${this.indent()}${nextRes} = ${nextFunc}.call(${iterator}, {}, "next");\n`;
255
+ if (isAwait) {
256
+ code +=
257
+ `${this.indent()}${nextRes} = co_await ${nextFunc}.call(${iterator}, {}, "next");\n`;
258
+ }
259
+ else {
260
+ code +=
261
+ `${this.indent()}${nextRes} = ${nextFunc}.call(${iterator}, {}, "next");\n`;
262
+ }
244
263
  this.indentationLevel--;
245
264
  code += `${this.indent()}}\n`;
246
265
  this.indentationLevel--; // Exit the scope for the for-of loop
@@ -269,7 +288,7 @@ export function visitWhileStatement(node, context) {
269
288
  ...context,
270
289
  currentLabel: undefined,
271
290
  isFunctionBody: false,
272
- });
291
+ }).trim();
273
292
  if (ts.isBlock(node.statement)) {
274
293
  let blockContent = statementCode.substring(1, statementCode.length - 2); // remove curly braces
275
294
  if (context.currentLabel) {
@@ -309,7 +328,7 @@ export function visitDoStatement(node, context) {
309
328
  ...context,
310
329
  currentLabel: undefined,
311
330
  isFunctionBody: false,
312
- });
331
+ }).trim();
313
332
  if (ts.isBlock(node.statement)) {
314
333
  let blockContent = statementCode.substring(1, statementCode.length - 2); // remove curly braces
315
334
  if (context.currentLabel) {
@@ -28,8 +28,9 @@ export function visitObjectPropertyName(node, context) {
28
28
  export function visitObjectLiteralExpression(node, context) {
29
29
  const obj = node;
30
30
  const objVar = this.generateUniqueName("__obj_", this.getDeclaredSymbols(node));
31
- let code = `([&]() {
32
- ${this.indent()} auto ${objVar} = jspp::AnyValue::make_object_with_proto({}, ::Object.get_own_property("prototype"));\n`;
31
+ let code = `([&]() {\n`;
32
+ code +=
33
+ `${this.indent()} auto ${objVar} = jspp::AnyValue::make_object_with_proto({}, ::Object.get_own_property("prototype"));\n`;
33
34
  this.indentationLevel++;
34
35
  for (const prop of obj.properties) {
35
36
  if (ts.isPropertyAssignment(prop)) {
@@ -101,6 +102,8 @@ ${this.indent()} auto ${objVar} = jspp::AnyValue::make_object_with_proto({}, ::
101
102
  }
102
103
  }
103
104
  this.indentationLevel--;
105
+ // code +=
106
+ // `${this.indent()} ${returnCmd} ${objVar};\n${this.indent()}} )() ))`;
104
107
  code += `${this.indent()} return ${objVar};\n${this.indent()}})()`;
105
108
  return code;
106
109
  }
@@ -593,7 +596,7 @@ export function visitCallExpression(node, context) {
593
596
  if (callExpr.questionDotToken) {
594
597
  return `jspp::Access::optional_call(${derefObj}.get_own_property("${propName}"), ${derefObj}, ${argsSpan}, "${this.escapeString(propName)}")`;
595
598
  }
596
- return `([&](){ auto __obj = ${derefObj}; return __obj.get_own_property("${propName}").call(__obj, ${argsSpan}, "${this.escapeString(propName)}"); })()`;
599
+ return `${derefObj}.call_own_property("${propName}", ${argsSpan})`;
597
600
  }
598
601
  // Handle obj[method]() -> pass obj as 'this'
599
602
  if (ts.isElementAccessExpression(callee)) {
@@ -628,7 +631,7 @@ export function visitCallExpression(node, context) {
628
631
  if (callExpr.questionDotToken) {
629
632
  return `jspp::Access::optional_call(${derefObj}.get_own_property(${argText}), ${derefObj}, ${argsSpan})`;
630
633
  }
631
- return `([&](){ auto __obj = ${derefObj}; return __obj.get_own_property(${argText}).call(__obj, ${argsSpan}); })()`;
634
+ return `${derefObj}.call_own_property(${argText}, ${argsSpan})`;
632
635
  }
633
636
  const calleeCode = this.visit(callee, context);
634
637
  let derefCallee = calleeCode;
@@ -13,9 +13,11 @@ export function generateLambda(node, context, options) {
13
13
  isInsideGeneratorFunction: isInsideGeneratorFunction,
14
14
  isInsideAsyncFunction: isInsideAsyncFunction,
15
15
  });
16
- const funcReturnType = isInsideGeneratorFunction
17
- ? "jspp::JsIterator<jspp::AnyValue>"
18
- : (isInsideAsyncFunction ? "jspp::JsPromise" : "jspp::AnyValue");
16
+ const funcReturnType = (isInsideGeneratorFunction && isInsideAsyncFunction)
17
+ ? "jspp::JsAsyncIterator<jspp::AnyValue>"
18
+ : (isInsideGeneratorFunction
19
+ ? "jspp::JsIterator<jspp::AnyValue>"
20
+ : (isInsideAsyncFunction ? "jspp::JsPromise" : "jspp::AnyValue"));
19
21
  const isArrow = ts.isArrowFunction(node);
20
22
  // Lambda arguments are ALWAYS const references/spans to avoid copy overhead for normal functions.
21
23
  // For generators/async, we manually copy them inside the body.
@@ -34,8 +36,10 @@ export function generateLambda(node, context, options) {
34
36
  topLevelScopeSymbols,
35
37
  localScopeSymbols: new DeclaredSymbols(),
36
38
  superClassVar: context.superClassVar,
39
+ isInsideGeneratorFunction: isInsideGeneratorFunction,
40
+ isInsideAsyncFunction: isInsideAsyncFunction,
37
41
  };
38
- // Name of 'this' and 'args' to be used in the body.
42
+ // Name of 'this' and 'args' to be used in the body.
39
43
  // If generator/async, we use the copied versions.
40
44
  let bodyThisVar = this.globalThisVar;
41
45
  let bodyArgsVar = argsName;
@@ -45,40 +49,42 @@ export function generateLambda(node, context, options) {
45
49
  const thisCopy = this.generateUniqueName("__this_copy", declaredSymbols);
46
50
  const argsCopy = this.generateUniqueName("__args_copy", declaredSymbols);
47
51
  if (!isArrow) {
48
- preamble += `${this.indent()}jspp::AnyValue ${thisCopy} = ${this.globalThisVar};\n`;
52
+ preamble +=
53
+ `${this.indent()}jspp::AnyValue ${thisCopy} = ${this.globalThisVar};\n`;
49
54
  bodyThisVar = thisCopy;
50
55
  }
51
- preamble += `${this.indent()}std::vector<jspp::AnyValue> ${argsCopy}(${argsName}.begin(), ${argsName}.end());\n`;
56
+ preamble +=
57
+ `${this.indent()}std::vector<jspp::AnyValue> ${argsCopy}(${argsName}.begin(), ${argsName}.end());\n`;
52
58
  bodyArgsVar = argsCopy;
53
- // Update visitContext to use the new 'this' variable if needed?
54
- // CodeGenerator.globalThisVar is a member of the class, so we can't easily change it for the recursive visit
59
+ // Update visitContext to use the new 'this' variable if needed?
60
+ // CodeGenerator.globalThisVar is a member of the class, so we can't easily change it for the recursive visit
55
61
  // without changing the class state or passing it down.
56
- // BUT: visitThisKeyword uses `this.globalThisVar`.
62
+ // BUT: visitThisKeyword uses `this.globalThisVar`.
57
63
  // We need to temporarily swap `this.globalThisVar` or handle it.
58
64
  // Actually, `this.globalThisVar` is set once in `generate`.
59
65
  // Wait, `visitThisKeyword` uses `this.globalThisVar`.
60
66
  // If we change the variable name for 'this', we must ensure `visitThisKeyword` generates the correct name.
61
67
  // `generateLambda` does NOT create a new CodeGenerator instance, so `this.globalThisVar` is the global one.
62
- //
68
+ //
63
69
  // We need to support shadowing `globalThisVar` for the duration of the visit.
64
- // The current `CodeGenerator` doesn't seem to support changing `globalThisVar` recursively easily
70
+ // The current `CodeGenerator` doesn't seem to support changing `globalThisVar` recursively easily
65
71
  // because it's a property of the class, not `VisitContext`.
66
- //
72
+ //
67
73
  // However, `visitFunctionDeclaration` etc don't update `globalThisVar`.
68
74
  // `visitThisKeyword` just returns `this.globalThisVar`.
69
- //
75
+ //
70
76
  // If we are inside a function, `this` should refer to the function's `this`.
71
77
  // `this.globalThisVar` is generated in `generate()`: `__this_val__...`.
72
78
  // And `generateLambda` uses `this.globalThisVar` as the parameter name.
73
- //
79
+ //
74
80
  // So, if we copy it to `__this_copy`, `visitThisKeyword` will still print `__this_val__...`.
75
81
  // This is BAD for generators because `__this_val__...` is a reference that dies.
76
- //
82
+ //
77
83
  // FIX: We must reuse the SAME name for the local copy, and shadow the parameter.
78
84
  // But we can't declare a variable with the same name as the parameter in the same scope.
79
- //
85
+ //
80
86
  // We can rename the PARAMETER to something else, and declare the local variable with `this.globalThisVar`.
81
- //
87
+ //
82
88
  }
83
89
  // Adjust parameter names for generator/async to allow shadowing/copying
84
90
  let finalThisParamName = isArrow ? "" : this.globalThisVar;
@@ -93,16 +99,19 @@ export function generateLambda(node, context, options) {
93
99
  ? "const jspp::AnyValue&"
94
100
  : `${paramThisType} ${finalThisParamName}`;
95
101
  // Re-construct lambda header with potentially new param names
96
- lambda = `${capture}(${thisArgParamFinal}, ${paramArgsType} ${finalArgsParamName}) mutable -> ${funcReturnType} `;
102
+ lambda =
103
+ `${capture}(${thisArgParamFinal}, ${paramArgsType} ${finalArgsParamName}) mutable -> ${funcReturnType} `;
97
104
  // Regenerate preamble
98
105
  preamble = "";
99
106
  if (isInsideGeneratorFunction || isInsideAsyncFunction) {
100
107
  if (!isArrow) {
101
- preamble += `${this.indent()}jspp::AnyValue ${this.globalThisVar} = ${finalThisParamName};\n`;
108
+ preamble +=
109
+ `${this.indent()}jspp::AnyValue ${this.globalThisVar} = ${finalThisParamName};\n`;
102
110
  }
103
- preamble += `${this.indent()}std::vector<jspp::AnyValue> ${argsName}(${finalArgsParamName}.begin(), ${finalArgsParamName}.end());\n`;
111
+ preamble +=
112
+ `${this.indent()}std::vector<jspp::AnyValue> ${argsName}(${finalArgsParamName}.begin(), ${finalArgsParamName}.end());\n`;
104
113
  }
105
- // Now 'argsName' refers to the vector (if copied) or the span (if not).
114
+ // Now 'argsName' refers to the vector (if copied) or the span (if not).
106
115
  // And 'this.globalThisVar' refers to the copy (if copied) or the param (if not).
107
116
  // So subsequent code using `argsName` and `visit` (using `globalThisVar`) works correctly.
108
117
  const paramExtractor = (parameters) => {
@@ -168,7 +177,7 @@ export function generateLambda(node, context, options) {
168
177
  if (ts.isBlock(node.body)) {
169
178
  // Hoist var declarations in the function body
170
179
  const varDecls = collectFunctionScopedDeclarations(node.body);
171
- varDecls.forEach(decl => {
180
+ varDecls.forEach((decl) => {
172
181
  preamble += this.hoistDeclaration(decl, visitContext.localScopeSymbols);
173
182
  });
174
183
  this.indentationLevel++;
@@ -183,7 +192,8 @@ export function generateLambda(node, context, options) {
183
192
  isInsideAsyncFunction: isInsideAsyncFunction,
184
193
  });
185
194
  // The block visitor already adds braces, so we need to inject the preamble and param extraction.
186
- lambda += "{\n" + preamble + paramExtraction + blockContent.substring(2);
195
+ lambda += "{\n" + preamble + paramExtraction +
196
+ blockContent.trimStart().substring(2);
187
197
  }
188
198
  else {
189
199
  lambda += "{\n";
@@ -210,10 +220,18 @@ export function generateLambda(node, context, options) {
210
220
  let method = "";
211
221
  // Handle generator function
212
222
  if (isInsideGeneratorFunction) {
213
- signature =
214
- "jspp::JsIterator<jspp::AnyValue>(const jspp::AnyValue&, std::span<const jspp::AnyValue>)";
215
- callable = `std::function<${signature}>(${lambda})`;
216
- method = `jspp::AnyValue::make_generator`;
223
+ if (isInsideAsyncFunction) {
224
+ signature =
225
+ "jspp::JsAsyncIterator<jspp::AnyValue>(const jspp::AnyValue&, std::span<const jspp::AnyValue>)";
226
+ callable = `std::function<${signature}>(${lambda})`;
227
+ method = `jspp::AnyValue::make_async_generator`;
228
+ }
229
+ else {
230
+ signature =
231
+ "jspp::JsIterator<jspp::AnyValue>(const jspp::AnyValue&, std::span<const jspp::AnyValue>)";
232
+ callable = `std::function<${signature}>(${lambda})`;
233
+ method = `jspp::AnyValue::make_generator`;
234
+ }
217
235
  } // Handle async function
218
236
  else if (isInsideAsyncFunction) {
219
237
  signature =
@@ -34,22 +34,20 @@ export function getDeclaredSymbols(node) {
34
34
  return symbols;
35
35
  }
36
36
  export function generateUniqueName(prefix, ...namesToAvoid) {
37
- let name = `${prefix}${this.exceptionCounter}`;
37
+ let name = `${prefix}${this.uniqueNameCounter}`;
38
38
  while (namesToAvoid.some((names) => names.has(name))) {
39
- this.exceptionCounter++;
40
- name = `${prefix}${this.exceptionCounter}`;
39
+ this.uniqueNameCounter++;
40
+ name = `${prefix}${this.uniqueNameCounter}`;
41
41
  }
42
- this.exceptionCounter++;
42
+ this.uniqueNameCounter++;
43
43
  return name;
44
44
  }
45
- export function generateUniqueExceptionName(nameToAvoid) {
46
- let exceptionName = `__caught_exception_${this.exceptionCounter}`;
47
- while (exceptionName === nameToAvoid) {
48
- this.exceptionCounter++;
49
- exceptionName = `__caught_exception_${this.exceptionCounter}`;
45
+ export function generateUniqueExceptionName(exceptionNameToAvoid, ...otherNamesToAvoid) {
46
+ let prefix = `__caught_exception_`;
47
+ if (exceptionNameToAvoid) {
48
+ prefix += exceptionNameToAvoid;
50
49
  }
51
- this.exceptionCounter++;
52
- return exceptionName;
50
+ return this.generateUniqueName(prefix, ...otherNamesToAvoid);
53
51
  }
54
52
  export function getScopeForNode(node) {
55
53
  let current = node;
@@ -189,14 +187,16 @@ export function collectFunctionScopedDeclarations(node) {
189
187
  const decls = [];
190
188
  function visit(n) {
191
189
  if (ts.isVariableStatement(n)) {
192
- const isLetOrConst = (n.declarationList.flags & (ts.NodeFlags.Let | ts.NodeFlags.Const)) !== 0;
190
+ const isLetOrConst = (n.declarationList.flags &
191
+ (ts.NodeFlags.Let | ts.NodeFlags.Const)) !== 0;
193
192
  if (!isLetOrConst) {
194
193
  decls.push(...n.declarationList.declarations);
195
194
  }
196
195
  }
197
196
  else if (ts.isForStatement(n)) {
198
197
  if (n.initializer && ts.isVariableDeclarationList(n.initializer)) {
199
- const isLetOrConst = (n.initializer.flags & (ts.NodeFlags.Let | ts.NodeFlags.Const)) !== 0;
198
+ const isLetOrConst = (n.initializer.flags &
199
+ (ts.NodeFlags.Let | ts.NodeFlags.Const)) !== 0;
200
200
  if (!isLetOrConst) {
201
201
  decls.push(...n.initializer.declarations);
202
202
  }
@@ -204,7 +204,8 @@ export function collectFunctionScopedDeclarations(node) {
204
204
  }
205
205
  else if (ts.isForInStatement(n)) {
206
206
  if (ts.isVariableDeclarationList(n.initializer)) {
207
- const isLetOrConst = (n.initializer.flags & (ts.NodeFlags.Let | ts.NodeFlags.Const)) !== 0;
207
+ const isLetOrConst = (n.initializer.flags &
208
+ (ts.NodeFlags.Let | ts.NodeFlags.Const)) !== 0;
208
209
  if (!isLetOrConst) {
209
210
  decls.push(...n.initializer.declarations);
210
211
  }
@@ -212,14 +213,19 @@ export function collectFunctionScopedDeclarations(node) {
212
213
  }
213
214
  else if (ts.isForOfStatement(n)) {
214
215
  if (ts.isVariableDeclarationList(n.initializer)) {
215
- const isLetOrConst = (n.initializer.flags & (ts.NodeFlags.Let | ts.NodeFlags.Const)) !== 0;
216
+ const isLetOrConst = (n.initializer.flags &
217
+ (ts.NodeFlags.Let | ts.NodeFlags.Const)) !== 0;
216
218
  if (!isLetOrConst) {
217
219
  decls.push(...n.initializer.declarations);
218
220
  }
219
221
  }
220
222
  }
221
223
  // Stop recursion at function boundaries (but not the root node if it is a function)
222
- if (n !== node && (ts.isFunctionDeclaration(n) || ts.isFunctionExpression(n) || ts.isArrowFunction(n) || ts.isMethodDeclaration(n) || ts.isGetAccessor(n) || ts.isSetAccessor(n) || ts.isClassDeclaration(n))) {
224
+ if (n !== node &&
225
+ (ts.isFunctionDeclaration(n) || ts.isFunctionExpression(n) ||
226
+ ts.isArrowFunction(n) || ts.isMethodDeclaration(n) ||
227
+ ts.isGetAccessor(n) || ts.isSetAccessor(n) ||
228
+ ts.isClassDeclaration(n))) {
223
229
  return;
224
230
  }
225
231
  ts.forEachChild(n, visit);
@@ -231,7 +237,8 @@ export function collectBlockScopedDeclarations(statements) {
231
237
  const decls = [];
232
238
  for (const stmt of statements) {
233
239
  if (ts.isVariableStatement(stmt)) {
234
- const isLetOrConst = (stmt.declarationList.flags & (ts.NodeFlags.Let | ts.NodeFlags.Const)) !== 0;
240
+ const isLetOrConst = (stmt.declarationList.flags &
241
+ (ts.NodeFlags.Let | ts.NodeFlags.Const)) !== 0;
235
242
  if (isLetOrConst) {
236
243
  decls.push(...stmt.declarationList.declarations);
237
244
  }
@@ -1,14 +1,13 @@
1
- import { TypeAnalyzer } from "../../analysis/typeAnalyzer";
2
1
  import { DeclaredSymbols } from "../../ast/symbols";
3
2
  import { generateLambda } from "./function-handlers";
4
3
  import { escapeString, generateUniqueExceptionName, generateUniqueName, getDeclaredSymbols, getDerefCode, getJsVarName, getReturnCommand, getScopeForNode, hoistDeclaration, indent, isAsyncFunction, isBuiltinObject, isGeneratorFunction, markSymbolAsChecked, prepareScopeSymbolsForVisit, } from "./helpers";
5
4
  import { visit } from "./visitor";
6
- const CONTAINER_FUNCTION_NAME = "__container__";
5
+ const MODULE_NAME = "__main_function__";
7
6
  export class CodeGenerator {
8
7
  indentationLevel = 0;
9
8
  typeAnalyzer;
10
9
  globalThisVar;
11
- exceptionCounter = 0;
10
+ uniqueNameCounter = 0;
12
11
  // visitor
13
12
  visit = visit;
14
13
  // helpers
@@ -36,7 +35,7 @@ export class CodeGenerator {
36
35
  this.typeAnalyzer = analyzer;
37
36
  this.globalThisVar = this.generateUniqueName("__this_val__", this.getDeclaredSymbols(ast));
38
37
  const declarations = `#include "index.hpp"\n\n`;
39
- let containerCode = `jspp::AnyValue ${CONTAINER_FUNCTION_NAME}() {\n`;
38
+ let containerCode = `jspp::JsPromise ${MODULE_NAME}() {\n`;
40
39
  this.indentationLevel++;
41
40
  containerCode +=
42
41
  `${this.indent()}jspp::AnyValue ${this.globalThisVar} = global;\n`;
@@ -44,23 +43,31 @@ export class CodeGenerator {
44
43
  isMainContext: true,
45
44
  isInsideFunction: true,
46
45
  isFunctionBody: true,
46
+ isInsideAsyncFunction: true,
47
47
  topLevelScopeSymbols: new DeclaredSymbols(),
48
48
  localScopeSymbols: new DeclaredSymbols(),
49
49
  });
50
50
  this.indentationLevel--;
51
- containerCode += " return jspp::Constants::UNDEFINED;\n";
51
+ containerCode += " co_return jspp::Constants::UNDEFINED;\n";
52
52
  containerCode += "}\n\n";
53
53
  let mainCode = "int main(int argc, char** argv) {\n";
54
54
  mainCode += ` try {\n`;
55
55
  mainCode += ` jspp::setup_process_argv(argc, argv);\n`;
56
- mainCode += ` ${CONTAINER_FUNCTION_NAME}();\n`;
56
+ mainCode += ` auto p = ${MODULE_NAME}();\n`;
57
+ mainCode += ` p.then(nullptr, [](const jspp::AnyValue& err) {\n`;
58
+ mainCode +=
59
+ ` auto error = std::make_shared<jspp::AnyValue>(err);\n`;
60
+ mainCode +=
61
+ ` console.call_own_property("error", std::span<const jspp::AnyValue>((const jspp::AnyValue[]){*error}, 1));\n`;
62
+ mainCode += ` std::exit(1);\n`;
63
+ mainCode += ` });\n`;
57
64
  mainCode += ` jspp::Scheduler::instance().run();\n`;
58
65
  mainCode += ` } catch (const std::exception& ex) {\n`;
59
66
  mainCode +=
60
- " auto error = std::make_shared<jspp::AnyValue>(jspp::Exception::exception_to_any_value(ex));\n{\n";
67
+ " auto error = std::make_shared<jspp::AnyValue>(jspp::Exception::exception_to_any_value(ex));\n {\n";
61
68
  mainCode +=
62
- ` ([&](){ auto __obj = console; return __obj.get_own_property("error").call(__obj, std::span<const jspp::AnyValue>((const jspp::AnyValue[]){*error}, 1), "console.error"); })();\n`;
63
- mainCode += ` return 1;\n}\n`;
69
+ ` console.call_own_property("error", std::span<const jspp::AnyValue>((const jspp::AnyValue[]){*error}, 1));\n`;
70
+ mainCode += ` return 1;\n }\n`;
64
71
  mainCode += ` }\n`;
65
72
  mainCode += " return 0;\n}";
66
73
  return declarations + containerCode + mainCode;