@ugo-studio/jspp 0.2.9 → 0.3.1

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.
Files changed (97) hide show
  1. package/LICENSE +25 -25
  2. package/README.md +20 -12
  3. package/dist/analysis/scope.js +5 -3
  4. package/dist/analysis/typeAnalyzer.js +21 -25
  5. package/dist/cli/index.js +14 -4
  6. package/dist/cli/utils.js +61 -0
  7. package/dist/core/codegen/class-handlers.js +6 -6
  8. package/dist/core/codegen/control-flow-handlers.js +10 -9
  9. package/dist/core/codegen/declaration-handlers.js +10 -3
  10. package/dist/core/codegen/destructuring-handlers.js +9 -4
  11. package/dist/core/codegen/expression-handlers.js +40 -29
  12. package/dist/core/codegen/function-handlers.js +78 -12
  13. package/dist/core/codegen/helpers.js +91 -14
  14. package/dist/core/codegen/index.js +4 -2
  15. package/dist/core/codegen/statement-handlers.js +9 -7
  16. package/package.json +2 -2
  17. package/scripts/precompile-headers.ts +249 -50
  18. package/scripts/setup-compiler.ts +63 -63
  19. package/src/prelude/any_value.cpp +636 -0
  20. package/src/prelude/any_value.hpp +369 -362
  21. package/src/prelude/{exception_helpers.hpp → exception.cpp} +53 -53
  22. package/src/prelude/exception.hpp +27 -27
  23. package/src/prelude/iterator_instantiations.hpp +10 -0
  24. package/src/prelude/{index.hpp → jspp.hpp} +10 -16
  25. package/src/prelude/library/array.cpp +191 -0
  26. package/src/prelude/library/array.hpp +13 -186
  27. package/src/prelude/library/console.cpp +125 -0
  28. package/src/prelude/library/console.hpp +24 -112
  29. package/src/prelude/library/error.cpp +100 -0
  30. package/src/prelude/library/error.hpp +13 -113
  31. package/src/prelude/library/function.cpp +69 -0
  32. package/src/prelude/library/function.hpp +11 -10
  33. package/src/prelude/library/global.cpp +96 -0
  34. package/src/prelude/library/global.hpp +12 -28
  35. package/src/prelude/library/global_usings.hpp +15 -0
  36. package/src/prelude/library/math.cpp +258 -0
  37. package/src/prelude/library/math.hpp +26 -308
  38. package/src/prelude/library/object.cpp +379 -0
  39. package/src/prelude/library/object.hpp +14 -276
  40. package/src/prelude/library/performance.cpp +21 -0
  41. package/src/prelude/library/performance.hpp +5 -20
  42. package/src/prelude/library/process.cpp +38 -0
  43. package/src/prelude/library/process.hpp +11 -39
  44. package/src/prelude/library/promise.cpp +131 -0
  45. package/src/prelude/library/promise.hpp +12 -123
  46. package/src/prelude/library/symbol.cpp +56 -0
  47. package/src/prelude/library/symbol.hpp +11 -52
  48. package/src/prelude/library/timer.cpp +88 -0
  49. package/src/prelude/library/timer.hpp +16 -92
  50. package/src/prelude/runtime.cpp +19 -0
  51. package/src/prelude/types.hpp +184 -179
  52. package/src/prelude/utils/access.hpp +502 -411
  53. package/src/prelude/utils/assignment_operators.hpp +99 -99
  54. package/src/prelude/utils/log_any_value/array.hpp +61 -40
  55. package/src/prelude/utils/log_any_value/function.hpp +39 -39
  56. package/src/prelude/utils/log_any_value/object.hpp +60 -3
  57. package/src/prelude/utils/operators.hpp +351 -336
  58. package/src/prelude/utils/operators_primitive.hpp +336 -336
  59. package/src/prelude/utils/well_known_symbols.hpp +24 -24
  60. package/src/prelude/values/array.cpp +1399 -0
  61. package/src/prelude/values/array.hpp +4 -1
  62. package/src/prelude/values/async_iterator.cpp +251 -0
  63. package/src/prelude/values/async_iterator.hpp +111 -83
  64. package/src/prelude/values/function.cpp +262 -0
  65. package/src/prelude/values/function.hpp +62 -82
  66. package/src/prelude/values/iterator.cpp +309 -0
  67. package/src/prelude/values/iterator.hpp +33 -64
  68. package/src/prelude/values/number.cpp +176 -0
  69. package/src/prelude/values/object.cpp +159 -0
  70. package/src/prelude/values/object.hpp +4 -0
  71. package/src/prelude/values/promise.cpp +479 -0
  72. package/src/prelude/values/promise.hpp +79 -72
  73. package/src/prelude/values/prototypes/array.hpp +46 -1336
  74. package/src/prelude/values/prototypes/async_iterator.hpp +19 -61
  75. package/src/prelude/values/prototypes/function.hpp +7 -46
  76. package/src/prelude/values/prototypes/iterator.hpp +25 -201
  77. package/src/prelude/values/prototypes/number.hpp +23 -210
  78. package/src/prelude/values/prototypes/object.hpp +7 -23
  79. package/src/prelude/values/prototypes/promise.hpp +18 -196
  80. package/src/prelude/values/prototypes/string.hpp +39 -542
  81. package/src/prelude/values/prototypes/symbol.hpp +9 -70
  82. package/src/prelude/values/shape.hpp +52 -52
  83. package/src/prelude/values/string.cpp +485 -0
  84. package/src/prelude/values/string.hpp +25 -26
  85. package/src/prelude/values/symbol.cpp +89 -0
  86. package/src/prelude/values/symbol.hpp +101 -101
  87. package/src/prelude/any_value_access.hpp +0 -170
  88. package/src/prelude/any_value_defines.hpp +0 -190
  89. package/src/prelude/any_value_helpers.hpp +0 -374
  90. package/src/prelude/values/helpers/array.hpp +0 -209
  91. package/src/prelude/values/helpers/async_iterator.hpp +0 -275
  92. package/src/prelude/values/helpers/function.hpp +0 -109
  93. package/src/prelude/values/helpers/iterator.hpp +0 -145
  94. package/src/prelude/values/helpers/object.hpp +0 -104
  95. package/src/prelude/values/helpers/promise.hpp +0 -254
  96. package/src/prelude/values/helpers/string.hpp +0 -61
  97. package/src/prelude/values/helpers/symbol.hpp +0 -21
