@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
|
@@ -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)) {
|
|
@@ -644,7 +643,8 @@ export function visitBinaryExpression(node, context) {
|
|
|
644
643
|
return `jspp::nullish_coalesce(${finalLeft}, ${finalRight})`;
|
|
645
644
|
}
|
|
646
645
|
const isLiteral = (n) => ts.isNumericLiteral(n);
|
|
647
|
-
const supportsNativeBoolean =
|
|
646
|
+
const supportsNativeBoolean = ts.isIfStatement(node.parent) ||
|
|
647
|
+
ts.isConditionalExpression(node.parent);
|
|
648
648
|
// Native values for lhs and rhs
|
|
649
649
|
const literalLeft = isLiteral(binExpr.left)
|
|
650
650
|
? binExpr.left.getText()
|
|
@@ -744,10 +744,7 @@ export function visitConditionalExpression(node, context) {
|
|
|
744
744
|
const condExpr = node;
|
|
745
745
|
const isBinaryExpression = ts.isBinaryExpression(condExpr.condition) &&
|
|
746
746
|
constants.booleanOperators.includes(condExpr.condition.operatorToken.kind);
|
|
747
|
-
const condition = this.visit(condExpr.condition,
|
|
748
|
-
...context,
|
|
749
|
-
supportedNativeLiterals: isBinaryExpression ? ["boolean"] : undefined,
|
|
750
|
-
});
|
|
747
|
+
const condition = this.visit(condExpr.condition, context);
|
|
751
748
|
const whenTrueStmt = this.visit(condExpr.whenTrue, {
|
|
752
749
|
...context,
|
|
753
750
|
isFunctionBody: false,
|
|
@@ -1023,6 +1020,7 @@ export function visitCallExpression(node, context) {
|
|
|
1023
1020
|
if (nativeFeature && nativeFeature.type === "lambda") {
|
|
1024
1021
|
const nativeName = nativeFeature.name;
|
|
1025
1022
|
const parameters = nativeFeature.parameters || [];
|
|
1023
|
+
const argumentKeywordIsUsed = nativeFeature.argumentKeywordIsUsed;
|
|
1026
1024
|
if (!hasSpread) {
|
|
1027
1025
|
let argsPart = "";
|
|
1028
1026
|
if (parameters) {
|
|
@@ -1041,10 +1039,17 @@ export function visitCallExpression(node, context) {
|
|
|
1041
1039
|
.filter((_, i) => !parameters[i]?.dotDotDotToken).join(", ");
|
|
1042
1040
|
if (argsText)
|
|
1043
1041
|
argsPart += `, ${argsText}`;
|
|
1044
|
-
if (argsArray.length
|
|
1042
|
+
if (argsArray.length >= parameters.length &&
|
|
1045
1043
|
!!parameters[parameters.length - 1]?.dotDotDotToken) {
|
|
1046
1044
|
const restArgsText = `jspp::AnyValue::make_array(std::vector<jspp::AnyValue>{${argsArray.slice(parameters.length - 1).join(", ")}})`;
|
|
1047
|
-
argsPart += `, ${restArgsText}`;
|
|
1045
|
+
argsPart += `, ${restArgsText}${argumentKeywordIsUsed ? `, ${argsArray.length}` : ""}`;
|
|
1046
|
+
}
|
|
1047
|
+
else if (argsArray.length > parameters.length &&
|
|
1048
|
+
argumentKeywordIsUsed) {
|
|
1049
|
+
const restArgsArray = argsArray.slice(parameters.length);
|
|
1050
|
+
const nativeRestArgsText = `std::span<const jspp::AnyValue>((const jspp::AnyValue[]){${restArgsArray.join(", ")}}, ${restArgsArray.length})`;
|
|
1051
|
+
argsPart +=
|
|
1052
|
+
`, ${argsArray.length}, ${nativeRestArgsText}`;
|
|
1048
1053
|
}
|
|
1049
1054
|
}
|
|
1050
1055
|
const callImplementation = `${nativeName}(jspp::Constants::UNDEFINED${argsPart})`;
|
|
@@ -1070,28 +1075,34 @@ export function visitCallExpression(node, context) {
|
|
|
1070
1075
|
if (!p)
|
|
1071
1076
|
continue;
|
|
1072
1077
|
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()))`);
|
|
1078
|
+
callArgs.push(`jspp::AnyValue::make_array(std::vector<jspp::AnyValue>(${argsVar}.begin() + std::min((std::size_t)${i}, ${argsVar}.size()), ${argsVar}.end()))`);
|
|
1074
1079
|
}
|
|
1075
1080
|
else {
|
|
1076
1081
|
callArgs.push(`(${argsVar}.size() > ${i} ? ${argsVar}[${i}] : jspp::Constants::UNDEFINED)`);
|
|
1077
1082
|
}
|
|
1078
1083
|
}
|
|
1079
|
-
|
|
1084
|
+
const callArgsPart = callArgs.length > 0
|
|
1085
|
+
? `, ${callArgs.join(", ")}`
|
|
1086
|
+
: "";
|
|
1087
|
+
const totalArgsSizePart = argumentKeywordIsUsed
|
|
1088
|
+
? `, ${argsVar}.size()`
|
|
1089
|
+
: "";
|
|
1090
|
+
let callImplementation = `${nativeName}(jspp::Constants::UNDEFINED${callArgsPart}${totalArgsSizePart})`;
|
|
1080
1091
|
if (symbol.features.isGenerator) {
|
|
1081
1092
|
if (symbol.features.isAsync) {
|
|
1082
|
-
|
|
1083
|
-
`jspp::AnyValue::from_async_iterator(${
|
|
1093
|
+
callImplementation =
|
|
1094
|
+
`jspp::AnyValue::from_async_iterator(${callImplementation})`;
|
|
1084
1095
|
}
|
|
1085
1096
|
else {
|
|
1086
|
-
|
|
1087
|
-
`jspp::AnyValue::from_iterator(${
|
|
1097
|
+
callImplementation =
|
|
1098
|
+
`jspp::AnyValue::from_iterator(${callImplementation})`;
|
|
1088
1099
|
}
|
|
1089
1100
|
}
|
|
1090
1101
|
else if (symbol.features.isAsync) {
|
|
1091
|
-
|
|
1092
|
-
`jspp::AnyValue::from_promise(${
|
|
1102
|
+
callImplementation =
|
|
1103
|
+
`jspp::AnyValue::from_promise(${callImplementation})`;
|
|
1093
1104
|
}
|
|
1094
|
-
code += `${this.indent()}return ${
|
|
1105
|
+
code += `${this.indent()}return ${callImplementation};\n`;
|
|
1095
1106
|
this.indentationLevel--;
|
|
1096
1107
|
code += `${this.indent()}})()`;
|
|
1097
1108
|
return code;
|
|
@@ -5,6 +5,8 @@ import { CodeGenerator } from "./index.js";
|
|
|
5
5
|
export function generateLambdaComponents(node, context, options) {
|
|
6
6
|
const declaredSymbols = this.getDeclaredSymbols(node);
|
|
7
7
|
const argsName = this.generateUniqueName("__args_", declaredSymbols, context.globalScopeSymbols, context.localScopeSymbols);
|
|
8
|
+
const nativeExcessArgsName = this.generateUniqueName("__excess_args", declaredSymbols, context.globalScopeSymbols, context.localScopeSymbols);
|
|
9
|
+
const nativeTotalArgsSizeName = this.generateUniqueName("__total_args_size", declaredSymbols, context.globalScopeSymbols, context.localScopeSymbols);
|
|
8
10
|
const isInsideGeneratorFunction = this.isGeneratorFunction(node);
|
|
9
11
|
const isInsideAsyncFunction = this.isAsyncFunction(node);
|
|
10
12
|
const isArrow = ts.isArrowFunction(node);
|
|
@@ -59,7 +61,7 @@ export function generateLambdaComponents(node, context, options) {
|
|
|
59
61
|
nativePreamble +=
|
|
60
62
|
`${this.indent()}jspp::AnyValue ${this.globalThisVar} = ${finalThisParamName};\n`;
|
|
61
63
|
}
|
|
62
|
-
// Note: Arguments are now automatically copied into the coroutine frame because
|
|
64
|
+
// Note: Arguments are now automatically copied into the coroutine frame because
|
|
63
65
|
// the lambda parameter is passed by value (std::vector).
|
|
64
66
|
// We just need to define argsName as a reference to the parameter.
|
|
65
67
|
preamble +=
|
|
@@ -69,17 +71,32 @@ export function generateLambdaComponents(node, context, options) {
|
|
|
69
71
|
let nativeFuncArgs = "";
|
|
70
72
|
let nativeParamsContent = "";
|
|
71
73
|
this.validateFunctionParams(node.parameters).forEach((p, i) => {
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
: this.generateUniqueName(`__param_native_${i}_`, visitContext.localScopeSymbols);
|
|
74
|
+
const isIdentifier = ts.isIdentifier(p.name);
|
|
75
|
+
const name = isIdentifier ? p.name.text : this.generateUniqueName(`__param_native_${i}_`, visitContext.localScopeSymbols);
|
|
75
76
|
const defaultValue = p.initializer
|
|
76
77
|
? this.visit(p.initializer, visitContext)
|
|
77
78
|
: !!p.dotDotDotToken
|
|
78
79
|
? "jspp::AnyValue::make_array(std::vector<jspp::AnyValue>{})"
|
|
79
80
|
: "jspp::Constants::UNDEFINED";
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
81
|
+
let signatureName = name;
|
|
82
|
+
let needsTemp = !isIdentifier;
|
|
83
|
+
if (isIdentifier) {
|
|
84
|
+
const scope = this.getScopeForNode(p);
|
|
85
|
+
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(name, scope);
|
|
86
|
+
if (typeInfo?.needsHeapAllocation) {
|
|
87
|
+
needsTemp = true;
|
|
88
|
+
signatureName = this.generateUniqueName(`__${name}_param_`, visitContext.localScopeSymbols);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
nativeFuncArgs += `, jspp::AnyValue ${signatureName} = ${defaultValue}`;
|
|
92
|
+
if (needsTemp) {
|
|
93
|
+
if (isIdentifier) {
|
|
94
|
+
nativeParamsContent +=
|
|
95
|
+
`${this.indent()}auto ${name} = std::make_shared<jspp::AnyValue>(${signatureName});\n`;
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
nativeParamsContent += this.generateDestructuring(p.name, signatureName, visitContext) + ";\n";
|
|
99
|
+
}
|
|
83
100
|
}
|
|
84
101
|
});
|
|
85
102
|
// Extract lambda parameters from arguments span/vector
|
|
@@ -102,7 +119,7 @@ export function generateLambdaComponents(node, context, options) {
|
|
|
102
119
|
// Handle rest parameters
|
|
103
120
|
if (!!p.dotDotDotToken) {
|
|
104
121
|
const initValue = `jspp::AnyValue::make_array(${argsName}.subspan(${i}))`;
|
|
105
|
-
if (typeInfo
|
|
122
|
+
if (typeInfo?.needsHeapAllocation) {
|
|
106
123
|
paramsCode +=
|
|
107
124
|
`${this.indent()}auto ${name} = std::make_shared<jspp::AnyValue>(${initValue});\n`;
|
|
108
125
|
}
|
|
@@ -114,7 +131,7 @@ export function generateLambdaComponents(node, context, options) {
|
|
|
114
131
|
}
|
|
115
132
|
// Normal parameter
|
|
116
133
|
const initValue = `${argsName}.size() > ${i} ? ${argsName}[${i}] : ${defaultValue}`;
|
|
117
|
-
if (typeInfo
|
|
134
|
+
if (typeInfo?.needsHeapAllocation) {
|
|
118
135
|
paramsCode +=
|
|
119
136
|
`${this.indent()}auto ${name} = std::make_shared<jspp::AnyValue>(${initValue});\n`;
|
|
120
137
|
}
|
|
@@ -139,6 +156,31 @@ export function generateLambdaComponents(node, context, options) {
|
|
|
139
156
|
// Generate params and function body
|
|
140
157
|
let paramsContent = "";
|
|
141
158
|
let blockContentWithoutOpeningBrace = "";
|
|
159
|
+
// Generate `arguments` variable if it used in the function body
|
|
160
|
+
if (this.isVariableUsedWithoutDeclaration("arguments", node.body)) {
|
|
161
|
+
// Add span parameter for holding the excess arguments from the caller of the native lambda
|
|
162
|
+
nativeFuncArgs +=
|
|
163
|
+
`, const std::size_t ${nativeTotalArgsSizeName}, std::span<const jspp::AnyValue> ${nativeExcessArgsName} = {}`;
|
|
164
|
+
// TODO: compile arguments array from parameters
|
|
165
|
+
// this.indentationLevel++;
|
|
166
|
+
// nativeParamsContent +=
|
|
167
|
+
// `${this.indent()}jspp::AnyValue arguments = jspp::Constants::UNDEFINED;\n`;
|
|
168
|
+
// nativeParamsContent += `${this.indent()}{\n`;
|
|
169
|
+
// this.indentationLevel++;
|
|
170
|
+
// nativeParamsContent +=
|
|
171
|
+
// `${this.indent()}std::vector<jspp::AnyValue> ${argsName};\n`;
|
|
172
|
+
// this.validateFunctionParams(node.parameters).forEach(
|
|
173
|
+
// (p, i) => {
|
|
174
|
+
// },
|
|
175
|
+
// );
|
|
176
|
+
// this.indentationLevel--;
|
|
177
|
+
// nativeParamsContent += `${this.indent()}}\n`;
|
|
178
|
+
// this.indentationLevel--;
|
|
179
|
+
// this.indentationLevel++;
|
|
180
|
+
// paramsContent =
|
|
181
|
+
// `${this.indent()}jspp::AnyValue arguments = jspp::AnyValue::make_array(${argsName});\n`;
|
|
182
|
+
// this.indentationLevel--;
|
|
183
|
+
}
|
|
142
184
|
if (node.body) {
|
|
143
185
|
if (ts.isBlock(node.body)) {
|
|
144
186
|
// Hoist var declarations in the function body
|
|
@@ -148,8 +190,16 @@ export function generateLambdaComponents(node, context, options) {
|
|
|
148
190
|
preamble += hoistCode;
|
|
149
191
|
nativePreamble += hoistCode;
|
|
150
192
|
});
|
|
193
|
+
// Hoist parameters (especially for destructuring)
|
|
194
|
+
node.parameters.forEach((p) => {
|
|
195
|
+
if (!ts.isIdentifier(p.name)) {
|
|
196
|
+
const hoistCode = this.hoistDeclaration(p, visitContext.localScopeSymbols, node.body);
|
|
197
|
+
preamble += hoistCode;
|
|
198
|
+
nativePreamble += hoistCode;
|
|
199
|
+
}
|
|
200
|
+
});
|
|
151
201
|
this.indentationLevel++;
|
|
152
|
-
paramsContent
|
|
202
|
+
paramsContent += generateParamsBuilder();
|
|
153
203
|
this.indentationLevel--;
|
|
154
204
|
// The block visitor already adds braces, so we need to remove the opening brace to inject the preamble and param extraction.
|
|
155
205
|
blockContentWithoutOpeningBrace = this.visit(node.body, {
|
|
@@ -163,7 +213,7 @@ export function generateLambdaComponents(node, context, options) {
|
|
|
163
213
|
}
|
|
164
214
|
else {
|
|
165
215
|
this.indentationLevel++;
|
|
166
|
-
paramsContent
|
|
216
|
+
paramsContent += generateParamsBuilder();
|
|
167
217
|
blockContentWithoutOpeningBrace =
|
|
168
218
|
`${this.indent()}${returnCommand} ${this.visit(node.body, {
|
|
169
219
|
...visitContext,
|
|
@@ -283,7 +333,23 @@ export function generateWrappedLambda(comps) {
|
|
|
283
333
|
args += `, "${funcName}"`;
|
|
284
334
|
}
|
|
285
335
|
}
|
|
286
|
-
|
|
336
|
+
let prototypeExpr = "";
|
|
337
|
+
if (isInsideGeneratorFunction) {
|
|
338
|
+
if (isInsideAsyncFunction) {
|
|
339
|
+
prototypeExpr =
|
|
340
|
+
'::AsyncGeneratorFunction.get_own_property("prototype")';
|
|
341
|
+
}
|
|
342
|
+
else {
|
|
343
|
+
prototypeExpr = '::GeneratorFunction.get_own_property("prototype")';
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
else if (isInsideAsyncFunction) {
|
|
347
|
+
prototypeExpr = '::AsyncFunction.get_own_property("prototype")';
|
|
348
|
+
}
|
|
349
|
+
else {
|
|
350
|
+
prototypeExpr = '::Function.get_own_property("prototype")';
|
|
351
|
+
}
|
|
352
|
+
const fullExpression = `${method}(${args}).set_prototype(${prototypeExpr})`;
|
|
287
353
|
if (ts.isFunctionDeclaration(node) && !isAssignment && node.name) {
|
|
288
354
|
const funcName = node.name?.getText();
|
|
289
355
|
return `${this.indent()}auto ${funcName} = ${fullExpression};\n`;
|
|
@@ -161,6 +161,9 @@ export function getJsVarName(node) {
|
|
|
161
161
|
* @returns The C++ code for accessing the variable's value.
|
|
162
162
|
*/
|
|
163
163
|
export function getDerefCode(nodeText, varName, context, typeInfo) {
|
|
164
|
+
if (typeInfo?.isBuiltin) {
|
|
165
|
+
return nodeText;
|
|
166
|
+
}
|
|
164
167
|
// Make sure varName is incased in quotes
|
|
165
168
|
if (!varName.startsWith('"'))
|
|
166
169
|
varName = '"' + varName;
|
|
@@ -230,21 +233,23 @@ export function getReturnCommand(context) {
|
|
|
230
233
|
* @throws CompilerError if a duplicate declaration is found.
|
|
231
234
|
*/
|
|
232
235
|
export function hoistDeclaration(decl, hoistedSymbols, scopeNode) {
|
|
233
|
-
const isLet = ts.isVariableDeclaration(decl) &&
|
|
234
|
-
(decl.parent.flags & (ts.NodeFlags.Let)) !== 0;
|
|
236
|
+
const isLet = ts.isParameter(decl) || (ts.isVariableDeclaration(decl) &&
|
|
237
|
+
(decl.parent.flags & (ts.NodeFlags.Let)) !== 0);
|
|
235
238
|
const isConst = ts.isVariableDeclaration(decl) &&
|
|
236
239
|
(decl.parent.flags & (ts.NodeFlags.Const)) !== 0;
|
|
237
|
-
const declType =
|
|
240
|
+
const declType = ts.isParameter(decl)
|
|
238
241
|
? DeclarationType.let
|
|
239
|
-
:
|
|
240
|
-
? DeclarationType.
|
|
241
|
-
:
|
|
242
|
-
? DeclarationType.
|
|
243
|
-
: ts.
|
|
244
|
-
? DeclarationType.
|
|
245
|
-
: ts.
|
|
246
|
-
? DeclarationType.
|
|
247
|
-
:
|
|
242
|
+
: isLet
|
|
243
|
+
? DeclarationType.let
|
|
244
|
+
: isConst
|
|
245
|
+
? DeclarationType.const
|
|
246
|
+
: ts.isFunctionDeclaration(decl)
|
|
247
|
+
? DeclarationType.function
|
|
248
|
+
: ts.isClassDeclaration(decl)
|
|
249
|
+
? DeclarationType.class
|
|
250
|
+
: ts.isEnumDeclaration(decl)
|
|
251
|
+
? DeclarationType.enum
|
|
252
|
+
: DeclarationType.var;
|
|
248
253
|
const hoistName = (nameNode) => {
|
|
249
254
|
if (ts.isIdentifier(nameNode)) {
|
|
250
255
|
const name = nameNode.text;
|
|
@@ -268,7 +273,8 @@ export function hoistDeclaration(decl, hoistedSymbols, scopeNode) {
|
|
|
268
273
|
(ts.isVariableDeclaration(decl) && decl.initializer &&
|
|
269
274
|
nameNode === decl.name &&
|
|
270
275
|
(ts.isArrowFunction(decl.initializer) ||
|
|
271
|
-
ts.isFunctionExpression(decl.initializer)
|
|
276
|
+
(ts.isFunctionExpression(decl.initializer) &&
|
|
277
|
+
!decl.initializer.name)))) {
|
|
272
278
|
const funcExpr = ts.isVariableDeclaration(decl)
|
|
273
279
|
? decl.initializer
|
|
274
280
|
: decl;
|
|
@@ -294,7 +300,7 @@ export function hoistDeclaration(decl, hoistedSymbols, scopeNode) {
|
|
|
294
300
|
const initializer = isLet || isConst || ts.isClassDeclaration(decl)
|
|
295
301
|
? "jspp::Constants::UNINITIALIZED"
|
|
296
302
|
: "jspp::Constants::UNDEFINED";
|
|
297
|
-
if (typeInfo
|
|
303
|
+
if (typeInfo?.needsHeapAllocation) {
|
|
298
304
|
return `${this.indent()}auto ${name} = std::make_shared<jspp::AnyValue>(${initializer});\n`;
|
|
299
305
|
}
|
|
300
306
|
else {
|
|
@@ -638,6 +644,77 @@ export function isDeclarationUsedBeforeInitialization(name, root) {
|
|
|
638
644
|
visit(root);
|
|
639
645
|
return isUsedBefore;
|
|
640
646
|
}
|
|
647
|
+
/**
|
|
648
|
+
* Checks if a variable name is used within a node subtree without a preceding declaration.
|
|
649
|
+
* Returns true if:
|
|
650
|
+
* 1. The variable is used but never declared within the provided root node.
|
|
651
|
+
* 2. The variable is used lexically before its declaration within the root node.
|
|
652
|
+
*
|
|
653
|
+
* @param name The variable name to check.
|
|
654
|
+
* @param root The root node to search within.
|
|
655
|
+
* @returns True if used without/before declaration.
|
|
656
|
+
*/
|
|
657
|
+
export function isVariableUsedWithoutDeclaration(name, root) {
|
|
658
|
+
let declPos = -1;
|
|
659
|
+
let foundDecl = false;
|
|
660
|
+
// 1. Find the declaration position within this root
|
|
661
|
+
const findDecl = (node) => {
|
|
662
|
+
if (foundDecl)
|
|
663
|
+
return;
|
|
664
|
+
if ((ts.isFunctionDeclaration(node) ||
|
|
665
|
+
ts.isClassDeclaration(node) ||
|
|
666
|
+
ts.isVariableDeclaration(node) ||
|
|
667
|
+
ts.isEnumDeclaration(node) ||
|
|
668
|
+
ts.isParameter(node)) &&
|
|
669
|
+
node.name && ts.isIdentifier(node.name) && node.name.text === name) {
|
|
670
|
+
declPos = node.getStart();
|
|
671
|
+
foundDecl = true;
|
|
672
|
+
}
|
|
673
|
+
else {
|
|
674
|
+
ts.forEachChild(node, findDecl);
|
|
675
|
+
}
|
|
676
|
+
};
|
|
677
|
+
findDecl(root);
|
|
678
|
+
let isUsedIllegally = false;
|
|
679
|
+
// 2. Traverse for usages
|
|
680
|
+
const visit = (node) => {
|
|
681
|
+
if (isUsedIllegally)
|
|
682
|
+
return;
|
|
683
|
+
if (ts.isIdentifier(node) && node.text === name) {
|
|
684
|
+
const parent = node.parent;
|
|
685
|
+
// Skip if this identifier IS the declaration site
|
|
686
|
+
const isDeclarationSite = (ts.isFunctionDeclaration(parent) ||
|
|
687
|
+
ts.isVariableDeclaration(parent) ||
|
|
688
|
+
ts.isClassDeclaration(parent) ||
|
|
689
|
+
ts.isMethodDeclaration(parent) ||
|
|
690
|
+
ts.isEnumDeclaration(parent) ||
|
|
691
|
+
ts.isEnumMember(parent) ||
|
|
692
|
+
ts.isParameter(parent) ||
|
|
693
|
+
ts.isImportSpecifier(parent)) &&
|
|
694
|
+
parent.name === node;
|
|
695
|
+
// Skip if it's a property access (e.g., obj.name)
|
|
696
|
+
const isProperty = (ts.isPropertyAccessExpression(parent) &&
|
|
697
|
+
parent.name === node) ||
|
|
698
|
+
(ts.isPropertyAssignment(parent) && parent.name === node);
|
|
699
|
+
if (!isDeclarationSite && !isProperty) {
|
|
700
|
+
const usagePos = node.getStart();
|
|
701
|
+
// Case 1: Used but not declared in this node
|
|
702
|
+
if (!foundDecl) {
|
|
703
|
+
isUsedIllegally = true;
|
|
704
|
+
return;
|
|
705
|
+
}
|
|
706
|
+
// Case 2: Used lexically before declaration
|
|
707
|
+
if (usagePos < declPos) {
|
|
708
|
+
isUsedIllegally = true;
|
|
709
|
+
return;
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
ts.forEachChild(node, visit);
|
|
714
|
+
};
|
|
715
|
+
visit(root);
|
|
716
|
+
return isUsedIllegally;
|
|
717
|
+
}
|
|
641
718
|
/**
|
|
642
719
|
* Validates and filters function parameters, checking for illegal "this"
|
|
643
720
|
* and correctly positioned rest parameters.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DeclaredSymbols } from "../../ast/symbols.js";
|
|
2
2
|
import { generateLambdaComponents, generateNativeLambda, generateWrappedLambda, } from "./function-handlers.js";
|
|
3
|
-
import { escapeString, generateUniqueExceptionName, generateUniqueName, getDeclaredSymbols, getDerefCode, getJsVarName, getReturnCommand, getScopeForNode, hoistDeclaration, indent, isAsyncFunction, isBuiltinObject, isDeclarationCalledAsFunction, isDeclarationUsedAsValue, isDeclarationUsedBeforeInitialization, isGeneratorFunction, markSymbolAsInitialized, prepareScopeSymbolsForVisit, validateFunctionParams, } from "./helpers.js";
|
|
3
|
+
import { escapeString, generateUniqueExceptionName, generateUniqueName, getDeclaredSymbols, getDerefCode, getJsVarName, getReturnCommand, getScopeForNode, hoistDeclaration, indent, isAsyncFunction, isBuiltinObject, isDeclarationCalledAsFunction, isDeclarationUsedAsValue, isDeclarationUsedBeforeInitialization, isVariableUsedWithoutDeclaration, isGeneratorFunction, markSymbolAsInitialized, prepareScopeSymbolsForVisit, validateFunctionParams, } from "./helpers.js";
|
|
4
4
|
import { generateDestructuring } from "./destructuring-handlers.js";
|
|
5
5
|
import { visit } from "./visitor.js";
|
|
6
6
|
const MODULE_NAME = "__entry_point__";
|
|
@@ -31,6 +31,7 @@ export class CodeGenerator {
|
|
|
31
31
|
isDeclarationCalledAsFunction = isDeclarationCalledAsFunction;
|
|
32
32
|
isDeclarationUsedAsValue = isDeclarationUsedAsValue;
|
|
33
33
|
isDeclarationUsedBeforeInitialization = isDeclarationUsedBeforeInitialization;
|
|
34
|
+
isVariableUsedWithoutDeclaration = isVariableUsedWithoutDeclaration;
|
|
34
35
|
validateFunctionParams = validateFunctionParams;
|
|
35
36
|
generateDestructuring = generateDestructuring;
|
|
36
37
|
// function handlers
|
|
@@ -44,7 +45,7 @@ export class CodeGenerator {
|
|
|
44
45
|
this.typeAnalyzer = analyzer;
|
|
45
46
|
this.isTypescript = isTypescript;
|
|
46
47
|
this.globalThisVar = this.generateUniqueName("__this_val__", this.getDeclaredSymbols(ast));
|
|
47
|
-
const declarations = `#include "
|
|
48
|
+
const declarations = `#include "jspp.hpp"\n#include "library/global_usings.hpp"\n\n`;
|
|
48
49
|
let containerCode = `jspp::JsPromise ${MODULE_NAME}() {\n`;
|
|
49
50
|
this.indentationLevel++;
|
|
50
51
|
containerCode +=
|
|
@@ -63,6 +64,7 @@ export class CodeGenerator {
|
|
|
63
64
|
containerCode += "}\n\n";
|
|
64
65
|
let mainCode = "int main(int argc, char** argv) {\n";
|
|
65
66
|
mainCode += ` try {\n`;
|
|
67
|
+
mainCode += ` jspp::initialize_runtime();\n`;
|
|
66
68
|
mainCode += ` jspp::setup_process_argv(argc, argv);\n`;
|
|
67
69
|
mainCode += ` auto p = ${MODULE_NAME}();\n`;
|
|
68
70
|
mainCode += ` p.then(nullptr, [](jspp::AnyValue err) {\n`;
|
|
@@ -54,12 +54,14 @@ export function visitSourceFile(node, context) {
|
|
|
54
54
|
this.markSymbolAsInitialized(funcName, globalScopeSymbols, localScopeSymbols);
|
|
55
55
|
// Update features in the symbol registry
|
|
56
56
|
const nativeName = this.generateUniqueName(`__${funcName}_native_`, hoistedSymbols);
|
|
57
|
+
const argumentKeywordIsUsed = this.isVariableUsedWithoutDeclaration("arguments", stmt.body);
|
|
57
58
|
hoistedSymbols.update(funcName, {
|
|
58
59
|
features: {
|
|
59
60
|
native: {
|
|
60
61
|
type: "lambda",
|
|
61
62
|
name: nativeName,
|
|
62
63
|
parameters: this.validateFunctionParams(stmt.parameters),
|
|
64
|
+
argumentKeywordIsUsed,
|
|
63
65
|
},
|
|
64
66
|
},
|
|
65
67
|
});
|
|
@@ -151,12 +153,14 @@ export function visitBlock(node, context) {
|
|
|
151
153
|
this.markSymbolAsInitialized(funcName, globalScopeSymbols, localScopeSymbols);
|
|
152
154
|
// Update features in the symbol registry
|
|
153
155
|
const nativeName = this.generateUniqueName(`__${funcName}_native_`, hoistedSymbols);
|
|
156
|
+
const argumentKeywordIsUsed = this.isVariableUsedWithoutDeclaration("arguments", stmt.body);
|
|
154
157
|
hoistedSymbols.update(funcName, {
|
|
155
158
|
features: {
|
|
156
159
|
native: {
|
|
157
160
|
type: "lambda",
|
|
158
161
|
name: nativeName,
|
|
159
162
|
parameters: this.validateFunctionParams(stmt.parameters),
|
|
163
|
+
argumentKeywordIsUsed,
|
|
160
164
|
},
|
|
161
165
|
},
|
|
162
166
|
});
|
|
@@ -238,7 +242,7 @@ export function visitEnumDeclaration(node, context) {
|
|
|
238
242
|
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(name, scope);
|
|
239
243
|
// Mark as initialized
|
|
240
244
|
this.markSymbolAsInitialized(name, context.globalScopeSymbols, context.localScopeSymbols);
|
|
241
|
-
const enumVar = typeInfo
|
|
245
|
+
const enumVar = typeInfo?.needsHeapAllocation ? `(*${name})` : name;
|
|
242
246
|
let code = `${this.indent()}${enumVar} = jspp::AnyValue::make_object({});\n`;
|
|
243
247
|
code += `${this.indent()}{\n`;
|
|
244
248
|
this.indentationLevel++;
|
|
@@ -337,10 +341,7 @@ export function visitIfStatement(node, context) {
|
|
|
337
341
|
const ifStmt = node;
|
|
338
342
|
const isBinaryExpression = ts.isBinaryExpression(ifStmt.expression) &&
|
|
339
343
|
constants.booleanOperators.includes(ifStmt.expression.operatorToken.kind);
|
|
340
|
-
const condition = this.visit(ifStmt.expression,
|
|
341
|
-
...context,
|
|
342
|
-
supportedNativeLiterals: isBinaryExpression ? ["boolean"] : undefined,
|
|
343
|
-
});
|
|
344
|
+
const condition = this.visit(ifStmt.expression, context);
|
|
344
345
|
const thenStmt = this.visit(ifStmt.thenStatement, {
|
|
345
346
|
...context,
|
|
346
347
|
isFunctionBody: false,
|
|
@@ -366,7 +367,7 @@ export function visitExpressionStatement(node, context) {
|
|
|
366
367
|
export function visitThrowStatement(node, context) {
|
|
367
368
|
const throwStmt = node;
|
|
368
369
|
const expr = this.visit(throwStmt.expression, context);
|
|
369
|
-
return `${this.indent()}throw jspp::Exception(${expr});
|
|
370
|
+
return `${this.indent()}throw jspp::Exception(${expr});
|
|
370
371
|
`;
|
|
371
372
|
}
|
|
372
373
|
export function visitTryStatement(node, context) {
|
|
@@ -656,7 +657,8 @@ export function visitCatchClause(node, context) {
|
|
|
656
657
|
const exceptionValueCode = `jspp::Exception::exception_to_any_value(${exceptionName})`;
|
|
657
658
|
if (ts.isIdentifier(catchClause.variableDeclaration.name)) {
|
|
658
659
|
const varName = catchClause.variableDeclaration.name.text;
|
|
659
|
-
code +=
|
|
660
|
+
code +=
|
|
661
|
+
`${this.indent()}jspp::AnyValue ${varName} = ${exceptionValueCode};\n`;
|
|
660
662
|
}
|
|
661
663
|
else {
|
|
662
664
|
code += this.generateDestructuring(catchClause.variableDeclaration.name, exceptionValueCode, context) + ";\n";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ugo-studio/jspp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "A modern transpiler that converts JavaScript code into high-performance, standard C++23.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "src/index.ts",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"scripts"
|
|
15
15
|
],
|
|
16
16
|
"scripts": {
|
|
17
|
-
"postinstall": "bun run scripts/setup-compiler.ts",
|
|
17
|
+
"postinstall": "bun run scripts/setup-compiler.ts && bun run scripts/precompile-headers.ts",
|
|
18
18
|
"dev": "bun run src/cli/index.ts",
|
|
19
19
|
"typecheck": "tsc --noEmit",
|
|
20
20
|
"precompile": "bun run scripts/precompile-headers.ts",
|