@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.
- package/LICENSE +25 -25
- package/README.md +20 -12
- package/dist/analysis/scope.js +5 -3
- package/dist/analysis/typeAnalyzer.js +21 -25
- package/dist/cli/index.js +14 -4
- package/dist/cli/utils.js +61 -0
- package/dist/core/codegen/class-handlers.js +6 -6
- package/dist/core/codegen/control-flow-handlers.js +10 -9
- package/dist/core/codegen/declaration-handlers.js +10 -3
- package/dist/core/codegen/destructuring-handlers.js +9 -4
- package/dist/core/codegen/expression-handlers.js +40 -29
- package/dist/core/codegen/function-handlers.js +78 -12
- package/dist/core/codegen/helpers.js +91 -14
- package/dist/core/codegen/index.js +4 -2
- package/dist/core/codegen/statement-handlers.js +9 -7
- package/package.json +2 -2
- package/scripts/precompile-headers.ts +249 -50
- package/scripts/setup-compiler.ts +63 -63
- package/src/prelude/any_value.cpp +636 -0
- package/src/prelude/any_value.hpp +369 -362
- 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} +10 -16
- package/src/prelude/library/array.cpp +191 -0
- package/src/prelude/library/array.hpp +13 -186
- package/src/prelude/library/console.cpp +125 -0
- package/src/prelude/library/console.hpp +24 -112
- package/src/prelude/library/error.cpp +100 -0
- package/src/prelude/library/error.hpp +13 -113
- package/src/prelude/library/function.cpp +69 -0
- package/src/prelude/library/function.hpp +11 -10
- package/src/prelude/library/global.cpp +96 -0
- package/src/prelude/library/global.hpp +12 -28
- package/src/prelude/library/global_usings.hpp +15 -0
- package/src/prelude/library/math.cpp +258 -0
- package/src/prelude/library/math.hpp +26 -308
- package/src/prelude/library/object.cpp +379 -0
- package/src/prelude/library/object.hpp +14 -276
- 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 +11 -39
- package/src/prelude/library/promise.cpp +131 -0
- package/src/prelude/library/promise.hpp +12 -123
- package/src/prelude/library/symbol.cpp +56 -0
- package/src/prelude/library/symbol.hpp +11 -52
- package/src/prelude/library/timer.cpp +88 -0
- package/src/prelude/library/timer.hpp +16 -92
- package/src/prelude/runtime.cpp +19 -0
- package/src/prelude/types.hpp +184 -179
- package/src/prelude/utils/access.hpp +502 -411
- package/src/prelude/utils/assignment_operators.hpp +99 -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/object.hpp +60 -3
- package/src/prelude/utils/operators.hpp +351 -336
- package/src/prelude/utils/operators_primitive.hpp +336 -336
- 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 -1
- package/src/prelude/values/async_iterator.cpp +251 -0
- package/src/prelude/values/async_iterator.hpp +111 -83
- package/src/prelude/values/function.cpp +262 -0
- package/src/prelude/values/function.hpp +62 -82
- package/src/prelude/values/iterator.cpp +309 -0
- package/src/prelude/values/iterator.hpp +33 -64
- package/src/prelude/values/number.cpp +176 -0
- package/src/prelude/values/object.cpp +159 -0
- package/src/prelude/values/object.hpp +4 -0
- package/src/prelude/values/promise.cpp +479 -0
- package/src/prelude/values/promise.hpp +79 -72
- package/src/prelude/values/prototypes/array.hpp +46 -1336
- package/src/prelude/values/prototypes/async_iterator.hpp +19 -61
- package/src/prelude/values/prototypes/function.hpp +7 -46
- package/src/prelude/values/prototypes/iterator.hpp +25 -201
- package/src/prelude/values/prototypes/number.hpp +23 -210
- package/src/prelude/values/prototypes/object.hpp +7 -23
- package/src/prelude/values/prototypes/promise.hpp +18 -196
- package/src/prelude/values/prototypes/string.hpp +39 -542
- 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/string.hpp +25 -26
- package/src/prelude/values/symbol.cpp +89 -0
- package/src/prelude/values/symbol.hpp +101 -101
- 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/values/helpers/array.hpp +0 -209
- 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 -61
- 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
|
|
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.
|
|
71
|
-
-
|
|
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
|
-
|
|
109
|
+
### Timing and Reports
|
|
103
110
|
|
|
104
|
-
|
|
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
|
-
- [
|
|
143
|
-
- [
|
|
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
|
-
- [ ] **
|
|
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
|
|
package/dist/analysis/scope.js
CHANGED
|
@@ -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 (
|
|
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 (
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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"] : ["-
|
|
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, "
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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<
|
|
158
|
-
code += `${this.indent()}for (const auto& ${varName}
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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", {}
|
|
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})
|
|
155
|
-
const restValueCode = `jspp::Access::get_rest_object(${valueCode}, std::vector<
|
|
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 {
|