package/LICENSE CHANGED
@@ -1,25 +1,25 @@
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.
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
@@ -11,7 +11,11 @@ The primary goal of this project is to achieve a near-perfect translation of Jav
11
11
 
12
12
  JavaScript is flexible and dynamic; C++ is performant and type-safe. JSPP aims to offer the best of both worlds. By transpiling JS/TS to C++, we can potentially run JavaScript logic in environments where C++ is native, with significant performance gains and opportunities for low-level interoperability.
13
13
 
14
- This project serves as a deep dive into compiler design, language semantics, and the expressive power of modern C++. The current implementation translates an entire JavaScript/TypeScript file into a single C++ `main` function, cleverly using `std::shared_ptr<std::any>` (and custom wrappers) to replicate JavaScript's dynamic typing, garbage-collection-like memory management, and complex features like closures.
14
+ This project serves as a deep dive into compiler design, language semantics, and the expressive power of modern C++. The architecture is designed for performance, utilizing:
15
+ - **Fast Runtime Library:** Core JavaScript logic is implemented in a static C++ library (`libjspp.a`), precompiled for speed.
16
+ - **Precompiled Headers (PCH):** Common headers are precompiled to drastically reduce the front-end parsing time of the C++ compiler.
17
+ - **NaN-Boxing:** An efficient 64-bit value representation (NaN-boxing) is used to replicate JavaScript's dynamic typing with minimal overhead.
18
+ - **Modern C++23:** Leverages the latest language features, including coroutines for `async/await` and generators.
15
19
 
16
20
  ## Features
17
21
 
@@ -67,8 +71,10 @@ To contribute to JSPP or run its test suite, follow these steps:
67
71
  ### Prerequisites
68
72
 
