@ugo-studio/jspp 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +25 -25
- package/README.md +20 -12
- package/dist/cli/args.js +22 -0
- package/dist/cli/compiler.js +53 -0
- package/dist/cli/index.js +43 -107
- package/dist/cli/pch.js +71 -0
- package/dist/cli/runner.js +23 -0
- package/dist/cli/spinner.js +27 -11
- package/dist/cli/transpiler.js +20 -0
- package/dist/cli/utils.js +59 -0
- package/dist/cli/wasm.js +70 -0
- package/dist/index.js +17 -6
- package/dist/{analysis → interpreter/analysis}/scope.js +38 -3
- package/dist/{analysis → interpreter/analysis}/typeAnalyzer.js +563 -28
- package/dist/{core → interpreter/core}/codegen/class-handlers.js +1 -1
- package/dist/{core → interpreter/core}/codegen/control-flow-handlers.js +12 -11
- package/dist/{core → interpreter/core}/codegen/declaration-handlers.js +28 -9
- package/dist/{core → interpreter/core}/codegen/destructuring-handlers.js +9 -4
- package/dist/{core → interpreter/core}/codegen/expression-handlers.js +82 -88
- package/dist/{core → interpreter/core}/codegen/function-handlers.js +159 -46
- package/dist/{core → interpreter/core}/codegen/helpers.js +170 -25
- package/dist/interpreter/core/codegen/index.js +156 -0
- package/dist/{core → interpreter/core}/codegen/literal-handlers.js +9 -0
- package/dist/{core → interpreter/core}/codegen/statement-handlers.js +47 -7
- package/package.json +6 -4
- package/scripts/precompile-headers.ts +293 -50
- package/scripts/setup-compiler.ts +63 -63
- package/scripts/setup-emsdk.ts +114 -0
- package/src/prelude/any_value.cpp +888 -0
- package/src/prelude/any_value.hpp +29 -24
- package/src/prelude/{exception_helpers.hpp → exception.cpp} +53 -53
- package/src/prelude/exception.hpp +27 -27
- package/src/prelude/iterator_instantiations.hpp +10 -0
- package/src/prelude/{index.hpp → jspp.hpp} +13 -17
- package/src/prelude/library/array.cpp +191 -0
- package/src/prelude/library/array.hpp +5 -178
- package/src/prelude/library/boolean.cpp +30 -0
- package/src/prelude/library/boolean.hpp +14 -0
- package/src/prelude/library/console.cpp +125 -0
- package/src/prelude/library/console.hpp +9 -97
- package/src/prelude/library/error.cpp +100 -0
- package/src/prelude/library/error.hpp +8 -108
- package/src/prelude/library/function.cpp +69 -0
- package/src/prelude/library/function.hpp +6 -5
- package/src/prelude/library/global.cpp +98 -0
- package/src/prelude/library/global.hpp +12 -28
- package/src/prelude/library/global_usings.hpp +15 -0
- package/src/prelude/library/math.cpp +261 -0
- package/src/prelude/library/math.hpp +8 -288
- package/src/prelude/library/object.cpp +379 -0
- package/src/prelude/library/object.hpp +5 -267
- package/src/prelude/library/performance.cpp +21 -0
- package/src/prelude/library/performance.hpp +5 -20
- package/src/prelude/library/process.cpp +38 -0
- package/src/prelude/library/process.hpp +3 -31
- package/src/prelude/library/promise.cpp +131 -0
- package/src/prelude/library/promise.hpp +5 -116
- package/src/prelude/library/symbol.cpp +56 -0
- package/src/prelude/library/symbol.hpp +5 -46
- package/src/prelude/library/timer.cpp +88 -0
- package/src/prelude/library/timer.hpp +11 -87
- package/src/prelude/runtime.cpp +19 -0
- package/src/prelude/types.hpp +26 -20
- package/src/prelude/utils/access.hpp +123 -32
- package/src/prelude/utils/assignment_operators.hpp +119 -99
- package/src/prelude/utils/log_any_value/array.hpp +61 -40
- package/src/prelude/utils/log_any_value/function.hpp +39 -39
- package/src/prelude/utils/log_any_value/log_any_value.hpp +1 -1
- package/src/prelude/utils/log_any_value/object.hpp +60 -3
- package/src/prelude/utils/log_any_value/primitives.hpp +1 -1
- package/src/prelude/utils/operators.hpp +109 -94
- package/src/prelude/utils/operators_native.hpp +349 -0
- package/src/prelude/utils/well_known_symbols.hpp +24 -24
- package/src/prelude/values/array.cpp +1399 -0
- package/src/prelude/values/array.hpp +4 -0
- package/src/prelude/values/async_iterator.cpp +251 -0
- package/src/prelude/values/async_iterator.hpp +60 -32
- package/src/prelude/values/boolean.cpp +64 -0
- package/src/prelude/values/function.cpp +262 -0
- package/src/prelude/values/function.hpp +10 -30
- package/src/prelude/values/iterator.cpp +309 -0
- package/src/prelude/values/iterator.hpp +33 -64
- package/src/prelude/values/number.cpp +221 -0
- package/src/prelude/values/object.cpp +200 -0
- package/src/prelude/values/object.hpp +4 -0
- package/src/prelude/values/promise.cpp +479 -0
- package/src/prelude/values/promise.hpp +9 -2
- package/src/prelude/values/prototypes/array.hpp +46 -1348
- package/src/prelude/values/prototypes/async_iterator.hpp +19 -61
- package/src/prelude/values/prototypes/boolean.hpp +24 -0
- package/src/prelude/values/prototypes/function.hpp +7 -46
- package/src/prelude/values/prototypes/iterator.hpp +15 -191
- package/src/prelude/values/prototypes/number.hpp +30 -210
- package/src/prelude/values/prototypes/object.hpp +7 -23
- package/src/prelude/values/prototypes/promise.hpp +8 -186
- package/src/prelude/values/prototypes/string.hpp +28 -553
- package/src/prelude/values/prototypes/symbol.hpp +9 -70
- package/src/prelude/values/shape.hpp +52 -52
- package/src/prelude/values/string.cpp +485 -0
- package/src/prelude/values/symbol.cpp +89 -0
- package/src/prelude/values/symbol.hpp +101 -101
- package/dist/cli/file-utils.js +0 -20
- package/dist/cli-utils/args.js +0 -59
- package/dist/cli-utils/colors.js +0 -9
- package/dist/cli-utils/file-utils.js +0 -20
- package/dist/cli-utils/spinner.js +0 -55
- package/dist/cli.js +0 -153
- package/dist/core/codegen/index.js +0 -86
- package/src/prelude/any_value_access.hpp +0 -170
- package/src/prelude/any_value_defines.hpp +0 -190
- package/src/prelude/any_value_helpers.hpp +0 -374
- package/src/prelude/utils/operators_primitive.hpp +0 -337
- package/src/prelude/values/helpers/array.hpp +0 -199
- package/src/prelude/values/helpers/async_iterator.hpp +0 -275
- package/src/prelude/values/helpers/function.hpp +0 -109
- package/src/prelude/values/helpers/iterator.hpp +0 -145
- package/src/prelude/values/helpers/object.hpp +0 -104
- package/src/prelude/values/helpers/promise.hpp +0 -254
- package/src/prelude/values/helpers/string.hpp +0 -37
- package/src/prelude/values/helpers/symbol.hpp +0 -21
- /package/dist/{ast → interpreter/ast}/symbols.js +0 -0
- /package/dist/{ast → interpreter/ast}/types.js +0 -0
- /package/dist/{core → interpreter/core}/codegen/visitor.js +0 -0
- /package/dist/{core → interpreter/core}/constants.js +0 -0
- /package/dist/{core → interpreter/core}/error.js +0 -0
- /package/dist/{core → interpreter/core}/parser.js +0 -0
- /package/dist/{core → interpreter/core}/traverser.js +0 -0
|
@@ -35,7 +35,7 @@ export function visitClassDeclaration(node, context) {
|
|
|
35
35
|
constructorLambda = this.generateWrappedLambda(this.generateLambdaComponents(constructor, {
|
|
36
36
|
...classContext,
|
|
37
37
|
isInsideFunction: true,
|
|
38
|
-
|
|
38
|
+
functionName: className,
|
|
39
39
|
}, { isClass: true }));
|
|
40
40
|
}
|
|
41
41
|
else {
|
|
@@ -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
|
});
|
|
@@ -475,13 +476,13 @@ export function visitSwitchStatement(node, context) {
|
|
|
475
476
|
let condition = "";
|
|
476
477
|
if (firstIf) {
|
|
477
478
|
condition =
|
|
478
|
-
`(${fallthroughVar} || jspp::
|
|
479
|
+
`(${fallthroughVar} || jspp::is_strictly_equal_to_native(${switchValueVar}, ${caseExprCode}))`;
|
|
479
480
|
code += `${this.indent()}if ${condition} {\n`;
|
|
480
481
|
firstIf = false;
|
|
481
482
|
}
|
|
482
483
|
else {
|
|
483
484
|
condition =
|
|
484
|
-
`(${fallthroughVar} || jspp::
|
|
485
|
+
`(${fallthroughVar} || jspp::is_strictly_equal_to_native(${switchValueVar}, ${caseExprCode}))`;
|
|
485
486
|
code += `${this.indent()}if ${condition} {\n`;
|
|
486
487
|
}
|
|
487
488
|
this.indentationLevel++;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import ts from "typescript";
|
|
2
|
+
import { RESERVED_VAR_NAMES } 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.has(name)) {
|
|
22
|
+
throw new CompilerError(`Cannot declare a variable named '${name}'.`, 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
|
}
|
|
@@ -49,12 +53,14 @@ export function visitVariableDeclaration(node, context) {
|
|
|
49
53
|
(ts.isFunctionExpression(initExpr) && !initExpr.name)) {
|
|
50
54
|
const initContext = {
|
|
51
55
|
...context,
|
|
52
|
-
|
|
56
|
+
functionName: name, // Use the variable name as function name
|
|
53
57
|
};
|
|
54
58
|
const nativeName = this.generateUniqueName(`__${name}_native_`, context.localScopeSymbols, context.globalScopeSymbols);
|
|
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
|
});
|
|
@@ -85,16 +92,28 @@ export function visitVariableDeclaration(node, context) {
|
|
|
85
92
|
return nativeLambdaCode;
|
|
86
93
|
}
|
|
87
94
|
}
|
|
88
|
-
initializer =
|
|
95
|
+
initializer = initText;
|
|
89
96
|
}
|
|
90
97
|
const isLetOrConst = (varDecl.parent.flags & (ts.NodeFlags.Let | ts.NodeFlags.Const)) !== 0;
|
|
91
98
|
const shouldDeref = context.derefBeforeAssignment &&
|
|
92
99
|
(!context.localScopeSymbols.has(name));
|
|
93
|
-
|
|
100
|
+
let assignmentTarget = shouldDeref
|
|
94
101
|
? this.getDerefCode(name, name, context, typeInfo)
|
|
95
|
-
: (typeInfo
|
|
102
|
+
: (typeInfo?.needsHeapAllocation && !shouldSkipDeref
|
|
96
103
|
? `*${name}`
|
|
97
104
|
: name);
|
|
105
|
+
const sym = context.localScopeSymbols.get(name) ||
|
|
106
|
+
context.globalScopeSymbols.get(name);
|
|
107
|
+
if (sym?.checks.skippedHoisting) {
|
|
108
|
+
assignmentTarget = typeInfo?.needsHeapAllocation && !shouldSkipDeref
|
|
109
|
+
? `auto ${name}`
|
|
110
|
+
: `jspp::AnyValue ${name}`;
|
|
111
|
+
if (initializer) {
|
|
112
|
+
initializer = typeInfo?.needsHeapAllocation && !shouldSkipDeref
|
|
113
|
+
? `std::make_shared<jspp::AnyValue>(${initializer})`
|
|
114
|
+
: initializer;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
98
117
|
if (nativeLambdaCode)
|
|
99
118
|
nativeLambdaCode += `;\n${this.indent()}`;
|
|
100
119
|
if (isLetOrConst) {
|
|
@@ -109,21 +128,21 @@ export function visitVariableDeclaration(node, context) {
|
|
|
109
128
|
return `${nativeLambdaCode}${assignmentTarget} = jspp::Constants::UNDEFINED`;
|
|
110
129
|
}
|
|
111
130
|
}
|
|
112
|
-
return `${nativeLambdaCode}${assignmentTarget}${initializer}`;
|
|
131
|
+
return `${nativeLambdaCode}${assignmentTarget} = ${initializer}`;
|
|
113
132
|
}
|
|
114
133
|
// For 'var', it's a bit more complex.
|
|
115
134
|
if (context.isAssignmentOnly) {
|
|
116
135
|
if (!initializer)
|
|
117
136
|
return "";
|
|
118
|
-
return `${nativeLambdaCode}${assignmentTarget}${initializer}`;
|
|
137
|
+
return `${nativeLambdaCode}${assignmentTarget} = ${initializer}`;
|
|
119
138
|
}
|
|
120
139
|
else {
|
|
121
140
|
// This case should not be hit with the new hoisting logic,
|
|
122
141
|
// but is kept for safety.
|
|
123
142
|
const initValue = initializer
|
|
124
|
-
? initializer
|
|
143
|
+
? initializer
|
|
125
144
|
: "jspp::Constants::UNDEFINED";
|
|
126
|
-
if (typeInfo
|
|
145
|
+
if (typeInfo?.needsHeapAllocation) {
|
|
127
146
|
return `auto ${name} = std::make_shared<jspp::AnyValue>(${initValue})`;
|
|
128
147
|
}
|
|
129
148
|
else {
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import ts from "typescript";
|
|
2
|
+
import { RESERVED_VAR_NAMES } 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.has(name)) {
|
|
17
|
+
throw new CompilerError(`Cannot destructure to a variable named '${name}'.`, 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 {
|
|
@@ -84,11 +84,11 @@ export function visitObjectLiteralExpression(node, context) {
|
|
|
84
84
|
ts.isMethodDeclaration(prop) || ts.isGetAccessor(prop) ||
|
|
85
85
|
ts.isSetAccessor(prop) || ts.isSpreadAssignment(prop))) {
|
|
86
86
|
// Empty object
|
|
87
|
-
return `jspp::AnyValue::
|
|
87
|
+
return `jspp::AnyValue::make_object({}).set_prototype(::Object.get_own_property("prototype"))`;
|
|
88
88
|
}
|
|
89
89
|
let code = `([&]() {\n`;
|
|
90
90
|
code +=
|
|
91
|
-
`${this.indent()} auto ${objVar} = jspp::AnyValue::
|
|
91
|
+
`${this.indent()} auto ${objVar} = jspp::AnyValue::make_object({}).set_prototype(::Object.get_own_property("prototype"));\n`;
|
|
92
92
|
this.indentationLevel++;
|
|
93
93
|
for (const prop of properties) {
|
|
94
94
|
if (ts.isPropertyAssignment(prop)) {
|
|
@@ -200,7 +200,7 @@ export function visitArrayLiteralExpression(node, context) {
|
|
|
200
200
|
const elementsSpan = elements.length > 0
|
|
201
201
|
? `std::span<const jspp::AnyValue>((const jspp::AnyValue[]){${elementsJoined}}, ${elements.length})`
|
|
202
202
|
: "std::span<const jspp::AnyValue>{}";
|
|
203
|
-
return `jspp::AnyValue::
|
|
203
|
+
return `jspp::AnyValue::make_array(${elementsSpan}).set_prototype(::Array.get_own_property("prototype"))`;
|
|
204
204
|
}
|
|
205
205
|
const arrVar = this.generateUniqueName("__arr_", this.getDeclaredSymbols(node));
|
|
206
206
|
let code = `([&]() {\n`;
|
|
@@ -238,7 +238,7 @@ export function visitArrayLiteralExpression(node, context) {
|
|
|
238
238
|
}
|
|
239
239
|
this.indentationLevel--;
|
|
240
240
|
code +=
|
|
241
|
-
`${this.indent()} return jspp::AnyValue::
|
|
241
|
+
`${this.indent()} return jspp::AnyValue::make_array(std::move(${arrVar})).set_prototype(::Array.get_own_property("prototype"));\n`;
|
|
242
242
|
code += `${this.indent()}})()`;
|
|
243
243
|
return code;
|
|
244
244
|
}
|
|
@@ -254,7 +254,7 @@ export function visitPrefixUnaryExpression(node, context) {
|
|
|
254
254
|
if (context.derefBeforeAssignment) {
|
|
255
255
|
target = this.getDerefCode(operand, operand, context, typeInfo);
|
|
256
256
|
}
|
|
257
|
-
else if (typeInfo
|
|
257
|
+
else if (typeInfo?.needsHeapAllocation) {
|
|
258
258
|
target = `*${operand}`;
|
|
259
259
|
}
|
|
260
260
|
}
|
|
@@ -277,7 +277,7 @@ export function visitPrefixUnaryExpression(node, context) {
|
|
|
277
277
|
if (context.derefBeforeAssignment) {
|
|
278
278
|
target = this.getDerefCode(operand, operand, context, typeInfo);
|
|
279
279
|
}
|
|
280
|
-
else if (typeInfo
|
|
280
|
+
else if (typeInfo?.needsHeapAllocation) {
|
|
281
281
|
target = `*${operand}`;
|
|
282
282
|
}
|
|
283
283
|
}
|
|
@@ -296,7 +296,7 @@ export function visitPostfixUnaryExpression(node, context) {
|
|
|
296
296
|
if (context.derefBeforeAssignment) {
|
|
297
297
|
target = this.getDerefCode(operand, operand, context, typeInfo);
|
|
298
298
|
}
|
|
299
|
-
else if (typeInfo
|
|
299
|
+
else if (typeInfo?.needsHeapAllocation) {
|
|
300
300
|
target = `*${operand}`;
|
|
301
301
|
}
|
|
302
302
|
}
|
|
@@ -381,7 +381,6 @@ export function visitBinaryExpression(node, context) {
|
|
|
381
381
|
const op = opToken.getText();
|
|
382
382
|
const visitContext = {
|
|
383
383
|
...context,
|
|
384
|
-
supportedNativeLiterals: undefined,
|
|
385
384
|
};
|
|
386
385
|
const assignmentOperators = [
|
|
387
386
|
ts.SyntaxKind.PlusEqualsToken,
|
|
@@ -409,7 +408,7 @@ export function visitBinaryExpression(node, context) {
|
|
|
409
408
|
if (ts.isIdentifier(binExpr.left)) {
|
|
410
409
|
const scope = this.getScopeForNode(binExpr.left);
|
|
411
410
|
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.left.text, scope);
|
|
412
|
-
target = typeInfo
|
|
411
|
+
target = typeInfo?.needsHeapAllocation
|
|
413
412
|
? `*${leftText}`
|
|
414
413
|
: leftText;
|
|
415
414
|
return `${target} = jspp::unsigned_right_shift(${target}, ${rightText})`;
|
|
@@ -422,7 +421,7 @@ export function visitBinaryExpression(node, context) {
|
|
|
422
421
|
if (ts.isIdentifier(binExpr.left)) {
|
|
423
422
|
const scope = this.getScopeForNode(binExpr.left);
|
|
424
423
|
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.left.text, scope);
|
|
425
|
-
target = typeInfo
|
|
424
|
+
target = typeInfo?.needsHeapAllocation
|
|
426
425
|
? `*${leftText}`
|
|
427
426
|
: leftText;
|
|
428
427
|
return `${target} = jspp::pow(${target}, ${rightText})`;
|
|
@@ -438,7 +437,7 @@ export function visitBinaryExpression(node, context) {
|
|
|
438
437
|
if (ts.isIdentifier(binExpr.left)) {
|
|
439
438
|
const scope = this.getScopeForNode(binExpr.left);
|
|
440
439
|
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.left.text, scope);
|
|
441
|
-
target = typeInfo
|
|
440
|
+
target = typeInfo?.needsHeapAllocation
|
|
442
441
|
? `*${leftText}`
|
|
443
442
|
: leftText;
|
|
444
443
|
if (opToken.kind === ts.SyntaxKind.AmpersandAmpersandEqualsToken) {
|
|
@@ -468,7 +467,7 @@ export function visitBinaryExpression(node, context) {
|
|
|
468
467
|
if (context.derefBeforeAssignment) {
|
|
469
468
|
target = this.getDerefCode(leftText, leftText, visitContext, typeInfo);
|
|
470
469
|
}
|
|
471
|
-
else if (typeInfo
|
|
470
|
+
else if (typeInfo?.needsHeapAllocation) {
|
|
472
471
|
target = `*${leftText}`;
|
|
473
472
|
}
|
|
474
473
|
}
|
|
@@ -575,7 +574,7 @@ export function visitBinaryExpression(node, context) {
|
|
|
575
574
|
}
|
|
576
575
|
const target = context.derefBeforeAssignment
|
|
577
576
|
? this.getDerefCode(leftText, leftText, visitContext, typeInfo)
|
|
578
|
-
: (typeInfo
|
|
577
|
+
: (typeInfo?.needsHeapAllocation ? `*${leftText}` : leftText);
|
|
579
578
|
// Update scope symbols on variable re-assignment
|
|
580
579
|
// Reset features
|
|
581
580
|
if (ts.isIdentifier(binExpr.left)) {
|
|
@@ -608,7 +607,8 @@ export function visitBinaryExpression(node, context) {
|
|
|
608
607
|
finalLeft = this.getDerefCode(leftText, this.getJsVarName(binExpr.left), visitContext, typeInfo);
|
|
609
608
|
}
|
|
610
609
|
// Number optimizations
|
|
611
|
-
|
|
610
|
+
const nodeType = this.typeAnalyzer.inferNodeReturnType(binExpr.left);
|
|
611
|
+
if (nodeType === "number") {
|
|
612
612
|
finalLeft = `${finalLeft}.as_double()`;
|
|
613
613
|
}
|
|
614
614
|
}
|
|
@@ -624,7 +624,8 @@ export function visitBinaryExpression(node, context) {
|
|
|
624
624
|
finalRight = this.getDerefCode(rightText, this.getJsVarName(binExpr.right), visitContext, typeInfo);
|
|
625
625
|
}
|
|
626
626
|
// Number optimizations
|
|
627
|
-
|
|
627
|
+
const nodeType = this.typeAnalyzer.inferNodeReturnType(binExpr.right);
|
|
628
|
+
if (nodeType === "number") {
|
|
628
629
|
finalRight = `${finalRight}.as_double()`;
|
|
629
630
|
}
|
|
630
631
|
}
|
|
@@ -643,100 +644,82 @@ export function visitBinaryExpression(node, context) {
|
|
|
643
644
|
if (opToken.kind === ts.SyntaxKind.QuestionQuestionToken) {
|
|
644
645
|
return `jspp::nullish_coalesce(${finalLeft}, ${finalRight})`;
|
|
645
646
|
}
|
|
646
|
-
const isLiteral = (n) => ts.isNumericLiteral(n);
|
|
647
|
-
const supportsNativeBoolean = context.supportedNativeLiterals?.includes("boolean") || false;
|
|
648
647
|
// Native values for lhs and rhs
|
|
649
|
-
const literalLeft =
|
|
648
|
+
const literalLeft = ts.isNumericLiteral(binExpr.left)
|
|
650
649
|
? binExpr.left.getText()
|
|
651
650
|
: finalLeft;
|
|
652
|
-
const literalRight =
|
|
651
|
+
const literalRight = ts.isNumericLiteral(binExpr.right)
|
|
653
652
|
? binExpr.right.getText()
|
|
654
653
|
: finalRight;
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
if (
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
funcName = "jspp::less_than_primitive";
|
|
673
|
-
}
|
|
674
|
-
if (opToken.kind === ts.SyntaxKind.LessThanEqualsToken) {
|
|
675
|
-
funcName = "jspp::less_than_or_equal_primitive";
|
|
676
|
-
}
|
|
677
|
-
if (opToken.kind === ts.SyntaxKind.GreaterThanToken) {
|
|
678
|
-
funcName = "jspp::greater_than_primitive";
|
|
679
|
-
}
|
|
680
|
-
if (opToken.kind === ts.SyntaxKind.GreaterThanEqualsToken) {
|
|
681
|
-
funcName = "jspp::greater_than_or_equal_primitive";
|
|
654
|
+
let supportsNativeValue = false;
|
|
655
|
+
const exprReturnType = this.typeAnalyzer.inferNodeReturnType(node);
|
|
656
|
+
if (exprReturnType === "boolean" &&
|
|
657
|
+
(ts.isIfStatement(node.parent) ||
|
|
658
|
+
ts.isConditionalExpression(node.parent))) {
|
|
659
|
+
supportsNativeValue = true;
|
|
660
|
+
}
|
|
661
|
+
else if (exprReturnType === "number" &&
|
|
662
|
+
context.isInsideNativeLambda &&
|
|
663
|
+
context.isInsideFunction) {
|
|
664
|
+
const funcDecl = this
|
|
665
|
+
.findEnclosingFunctionDeclarationFromReturnStatement(node);
|
|
666
|
+
if (funcDecl) {
|
|
667
|
+
const funcReturnType = this.typeAnalyzer.inferFunctionReturnType(funcDecl);
|
|
668
|
+
if (funcReturnType === "number") {
|
|
669
|
+
supportsNativeValue = true;
|
|
670
|
+
}
|
|
682
671
|
}
|
|
683
|
-
// For C++ primitive literals, standard operators are fine if they map directly,
|
|
684
|
-
// but we are safe using our functions (which handle doubles correctly).
|
|
685
|
-
// Actually, for pure numeric literals like "1 < 2", we can leave it as is if we want optimization,
|
|
686
|
-
// but consistency is safer.
|
|
687
|
-
// Let's stick to valid C++ syntax for literals if possible to avoid overhead?
|
|
688
|
-
// jspp::less_than(1, 2) works.
|
|
689
|
-
return `${funcName}(${literalLeft}, ${literalRight})`;
|
|
690
672
|
}
|
|
673
|
+
const method = supportsNativeValue ? "_native" : "";
|
|
691
674
|
// Return boxed value
|
|
692
675
|
if (opToken.kind === ts.SyntaxKind.EqualsEqualsEqualsToken) {
|
|
693
|
-
return `jspp::is_strictly_equal_to(${literalLeft}, ${literalRight})`;
|
|
676
|
+
return `jspp::is_strictly_equal_to${method}(${literalLeft}, ${literalRight})`;
|
|
694
677
|
}
|
|
695
678
|
if (opToken.kind === ts.SyntaxKind.EqualsEqualsToken) {
|
|
696
|
-
return `jspp::is_equal_to(${literalLeft}, ${literalRight})`;
|
|
679
|
+
return `jspp::is_equal_to${method}(${literalLeft}, ${literalRight})`;
|
|
697
680
|
}
|
|
698
681
|
if (opToken.kind === ts.SyntaxKind.ExclamationEqualsEqualsToken) {
|
|
699
|
-
return `jspp::not_strictly_equal_to(${literalLeft}, ${literalRight})`;
|
|
682
|
+
return `jspp::not_strictly_equal_to${method}(${literalLeft}, ${literalRight})`;
|
|
700
683
|
}
|
|
701
684
|
if (opToken.kind === ts.SyntaxKind.ExclamationEqualsToken) {
|
|
702
|
-
return `jspp::not_equal_to(${literalLeft}, ${literalRight})`;
|
|
685
|
+
return `jspp::not_equal_to${method}(${literalLeft}, ${literalRight})`;
|
|
703
686
|
}
|
|
704
687
|
if (opToken.kind === ts.SyntaxKind.AsteriskAsteriskToken) {
|
|
705
|
-
return `jspp::pow(${literalLeft}, ${literalRight})`;
|
|
688
|
+
return `jspp::pow${method}(${literalLeft}, ${literalRight})`;
|
|
706
689
|
}
|
|
707
690
|
if (opToken.kind === ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken) {
|
|
708
|
-
return `jspp::unsigned_right_shift(${literalLeft}, ${literalRight})`;
|
|
691
|
+
return `jspp::unsigned_right_shift${method}(${literalLeft}, ${literalRight})`;
|
|
709
692
|
}
|
|
710
693
|
// For other arithmetic and bitwise operations, use native operations if possible
|
|
711
694
|
switch (op) {
|
|
712
695
|
case "+":
|
|
713
|
-
return `jspp::add(${literalLeft}, ${literalRight})`;
|
|
696
|
+
return `jspp::add${method}(${literalLeft}, ${literalRight})`;
|
|
714
697
|
case "-":
|
|
715
|
-
return `jspp::sub(${literalLeft}, ${literalRight})`;
|
|
698
|
+
return `jspp::sub${method}(${literalLeft}, ${literalRight})`;
|
|
716
699
|
case "*":
|
|
717
|
-
return `jspp::mul(${literalLeft}, ${literalRight})`;
|
|
700
|
+
return `jspp::mul${method}(${literalLeft}, ${literalRight})`;
|
|
718
701
|
case "/":
|
|
719
|
-
return `jspp::div(${literalLeft}, ${literalRight})`;
|
|
702
|
+
return `jspp::div${method}(${literalLeft}, ${literalRight})`;
|
|
720
703
|
case "%":
|
|
721
|
-
return `jspp::mod(${literalLeft}, ${literalRight})`;
|
|
704
|
+
return `jspp::mod${method}(${literalLeft}, ${literalRight})`;
|
|
722
705
|
case "^":
|
|
723
|
-
return `jspp::bitwise_xor(${literalLeft}, ${literalRight})`;
|
|
706
|
+
return `jspp::bitwise_xor${method}(${literalLeft}, ${literalRight})`;
|
|
724
707
|
case "&":
|
|
725
|
-
return `jspp::bitwise_and(${literalLeft}, ${literalRight})`;
|
|
708
|
+
return `jspp::bitwise_and${method}(${literalLeft}, ${literalRight})`;
|
|
726
709
|
case "|":
|
|
727
|
-
return `jspp::bitwise_or(${literalLeft}, ${literalRight})`;
|
|
710
|
+
return `jspp::bitwise_or${method}(${literalLeft}, ${literalRight})`;
|
|
728
711
|
case "<<":
|
|
729
|
-
return `jspp::left_shift(${literalLeft}, ${literalRight})`;
|
|
712
|
+
return `jspp::left_shift${method}(${literalLeft}, ${literalRight})`;
|
|
730
713
|
case ">>":
|
|
731
|
-
return `jspp::right_shift(${literalLeft}, ${literalRight})`;
|
|
714
|
+
return `jspp::right_shift${method}(${literalLeft}, ${literalRight})`;
|
|
732
715
|
case "<":
|
|
733
|
-
return `jspp::less_than(${literalLeft}, ${literalRight})`;
|
|
716
|
+
return `jspp::less_than${method}(${literalLeft}, ${literalRight})`;
|
|
734
717
|
case ">":
|
|
735
|
-
return `jspp::greater_than(${literalLeft}, ${literalRight})`;
|
|
718
|
+
return `jspp::greater_than${method}(${literalLeft}, ${literalRight})`;
|
|
736
719
|
case "<=":
|
|
737
|
-
return `jspp::less_than_or_equal(${literalLeft}, ${literalRight})`;
|
|
720
|
+
return `jspp::less_than_or_equal${method}(${literalLeft}, ${literalRight})`;
|
|
738
721
|
case ">=":
|
|
739
|
-
return `jspp::greater_than_or_equal(${literalLeft}, ${literalRight})`;
|
|
722
|
+
return `jspp::greater_than_or_equal${method}(${literalLeft}, ${literalRight})`;
|
|
740
723
|
}
|
|
741
724
|
return `/* Unhandled Operator: ${finalLeft} ${op} ${finalRight} */`; // Default fallback
|
|
742
725
|
}
|
|
@@ -744,10 +727,7 @@ export function visitConditionalExpression(node, context) {
|
|
|
744
727
|
const condExpr = node;
|
|
745
728
|
const isBinaryExpression = ts.isBinaryExpression(condExpr.condition) &&
|
|
746
729
|
constants.booleanOperators.includes(condExpr.condition.operatorToken.kind);
|
|
747
|
-
const condition = this.visit(condExpr.condition,
|
|
748
|
-
...context,
|
|
749
|
-
supportedNativeLiterals: isBinaryExpression ? ["boolean"] : undefined,
|
|
750
|
-
});
|
|
730
|
+
const condition = this.visit(condExpr.condition, context);
|
|
751
731
|
const whenTrueStmt = this.visit(condExpr.whenTrue, {
|
|
752
732
|
...context,
|
|
753
733
|
isFunctionBody: false,
|
|
@@ -1023,6 +1003,7 @@ export function visitCallExpression(node, context) {
|
|
|
1023
1003
|
if (nativeFeature && nativeFeature.type === "lambda") {
|
|
1024
1004
|
const nativeName = nativeFeature.name;
|
|
1025
1005
|
const parameters = nativeFeature.parameters || [];
|
|
1006
|
+
const argumentKeywordIsUsed = nativeFeature.argumentKeywordIsUsed;
|
|
1026
1007
|
if (!hasSpread) {
|
|
1027
1008
|
let argsPart = "";
|
|
1028
1009
|
if (parameters) {
|
|
@@ -1041,10 +1022,17 @@ export function visitCallExpression(node, context) {
|
|
|
1041
1022
|
.filter((_, i) => !parameters[i]?.dotDotDotToken).join(", ");
|
|
1042
1023
|
if (argsText)
|
|
1043
1024
|
argsPart += `, ${argsText}`;
|
|
1044
|
-
if (argsArray.length
|
|
1025
|
+
if (argsArray.length >= parameters.length &&
|
|
1045
1026
|
!!parameters[parameters.length - 1]?.dotDotDotToken) {
|
|
1046
1027
|
const restArgsText = `jspp::AnyValue::make_array(std::vector<jspp::AnyValue>{${argsArray.slice(parameters.length - 1).join(", ")}})`;
|
|
1047
|
-
argsPart += `, ${restArgsText}`;
|
|
1028
|
+
argsPart += `, ${restArgsText}${argumentKeywordIsUsed ? `, ${argsArray.length}` : ""}`;
|
|
1029
|
+
}
|
|
1030
|
+
else if (argsArray.length > parameters.length &&
|
|
1031
|
+
argumentKeywordIsUsed) {
|
|
1032
|
+
const restArgsArray = argsArray.slice(parameters.length);
|
|
1033
|
+
const nativeRestArgsText = `std::span<const jspp::AnyValue>((const jspp::AnyValue[]){${restArgsArray.join(", ")}}, ${restArgsArray.length})`;
|
|
1034
|
+
argsPart +=
|
|
1035
|
+
`, ${argsArray.length}, ${nativeRestArgsText}`;
|
|
1048
1036
|
}
|
|
1049
1037
|
}
|
|
1050
1038
|
const callImplementation = `${nativeName}(jspp::Constants::UNDEFINED${argsPart})`;
|
|
@@ -1070,28 +1058,34 @@ export function visitCallExpression(node, context) {
|
|
|
1070
1058
|
if (!p)
|
|
1071
1059
|
continue;
|
|
1072
1060
|
if (p.dotDotDotToken) {
|
|
1073
|
-
callArgs.push(`jspp::AnyValue::make_array(std::vector<jspp::AnyValue>(${argsVar}.begin() + std::min((size_t)${i}, ${argsVar}.size()), ${argsVar}.end()))`);
|
|
1061
|
+
callArgs.push(`jspp::AnyValue::make_array(std::vector<jspp::AnyValue>(${argsVar}.begin() + std::min((std::size_t)${i}, ${argsVar}.size()), ${argsVar}.end()))`);
|
|
1074
1062
|
}
|
|
1075
1063
|
else {
|
|
1076
1064
|
callArgs.push(`(${argsVar}.size() > ${i} ? ${argsVar}[${i}] : jspp::Constants::UNDEFINED)`);
|
|
1077
1065
|
}
|
|
1078
1066
|
}
|
|
1079
|
-
|
|
1067
|
+
const callArgsPart = callArgs.length > 0
|
|
1068
|
+
? `, ${callArgs.join(", ")}`
|
|
1069
|
+
: "";
|
|
1070
|
+
const totalArgsSizePart = argumentKeywordIsUsed
|
|
1071
|
+
? `, ${argsVar}.size()`
|
|
1072
|
+
: "";
|
|
1073
|
+
let callImplementation = `${nativeName}(jspp::Constants::UNDEFINED${callArgsPart}${totalArgsSizePart})`;
|
|
1080
1074
|
if (symbol.features.isGenerator) {
|
|
1081
1075
|
if (symbol.features.isAsync) {
|
|
1082
|
-
|
|
1083
|
-
`jspp::AnyValue::from_async_iterator(${
|
|
1076
|
+
callImplementation =
|
|
1077
|
+
`jspp::AnyValue::from_async_iterator(${callImplementation})`;
|
|
1084
1078
|
}
|
|
1085
1079
|
else {
|
|
1086
|
-
|
|
1087
|
-
`jspp::AnyValue::from_iterator(${
|
|
1080
|
+
callImplementation =
|
|
1081
|
+
`jspp::AnyValue::from_iterator(${callImplementation})`;
|
|
1088
1082
|
}
|
|
1089
1083
|
}
|
|
1090
1084
|
else if (symbol.features.isAsync) {
|
|
1091
|
-
|
|
1092
|
-
`jspp::AnyValue::from_promise(${
|
|
1085
|
+
callImplementation =
|
|
1086
|
+
`jspp::AnyValue::from_promise(${callImplementation})`;
|
|
1093
1087
|
}
|
|
1094
|
-
code += `${this.indent()}return ${
|
|
1088
|
+
code += `${this.indent()}return ${callImplementation};\n`;
|
|
1095
1089
|
this.indentationLevel--;
|
|
1096
1090
|
code += `${this.indent()}})()`;
|
|
1097
1091
|
return code;
|