69
73
  - **Bun:** This project uses [Bun](https://bun.sh/) for package management, script execution, and testing.
70
- - **C++ Compiler:** A compiler with support for C++23 is required. This project is tested with `g++`.
71
- - `g++` (MinGW on Windows, or available via build-essentials on Linux)
74
+ - **C++ Compiler:** A compiler with support for C++23 is required (e.g., `g++` 13+ or `clang` 17+).
75
+ - **Windows:** [MSYS2](https://www.msys2.org/) with `mingw-w64-x86_64-gcc` is recommended.
76
+ - **Linux:** `g++-14` or equivalent.
77
+ - **macOS:** `brew install gcc`.
72
78
 
73
79
  ### Setup
74
80
 
@@ -76,10 +82,11 @@ To contribute to JSPP or run its test suite, follow these steps:
76
82
  ```sh
77
83
  git clone https://github.com/ugo-studio/jspp.git
78
84
  ```
79
- 2. Install dependencies:
85
+ 2. Install dependencies and build the runtime:
80
86
  ```sh
81
87
  bun install
82
88
  ```
89
+ *Note: The `postinstall` script will automatically check for your C++ compiler and precompile the runtime headers and library.*
83
90
 
84
91
  ## Usage
85
92
 
@@ -99,11 +106,9 @@ jspp my-code/test.ts
99
106
 
100
107
  The transpiled C++ file and executable will be generated in the same directory as the input file and cleaned up after execution (unless `--keep-cpp` is used).
101
108
 
102
- You can also run the test suite:
109
+ ### Timing and Reports
103
110
 
104
- ```sh
105
- bun test
106
- ```
111
+ In debug mode (default), JSPP provides a compilation time report using GCC's `-ftime-report`. This helps track the performance of the transpilation and compilation phases.
107
112
 
108
113
  ## Roadmap
109
114
 
@@ -115,7 +120,7 @@ This project is ambitious, and there is a long and exciting road ahead. Here is
115
120
 
116
121
  This phase focuses on building a solid foundation that correctly models JavaScript's core runtime behavior.
117
122
 
118
- - [x] Dynamic Variables & Primitives
123
+ - [x] Dynamic Variables & Primitives (NaN-boxing)
119
124
  - [x] Function Declarations & Arrow Functions
120
125
  - [x] Correct Hoisting for Variables and Functions
121
126
  - [x] Closures & Lexical Scoping
@@ -139,16 +144,19 @@ This phase broadens the range of supported JavaScript syntax and features.
139
144
 
140
145
  This phase focuses on building out the standard library and enabling modular code.
141
146
 
142
- - [ ] **JS Standard Library:** Implementation of common built-in objects. (Currently supports: `Math`, `Symbol`, `Error`, `String`, `Array`, `Object`, `Timer`). **Pending:** `Date`, `Temporal`, `Map`, `Set`, `JSON`, etc.
143
- - [x] **Asynchronous Operations:** Event loop, `Promise`, `async/await`, and timers (`setTimeout`).
147
+ - [x] **JS Standard Library:** Core implementation of `Math`, `Symbol`, `Error`, `String`, `Array`, `Object`, `Timer`.
148
+ - [ ] **Expanded Library:** `Date`, `Temporal`, `Map`, `Set`, `JSON`, `RegExp`.
149
+ - [x] **Asynchronous Operations:** Event loop, `Promise`, `async/await`.
144
150
  - [ ] **Module System:** Support for `import` and `export` to transpile multi-file projects.
145
151
 
146
152
  ### **Phase 4: Optimization & Advanced Features**
147
153
 
148
154
  With a feature-complete transpiler, the focus will shift to performance and advanced capabilities.
149
155
 
156
+ - [x] **Architecture Optimization:** Static library runtime and Precompiled Headers.
150
157
  - [ ] **Performance Benchmarking:** Create a suite to compare transpiled C++ performance against V8.
151
- - [ ] **C++ Interoperability:** Define a clear API for calling C++ functions from the transpiled JavaScript and vice-versa.
158
+ - [ ] **Linker Optimization:** Support for `LLD` or `Mold` linkers.
159
+ - [ ] **C++ Interoperability:** Define a clear API for calling C++ functions from JavaScript.
152
160
 
153
161
  ## Contributing
154
162
 
@@ -7,6 +7,9 @@ export const RESERVED_KEYWORDS = new Set([
7
7
  "co_return",
8
8
  "co_await",
9
9
  ]);
10
+ export const RESERVED_VAR_NAMES_FOR_STRICT_MODE = new Set([
11
+ "arguments",
12
+ ]);
10
13
  export const BUILTIN_OBJECTS = new Set([
11
14
  { name: "undefined", isConst: true },
12
15
  { name: "null", isConst: true },
@@ -57,7 +60,6 @@ export class Scope {
57
60
  export class ScopeManager {
58
61
  currentScope;
59
62
  allScopes = []; // Array to store all created scopes
60
- reservedKeywords = new Set(RESERVED_KEYWORDS);
61
63
  constructor() {
62
64
  const rootScope = new Scope(null, null); // The global scope
63
65
  this.currentScope = rootScope;
@@ -86,7 +88,7 @@ export class ScopeManager {
86
88
  // Defines a variable in the current scope.
87
89
  define(name, type) {
88
90
  // if (name === "named" || name === "letVal") console.log("Defining", name, "in scope. isBuiltin:", type.isBuiltin, " type:", type.type);
89
- if (this.reservedKeywords.has(name) && !type.isBuiltin) {
91
+ if (RESERVED_KEYWORDS.has(name) && !type.isBuiltin) {
90
92
  if (type.declaration) {
91
93
  throw new CompilerError(`Unexpected reserved word "${name}"`, type.declaration, "SyntaxError");
92
94
  }
@@ -96,7 +98,7 @@ export class ScopeManager {
96
98
  }
97
99
  // Defines a `var` variable (hoisted to function or global scope).
98
100
  defineVar(name, type) {
99
- if (this.reservedKeywords.has(name) && !type.isBuiltin) {
101
+ if (RESERVED_KEYWORDS.has(name) && !type.isBuiltin) {
100
102
  if (type.declaration) {
101
103
  throw new CompilerError(`Unexpected reserved word "${name}"`, type.declaration, "SyntaxError");
102
104
  }
@@ -47,6 +47,22 @@ export class TypeAnalyzer {
47
47
  labelStack = [];
48
48
  loopDepth = 0;
49
49
  switchDepth = 0;
50
+ defineParameter(nameNode, p, type) {
51
+ if (ts.isIdentifier(nameNode)) {
52
+ this.scopeManager.define(nameNode.text, {
53
+ type: type || getParameterType(p),
54
+ isParameter: true,
55
+ declaration: p,
56
+ });
57
+ }
58
+ else {
59
+ nameNode.elements.forEach((element) => {
60
+ if (ts.isBindingElement(element)) {
61
+ this.defineParameter(element.name, p, type);
62
+ }
63
+ });
64
+ }
65
+ }
50
66
  analyze(ast) {
51
67
  this.nodeToScope.set(ast, this.scopeManager.currentScope);
52
68
  const crossScopeModificationVisitor = (node) => {
@@ -246,11 +262,7 @@ export class TypeAnalyzer {
246
262
  if (p.getText() == "this") { // Catch invalid parameters
247
263
  throw new CompilerError("Cannot use 'this' as a parameter name.", p, "SyntaxError");
248
264
  }
249
- this.scopeManager.define(p.name.getText(), {
250
- type: getParameterType(p),
251
- isParameter: true,
252
- declaration: p,
253
- });
265
+ this.defineParameter(p.name, p);
254
266
  });
255
267
  this.functionStack.push(node);
256
268
  }
@@ -284,11 +296,7 @@ export class TypeAnalyzer {
284
296
  if (p.getText() == "this") { // Catch invalid parameters
285
297
  throw new CompilerError("Cannot use 'this' as a parameter name.", p, "SyntaxError");
286
298
  }
287
- this.scopeManager.define(p.name.getText(), {
288
- type: getParameterType(p),
289
- isParameter: true,
290
- declaration: p,
291
- });
299
+ this.defineParameter(p.name, p);
292
300
  });
293
301
  this.functionStack.push(node);
294
302
  }
@@ -323,11 +331,7 @@ export class TypeAnalyzer {
323
331
  if (p.getText() == "this") { // Catch invalid parameters
324
332
  throw new CompilerError("Cannot use 'this' as a parameter name.", p, "SyntaxError");
325
333
  }
326
- this.scopeManager.define(p.name.getText(), {
327
- type: getParameterType(p),
328
- isParameter: true,
329
- declaration: p,
330
- });
334
+ this.defineParameter(p.name, p);
331
335
  });
332
336
  this.functionStack.push(node);
333
337
  }
@@ -394,11 +398,7 @@ export class TypeAnalyzer {
394
398
  this.functionTypeInfo.set(node, funcType);
395
399
  this.scopeManager.enterScope(node);
396
400
  this.nodeToScope.set(node, this.scopeManager.currentScope);
397
- node.parameters.forEach((p) => this.scopeManager.define(p.name.getText(), {
398
- type: "auto",
399
- isParameter: true,
400
- declaration: p,
401
- }));
401
+ node.parameters.forEach((p) => this.defineParameter(p.name, p, "auto"));
402
402
  this.functionStack.push(node);
403
403
  }
404
404
  },
@@ -422,11 +422,7 @@ export class TypeAnalyzer {
422
422
  this.functionTypeInfo.set(node, funcType);
423
423
  this.scopeManager.enterScope(node);
424
424
  this.nodeToScope.set(node, this.scopeManager.currentScope);
425
- node.parameters.forEach((p) => this.scopeManager.define(p.name.getText(), {
426
- type: "auto",
427
- isParameter: true,
428
- declaration: p,
429
- }));
425
+ node.parameters.forEach((p) => this.defineParameter(p.name, p, "auto"));
430
426
  this.functionStack.push(node); // Constructor acts like a function
431
427
  }
432
428
  },
package/dist/cli/index.js CHANGED
@@ -7,8 +7,8 @@ import { CompilerError } from "../core/error.js";
7
7
  import { Interpreter } from "../index.js";
8
8
  import { parseArgs } from "./args.js";
9
9
  import { COLORS } from "./colors.js";
10
- import { getLatestMtime } from "./file-utils.js";
11
10
  import { Spinner } from "./spinner.js";
11
+ import { getLatestMtime, msToHumanReadable } from "./utils.js";
12
12
  const pkgDir = path.dirname(path.dirname(import.meta.dirname));
13
13
  async function main() {
14
14
  const { jsFilePath, isRelease, keepCpp, outputExePath, scriptArgs } = parseArgs(process.argv.slice(2));
@@ -30,7 +30,7 @@ async function main() {
30
30
  const mode = isRelease ? "release" : "debug";
31
31
  console.log(`${COLORS.bold}JSPP Compiler${COLORS.reset} ${COLORS.dim}v${pkg.version}${COLORS.reset}`);
32
32
  console.log(`Mode: ${isRelease ? COLORS.green : COLORS.yellow}${mode.toUpperCase()}${COLORS.reset}\n`);
33
- const flags = isRelease ? ["-O3", "-DNDEBUG"] : ["-O0"];
33
+ const flags = isRelease ? ["-O3", "-DNDEBUG"] : ["-Og", "-ftime-report"];
34
34
  if (process.platform === "win32") {
35
35
  flags.push("-Wa,-mbig-obj");
36
36
  }
@@ -51,7 +51,8 @@ async function main() {
51
51
  // 2. Precompiled Header Check
52
52
  spinner.text = "Checking precompiled headers...";
53
53
  spinner.start();
54
- const pchFile = path.join(pchDir, "index.hpp.gch");
54
+ const pchFile = path.join(pchDir, "jspp.hpp.gch");
55
+ const runtimeLibPath = path.join(pchDir, "libjspp.a");
55
56
  let shouldRebuild = false;
56
57
  try {
57
58
  const pchStats = await fs.stat(pchFile);
@@ -96,10 +97,15 @@ async function main() {
96
97
  spinner.start();
97
98
  // Ensure output directory exists
98
99
  await fs.mkdir(path.dirname(exeFilePath), { recursive: true });
100
+ const compileStartTime = performance.now();
99
101
  const compile = spawn("g++", [
100
102
  "-std=c++23",
101
103
  ...flags,
104
+ "-Winvalid-pch",
105
+ "-include",
106
+ "jspp.hpp",
102
107
  cppFilePath,
108
+ runtimeLibPath,
103
109
  "-o",
104
110
  exeFilePath,
105
111
  "-I",
@@ -122,7 +128,11 @@ async function main() {
122
128
  console.error(stderr);
123
129
  process.exit(1);
124
130
  }
125
- spinner.succeed(`Compiled to ${COLORS.green}${COLORS.bold}${path.basename(exeFilePath)}${COLORS.reset}`);
131
+ const compileEndTime = performance.now();
132
+ const compileTime = msToHumanReadable(compileEndTime - compileStartTime);
133
+ spinner.succeed(`Compiled to ${COLORS.green}${COLORS.bold}${path.basename(exeFilePath)}${COLORS.reset} in ${COLORS.dim}${COLORS.bold}${compileTime}${COLORS.reset}`);
134
+ // const stderr = Buffer.concat(compileStderrChunks).toString();
135
+ // console.log(stderr);
126
136
  // Clean up C++ file if not requested to keep
127
137
  if (!keepCpp) {
128
138
  try {
@@ -0,0 +1,61 @@
1
+ import fs from "fs/promises";
2
+ import path from "path";
3
+ export async function getLatestMtime(dirPath) {
4
+ let maxMtime = 0;
5
+ const entries = await fs.readdir(dirPath, { withFileTypes: true });
6
+ for (const entry of entries) {
7
+ const fullPath = path.join(dirPath, entry.name);
8
+ if (entry.isDirectory()) {
9
+ const nestedMtime = await getLatestMtime(fullPath);
10
+ if (nestedMtime > maxMtime)
11
+ maxMtime = nestedMtime;
12
+ }
13
+ else {
14
+ const stats = await fs.stat(fullPath);
15
+ if (stats.mtimeMs > maxMtime)
16
+ maxMtime = stats.mtimeMs;
17
+ }
18
+ }
19
+ return maxMtime;
20
+ }
21
+ /**
22
+ * Converts milliseconds to a human-readable format.
23
+ * @param ms - The time in milliseconds
24
+ * @returns A formatted string (e.g., "2 hours, 1 minute, 4 seconds")
25
+ */
26
+ export function msToHumanReadable(ms) {
27
+ if (ms < 0) {
28
+ throw new Error("Time cannot be negative");
29
+ }
30
+ if (ms === 0) {
31
+ return "0 seconds";
32
+ }
33
+ // Define time constants in milliseconds
34
+ const MS_PER_SECOND = 1000;
35
+ const MS_PER_MINUTE = 60 * MS_PER_SECOND;
36
+ const MS_PER_HOUR = 60 * MS_PER_MINUTE;
37
+ const MS_PER_DAY = 24 * MS_PER_HOUR;
38
+ // Calculate each unit
39
+ const days = Math.floor(ms / MS_PER_DAY);
40
+ const hours = Math.floor((ms % MS_PER_DAY) / MS_PER_HOUR);
41
+ const minutes = Math.floor((ms % MS_PER_HOUR) / MS_PER_MINUTE);
42
+ const seconds = Math.floor((ms % MS_PER_MINUTE) / MS_PER_SECOND);
43
+ // Array to hold the formatted parts
44
+ const parts = [];
45
+ // Helper function to handle pluralization
46
+ const addPart = (value, unit) => {
47
+ if (value > 0) {
48
+ parts.push(`${value} ${unit}${value > 1 ? "s" : ""}`);
49
+ }
50
+ };
51
+ addPart(days, "day");
52
+ addPart(hours, "hour");
53
+ addPart(minutes, "minute");
54
+ addPart(seconds, "second");
55
+ // If the time was less than 1 second, it will be empty
56
+ if (parts.length === 0) {
57
+ return `${ms} millisecond${ms > 1 ? "s" : ""}`;
58
+ }
59
+ // Join the parts with a comma and space
60
+ return parts.join(", ");
61
+ }
@@ -42,16 +42,16 @@ export function visitClassDeclaration(node, context) {
42
42
  // Default constructor
43
43
  if (parentName) {
44
44
  constructorLambda =
45
- `jspp::AnyValue::make_class([=](jspp::AnyValue ${this.globalThisVar}, std::span<const jspp::AnyValue> args) mutable -> jspp::AnyValue {
46
- auto __parent = ${parentName};
47
- __parent.call(${this.globalThisVar}, args, "super");
48
- return jspp::Constants::UNDEFINED;
45
+ `jspp::AnyValue::make_class([=](jspp::AnyValue ${this.globalThisVar}, std::span<const jspp::AnyValue> args) mutable -> jspp::AnyValue {
46
+ auto __parent = ${parentName};
47
+ __parent.call(${this.globalThisVar}, args, "super");
48
+ return jspp::Constants::UNDEFINED;
49
49
  }, "${className}")`;
50
50
  }
51
51
  else {
52
52
  constructorLambda =
53
- `jspp::AnyValue::make_class([=](jspp::AnyValue ${this.globalThisVar}, std::span<const jspp::AnyValue> args) mutable -> jspp::AnyValue {
54
- return jspp::Constants::UNDEFINED;
53
+ `jspp::AnyValue::make_class([=](jspp::AnyValue ${this.globalThisVar}, std::span<const jspp::AnyValue> args) mutable -> jspp::AnyValue {
54
+ return jspp::Constants::UNDEFINED;
55
55
  }, "${className}")`;
56
56
  }
57
57
  }
@@ -50,7 +50,7 @@ export function visitForStatement(node, context) {
50
50
  type: declType,
51
51
  checks: { initialized: true },
52
52
  });
53
- if (typeInfo.needsHeapAllocation) {
53
+ if (typeInfo?.needsHeapAllocation) {
54
54
  initializerCode =
55
55
  `auto ${name} = std::make_shared<jspp::AnyValue>(${initValue})`;
56
56
  }
@@ -124,7 +124,7 @@ export function visitForInStatement(node, context) {
124
124
  varName = decl.name.getText();
125
125
  const scope = this.getScopeForNode(decl);
126
126
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(varName, scope);
127
- if (typeInfo.needsHeapAllocation) {
127
+ if (typeInfo?.needsHeapAllocation) {
128
128
  code +=
129
129
  `${this.indent()}auto ${varName} = std::make_shared<jspp::AnyValue>(jspp::Constants::UNDEFINED);\n`;
130
130
  assignmentTarget = `*${varName}`;
@@ -140,7 +140,7 @@ export function visitForInStatement(node, context) {
140
140
  varName = forIn.initializer.getText();
141
141
  const scope = this.getScopeForNode(forIn.initializer);
142
142
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(varName, scope);
143
- assignmentTarget = typeInfo.needsHeapAllocation
143
+ assignmentTarget = typeInfo?.needsHeapAllocation
144
144
  ? `*${varName}`
145
145
  : varName;
146
146
  }
@@ -154,11 +154,10 @@ export function visitForInStatement(node, context) {
154
154
  }
155
155
  const keysVar = this.generateUniqueName("__keys_", new Set([varName]));
156
156
  code +=
157
- `${this.indent()}std::vector<std::string> ${keysVar} = jspp::Access::get_object_keys(${derefExpr});\n`;
158
- code += `${this.indent()}for (const auto& ${varName}_str : ${keysVar}) {\n`;
157
+ `${this.indent()}std::vector<jspp::AnyValue> ${keysVar} = jspp::Access::get_object_keys(${derefExpr});\n`;
158
+ code += `${this.indent()}for (const auto& ${varName}_val : ${keysVar}) {\n`;
159
159
  this.indentationLevel++;
160
- code +=
161
- `${this.indent()}${assignmentTarget} = jspp::AnyValue::make_string(${varName}_str);\n`;
160
+ code += `${this.indent()}${assignmentTarget} = ${varName}_val;\n`;
162
161
  code += this.visit(forIn.statement, {
163
162
  ...context,
164
163
  currentLabel: undefined,
@@ -196,7 +195,7 @@ export function visitForOfStatement(node, context) {
196
195
  elemName = decl.name.getText();
197
196
  const scope = this.getScopeForNode(decl);
198
197
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(elemName, scope);
199
- if (typeInfo.needsHeapAllocation) {
198
+ if (typeInfo?.needsHeapAllocation) {
200
199
  code +=
201
200
  `${this.indent()}auto ${elemName} = std::make_shared<jspp::AnyValue>(jspp::Constants::UNDEFINED);\n`;
202
201
  assignmentTarget = `*${elemName}`;
@@ -212,7 +211,7 @@ export function visitForOfStatement(node, context) {
212
211
  elemName = forOf.initializer.getText();
213
212
  const scope = this.getScopeForNode(forOf.initializer);
214
213
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(elemName, scope);
215
- assignmentTarget = typeInfo.needsHeapAllocation
214
+ assignmentTarget = typeInfo?.needsHeapAllocation
216
215
  ? `*${elemName}`
217
216
  : elemName;
218
217
  }
@@ -440,12 +439,14 @@ export function visitSwitchStatement(node, context) {
440
439
  this.markSymbolAsInitialized(funcName, globalScopeSymbols, localScopeSymbols);
441
440
  // Generate native name
442
441
  const nativeName = this.generateUniqueName(`__${funcName}_native_`, hoistedSymbols);
442
+ const argumentKeywordIsUsed = this.isVariableUsedWithoutDeclaration("arguments", stmt.body);
443
443
  hoistedSymbols.update(funcName, {
444
444
  features: {
445
445
  native: {
446
446
  type: "lambda",
447
447
  name: nativeName,
448
448
  parameters: this.validateFunctionParams(stmt.parameters),
449
+ argumentKeywordIsUsed,
449
450
  },
450
451
  },
451
452
  });
@@ -1,4 +1,5 @@
1
1
  import ts from "typescript";
2
+ import { RESERVED_VAR_NAMES_FOR_STRICT_MODE } from "../../analysis/scope.js";
2
3
  import { CompilerError } from "../error.js";
3
4
  import { CodeGenerator } from "./index.js";
4
5
  export function visitVariableDeclarationList(node, context) {
@@ -17,6 +18,9 @@ export function visitVariableDeclaration(node, context) {
17
18
  return this.generateDestructuring(varDecl.name, rhsCode, context);
18
19
  }
19
20
  const name = varDecl.name.getText();
21
+ if (RESERVED_VAR_NAMES_FOR_STRICT_MODE.has(name)) {
22
+ throw new CompilerError(`Cannot declare a variable named '${name}' in strict mode.`, varDecl.name, "SyntaxError");
23
+ }
20
24
  const scope = this.getScopeForNode(varDecl);
21
25
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(name, scope);
22
26
  // Mark the symbol as checked
@@ -34,7 +38,7 @@ export function visitVariableDeclaration(node, context) {
34
38
  const initTypeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(initExpr.text, initScope);
35
39
  const varName = this.getJsVarName(initExpr);
36
40
  // Check if both target and initializer are heap allocated
37
- if (typeInfo.needsHeapAllocation &&
41
+ if (typeInfo?.needsHeapAllocation &&
38
42
  initTypeInfo?.needsHeapAllocation) {
39
43
  shouldSkipDeref = true;
40
44
  }
@@ -55,6 +59,8 @@ export function visitVariableDeclaration(node, context) {
55
59
  const scopeNode = ts.isVariableDeclarationList(varDecl.parent)
56
60
  ? varDecl.parent.parent.parent
57
61
  : varDecl.parent;
62
+ const argumentKeywordIsUsed = ts.isFunctionExpression(initExpr) &&
63
+ this.isVariableUsedWithoutDeclaration("arguments", initExpr.body);
58
64
  // Mark before further visits
59
65
  context.localScopeSymbols.update(name, {
60
66
  features: {
@@ -62,6 +68,7 @@ export function visitVariableDeclaration(node, context) {
62
68
  type: "lambda",
63
69
  name: nativeName,
64
70
  parameters: this.validateFunctionParams(initExpr.parameters),
71
+ argumentKeywordIsUsed,
65
72
  },
66
73
  },
67
74
  });
@@ -92,7 +99,7 @@ export function visitVariableDeclaration(node, context) {
92
99
  (!context.localScopeSymbols.has(name));
93
100
  const assignmentTarget = shouldDeref
94
101
  ? this.getDerefCode(name, name, context, typeInfo)
95
- : (typeInfo.needsHeapAllocation && !shouldSkipDeref
102
+ : (typeInfo?.needsHeapAllocation && !shouldSkipDeref
96
103
  ? `*${name}`
97
104
  : name);
98
105
  if (nativeLambdaCode)
@@ -123,7 +130,7 @@ export function visitVariableDeclaration(node, context) {
123
130
  const initValue = initializer
124
131
  ? initializer.substring(3)
125
132
  : "jspp::Constants::UNDEFINED";
126
- if (typeInfo.needsHeapAllocation) {
133
+ if (typeInfo?.needsHeapAllocation) {
127
134
  return `auto ${name} = std::make_shared<jspp::AnyValue>(${initValue})`;
128
135
  }
129
136
  else {
@@ -1,4 +1,6 @@
1
1
  import ts from "typescript";
2
+ import { RESERVED_VAR_NAMES_FOR_STRICT_MODE } from "../../analysis/scope.js";
3
+ import { CompilerError } from "../error.js";
2
4
  import { visitObjectPropertyName } from "./expression-handlers.js";
3
5
  import { CodeGenerator } from "./index.js";
4
6
  import {} from "./visitor.js";
@@ -11,6 +13,9 @@ export function generateDestructuring(lhs, rhsCode, context) {
11
13
  const genAssignment = (pattern, valueCode) => {
12
14
  if (ts.isIdentifier(pattern)) {
13
15
  const name = pattern.text;
16
+ if (RESERVED_VAR_NAMES_FOR_STRICT_MODE.has(name)) {
17
+ throw new CompilerError(`Cannot destructure to a variable named '${name}' in strict mode.`, pattern, "SyntaxError");
18
+ }
14
19
  const scope = this.getScopeForNode(pattern);
15
20
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(name, scope);
16
21
  // Mark initialized if it's a declaration
@@ -103,7 +108,7 @@ export function generateDestructuring(lhs, rhsCode, context) {
103
108
  });
104
109
  // Call the return method to clean resources
105
110
  innerCode +=
106
- `${this.indent()}jspp::Access::call_optional_property_with_optional_call(${iterVar}, "return", {}, "return");\n`;
111
+ `${this.indent()}jspp::Access::call_optional_property_with_optional_call(${iterVar}, "return", {});\n`;
107
112
  return innerCode;
108
113
  }
109
114
  else if (ts.isObjectBindingPattern(pattern) ||
@@ -150,9 +155,9 @@ export function generateDestructuring(lhs, rhsCode, context) {
150
155
  }
151
156
  if (isRest) {
152
157
  const keysArray = `{${seenKeys.map((k) => k.startsWith('"') && k.endsWith('"')
153
- ? k
154
- : `(${k}).to_property_key()`).join(", ")}}`;
155
- const restValueCode = `jspp::Access::get_rest_object(${valueCode}, std::vector<std::string>${keysArray})`;
158
+ ? `jspp::AnyValue::make_string(${k})`
159
+ : `(${k})`).join(", ")}}`;
160
+ const restValueCode = `jspp::Access::get_rest_object(${valueCode}, std::vector<jspp::AnyValue>${keysArray})`;
156
161
  innerCode += genAssignment(target, restValueCode);
157
162
  }
158
163
  else {