xbintsc 0.2.5 → 0.2.6
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/dist/src/ast/expressions.d.ts +6 -2
- package/dist/src/ast/kinds.d.ts +1 -0
- package/dist/src/ast/kinds.js +1 -0
- package/dist/src/ast/kinds.js.map +1 -1
- package/dist/src/binder/binder.js +29 -0
- package/dist/src/binder/binder.js.map +1 -1
- package/dist/src/codegen/generator/calls.d.ts +1 -1
- package/dist/src/codegen/generator/calls.js +57 -14
- package/dist/src/codegen/generator/calls.js.map +1 -1
- package/dist/src/codegen/generator/context.d.ts +1 -1
- package/dist/src/codegen/generator/expressions.js +14 -3
- package/dist/src/codegen/generator/expressions.js.map +1 -1
- package/dist/src/codegen/generator/module.js +57 -2
- package/dist/src/codegen/generator/module.js.map +1 -1
- package/dist/src/codegen/generator/state.js +15 -0
- package/dist/src/codegen/generator/state.js.map +1 -1
- package/dist/src/codegen/generator/statements.js +25 -8
- package/dist/src/codegen/generator/statements.js.map +1 -1
- package/dist/src/codegen/generator/tables.d.ts +3 -0
- package/dist/src/codegen/generator/tables.js +12 -0
- package/dist/src/codegen/generator/tables.js.map +1 -1
- package/dist/src/codegen/values.d.ts +18 -11
- package/dist/src/codegen/values.js +92 -15
- package/dist/src/codegen/values.js.map +1 -1
- package/dist/src/diagnostics/source.d.ts +1 -1
- package/dist/src/diagnostics/source.js +12 -7
- package/dist/src/diagnostics/source.js.map +1 -1
- package/dist/src/driver/compiler.js +16 -2
- package/dist/src/driver/compiler.js.map +1 -1
- package/dist/src/extensions/node/child_process/index.d.ts +7 -0
- package/dist/src/extensions/node/child_process/index.js +19 -0
- package/dist/src/extensions/node/child_process/index.js.map +1 -0
- package/dist/src/extensions/node/crypto/index.d.ts +6 -0
- package/dist/src/extensions/node/crypto/index.js +18 -0
- package/dist/src/extensions/node/crypto/index.js.map +1 -0
- package/dist/src/extensions/node/index.js +6 -0
- package/dist/src/extensions/node/index.js.map +1 -1
- package/dist/src/extensions/node/module.d.ts +9 -1
- package/dist/src/extensions/node/module.js +14 -1
- package/dist/src/extensions/node/module.js.map +1 -1
- package/dist/src/extensions/node/url/index.d.ts +7 -0
- package/dist/src/extensions/node/url/index.js +21 -0
- package/dist/src/extensions/node/url/index.js.map +1 -0
- package/dist/src/lexer/scanner.js +4 -5
- package/dist/src/lexer/scanner.js.map +1 -1
- package/dist/src/parser/declarations.js +10 -9
- package/dist/src/parser/declarations.js.map +1 -1
- package/dist/src/parser/expressions.js +1 -1
- package/dist/src/parser/expressions.js.map +1 -1
- package/dist/tests/e2e/compile.test.js +36 -0
- package/dist/tests/e2e/compile.test.js.map +1 -1
- package/package.json +1 -1
- package/runtime/ext_node/child_process/child_process.c +213 -0
- package/runtime/ext_node/crypto/crypto.c +177 -0
- package/runtime/ext_node/process/process.c +37 -3
- package/runtime/ext_node/url/url.c +85 -0
- package/runtime/rt.h +20 -0
- package/runtime/rt_internal.h +11 -0
- package/runtime/xt_builtins.c +23 -0
- package/runtime/xt_containers.c +136 -10
- package/runtime/xt_io.c +53 -0
- package/runtime/xt_regex.h +16 -6
- package/runtime/xt_stdlib.c +11 -2
- package/runtime/xt_stdlib2.c +239 -13
- package/runtime/xt_values.c +2 -1
- package/src/ast/expressions.ts +7 -2
- package/src/ast/kinds.ts +1 -0
- package/src/binder/binder.ts +29 -0
- package/src/codegen/generator/calls.ts +59 -16
- package/src/codegen/generator/context.ts +1 -1
- package/src/codegen/generator/expressions.ts +14 -3
- package/src/codegen/generator/module.ts +60 -1
- package/src/codegen/generator/state.ts +15 -0
- package/src/codegen/generator/statements.ts +23 -8
- package/src/codegen/generator/tables.ts +13 -0
- package/src/codegen/values.ts +85 -16
- package/src/diagnostics/source.ts +12 -7
- package/src/driver/compiler.ts +15 -2
- package/src/extensions/node/child_process/index.ts +24 -0
- package/src/extensions/node/crypto/index.ts +23 -0
- package/src/extensions/node/index.ts +6 -0
- package/src/extensions/node/module.ts +13 -1
- package/src/extensions/node/url/index.ts +26 -0
- package/src/lexer/scanner.ts +4 -4
- package/src/parser/declarations.ts +10 -7
- package/src/parser/expressions.ts +1 -1
|
@@ -68,6 +68,8 @@ export const RUNTIME_DECLARATIONS: readonly string[] = [
|
|
|
68
68
|
"declare i64 @xt_object_new_with_proto(i64)",
|
|
69
69
|
"declare i64 @xt_object_get_prototype(i64)",
|
|
70
70
|
"declare i64 @xt_object_set_prototype(i64, i64)",
|
|
71
|
+
"declare i64 @xt_object_define_getter(i64, i64, i64)",
|
|
72
|
+
"declare i64 @xt_object_define_setter(i64, i64, i64)",
|
|
71
73
|
"declare i64 @xt_await(i64)",
|
|
72
74
|
"declare void @xt_drain_microtasks()",
|
|
73
75
|
"declare void @xt_run_event_loop()",
|
|
@@ -102,6 +104,7 @@ export const RUNTIME_DECLARATIONS: readonly string[] = [
|
|
|
102
104
|
"declare i64 @xt_date_ctor(i32, i64*)",
|
|
103
105
|
"declare i64 @xt_regexp_ctor(i32, i64*)",
|
|
104
106
|
"declare i64 @xt_error_ctor(i32, i64*)",
|
|
107
|
+
"declare i64 @xt_error_init(i64, i32, i64*)",
|
|
105
108
|
"declare i64 @xt_json_parse(i32, i64*)",
|
|
106
109
|
"declare i64 @xt_json_stringify(i32, i64*)",
|
|
107
110
|
"declare i64 @xt_object_from_entries(i64)",
|
|
@@ -169,10 +172,22 @@ export const RUNTIME_DECLARATIONS: readonly string[] = [
|
|
|
169
172
|
"declare i64 @xt_delete(i64, i64)",
|
|
170
173
|
"declare i64 @xt_rest_args(i32, i64*, i32)",
|
|
171
174
|
"declare void @xt_set_program_args(i32, i8**)",
|
|
175
|
+
"declare i64 @xt_import_meta(i64)",
|
|
172
176
|
"declare i64 @xt_array_new(i32, i64*)",
|
|
173
177
|
"declare i64 @xt_array_push(i64, i64)",
|
|
174
178
|
"declare i64 @xt_array_length(i64)",
|
|
175
179
|
"declare i64 @xt_array_spread(i64, i64)",
|
|
180
|
+
"declare i64 @xt_iter_length(i64)",
|
|
181
|
+
"declare i64 @xt_iter_value(i64, i64)",
|
|
182
|
+
"declare i32 @xt_array_size(i64)",
|
|
183
|
+
"declare i64* @xt_array_items(i64)",
|
|
184
|
+
"declare i64 @xt_builtin_value_parseInt(i64, i64, i32, i64*)",
|
|
185
|
+
"declare i64 @xt_builtin_value_parseFloat(i64, i64, i32, i64*)",
|
|
186
|
+
"declare i64 @xt_builtin_value_isNaN(i64, i64, i32, i64*)",
|
|
187
|
+
"declare i64 @xt_builtin_value_isFinite(i64, i64, i32, i64*)",
|
|
188
|
+
"declare i64 @xt_builtin_value_number(i64, i64, i32, i64*)",
|
|
189
|
+
"declare i64 @xt_builtin_value_string(i64, i64, i32, i64*)",
|
|
190
|
+
"declare i64 @xt_builtin_value_boolean(i64, i64, i32, i64*)",
|
|
176
191
|
"declare i64 @xt_get(i64, i64)",
|
|
177
192
|
"declare i64 @xt_set(i64, i64, i64)",
|
|
178
193
|
"declare i64 @xt_box_new(i64)",
|
|
@@ -143,15 +143,31 @@ export const statementMethods: StatementMethods = {
|
|
|
143
143
|
|
|
144
144
|
emitVariableStatement(statement: VariableStatement): void {
|
|
145
145
|
for (const declaration of statement.declarationList.declarations) {
|
|
146
|
-
const initial = declaration.initializer
|
|
147
|
-
? this.emitExpression(declaration.initializer)
|
|
148
|
-
: i64(XT_UNDEFINED);
|
|
149
146
|
if (declaration.name.kind === SyntaxKind.Identifier) {
|
|
150
147
|
const symbol = this.binding.symbolOfDeclaration.get(declaration);
|
|
151
|
-
if (!symbol)
|
|
152
|
-
|
|
148
|
+
if (!symbol) {
|
|
149
|
+
if (declaration.initializer) this.emitExpression(declaration.initializer);
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
if (this.current.slots.has(symbol.id)) {
|
|
153
|
+
// Hoisted `var` (or an already-declared slot): keep evaluating the
|
|
154
|
+
// initializer for its side effects, but do not redeclare the slot.
|
|
155
|
+
if (declaration.initializer) this.emitExpression(declaration.initializer);
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
if (symbol.boxed) {
|
|
159
|
+
// A captured binding lives in a box. Create the box *before*
|
|
160
|
+
// evaluating the initializer so a self-referencing closure
|
|
161
|
+
// (`const f = () => f()`) captures the box, not a copy of the
|
|
162
|
+
// still-uninitialised value.
|
|
163
|
+
this.declareSlot(symbol, i64(XT_UNDEFINED));
|
|
164
|
+
if (declaration.initializer) this.writeSlot(symbol, this.emitExpression(declaration.initializer));
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
const initial = declaration.initializer ? this.emitExpression(declaration.initializer) : i64(XT_UNDEFINED);
|
|
153
168
|
this.declareSlot(symbol, initial);
|
|
154
169
|
} else {
|
|
170
|
+
const initial = declaration.initializer ? this.emitExpression(declaration.initializer) : i64(XT_UNDEFINED);
|
|
155
171
|
this.emitBindingPattern(declaration.name, initial);
|
|
156
172
|
}
|
|
157
173
|
}
|
|
@@ -287,7 +303,6 @@ export const statementMethods: StatementMethods = {
|
|
|
287
303
|
const nonzero = this.reg();
|
|
288
304
|
this.emit(` ${nonzero} = icmp ne i32 ${truthy}, 0`);
|
|
289
305
|
this.terminate(`br i1 ${nonzero}, label %${bodyLabel}, label %${endLabel}`);
|
|
290
|
-
|
|
291
306
|
this.startBlock(bodyLabel);
|
|
292
307
|
this.current.loops.push({ breakLabel: endLabel, continueLabel: condLabel, tryDepth: this.current.tryFrames.length });
|
|
293
308
|
this.emitStatement(statement.statement);
|
|
@@ -369,7 +384,7 @@ export const statementMethods: StatementMethods = {
|
|
|
369
384
|
const indexPtr = this.alloca();
|
|
370
385
|
this.emit(` store i64 ${numberLiteral(0)}, i64* ${indexPtr}`);
|
|
371
386
|
const lengthValue = this.reg();
|
|
372
|
-
this.emit(` ${lengthValue} = call i64 @
|
|
387
|
+
this.emit(` ${lengthValue} = call i64 @xt_iter_length(i64 ${iterable})`);
|
|
373
388
|
|
|
374
389
|
const condLabel = this.label("forof.cond");
|
|
375
390
|
const bodyLabel = this.label("forof.body");
|
|
@@ -390,7 +405,7 @@ export const statementMethods: StatementMethods = {
|
|
|
390
405
|
|
|
391
406
|
this.startBlock(bodyLabel);
|
|
392
407
|
const element = this.reg();
|
|
393
|
-
this.emit(` ${element} = call i64 @
|
|
408
|
+
this.emit(` ${element} = call i64 @xt_iter_value(i64 ${iterable}, i64 ${index})`);
|
|
394
409
|
this.bindLoopVariable(statement.initializer, element);
|
|
395
410
|
this.current.loops.push({ breakLabel: endLabel, continueLabel: updateLabel, tryDepth: this.current.tryFrames.length });
|
|
396
411
|
this.emitStatement(statement.statement);
|
|
@@ -79,6 +79,18 @@ export const GLOBAL_FUNCTIONS: Record<string, string> = {
|
|
|
79
79
|
Boolean: "xt_boolean_ctor",
|
|
80
80
|
};
|
|
81
81
|
|
|
82
|
+
/** Global functions that are also usable as first-class values, each mapped to
|
|
83
|
+
* a runtime trampoline with the closure ABI. */
|
|
84
|
+
export const BUILTIN_FUNCTION_VALUES: Record<string, string> = {
|
|
85
|
+
parseInt: "xt_builtin_value_parseInt",
|
|
86
|
+
parseFloat: "xt_builtin_value_parseFloat",
|
|
87
|
+
isNaN: "xt_builtin_value_isNaN",
|
|
88
|
+
isFinite: "xt_builtin_value_isFinite",
|
|
89
|
+
Number: "xt_builtin_value_number",
|
|
90
|
+
String: "xt_builtin_value_string",
|
|
91
|
+
Boolean: "xt_builtin_value_boolean",
|
|
92
|
+
};
|
|
93
|
+
|
|
82
94
|
/** Global namespaces whose static methods map to runtime dispatchers. */
|
|
83
95
|
export const NAMESPACE_STATICS: Record<string, string> = {
|
|
84
96
|
Math: "xt_math_call",
|
|
@@ -107,6 +119,7 @@ export const NAMESPACE_STATICS: Record<string, string> = {
|
|
|
107
119
|
/** Namespace identifiers whose property access maps to a runtime getter. */
|
|
108
120
|
export const NAMESPACE_PROPERTIES: Record<string, string> = {
|
|
109
121
|
process: "xt_process_get",
|
|
122
|
+
"import.meta": "xt_import_meta",
|
|
110
123
|
};
|
|
111
124
|
|
|
112
125
|
/** Global constructors called as `new X(...)` (all have signature `(i32, i64*)`). */
|
package/src/codegen/values.ts
CHANGED
|
@@ -5,32 +5,101 @@
|
|
|
5
5
|
* value uses a tag in the high 16 bits plus a 48-bit payload. The compiler
|
|
6
6
|
* emits these constants directly into LLVM IR, so this module (together with
|
|
7
7
|
* runtime/rt.h) is the single source of truth for the representation.
|
|
8
|
+
*
|
|
9
|
+
* The constants are written as pre-computed signed 64-bit decimal literals
|
|
10
|
+
* rather than BigInt expressions, and `numberLiteral` derives the raw bits of a
|
|
11
|
+
* double with plain arithmetic. That keeps this module free of BigInt and
|
|
12
|
+
* typed-array APIs so the compiler can be compiled by itself on every platform.
|
|
8
13
|
*/
|
|
9
14
|
|
|
10
|
-
|
|
11
|
-
export const
|
|
15
|
+
/** `0xffff000000000000` as a signed i64. */
|
|
16
|
+
export const XT_TAG_MASK = "-281474976710656";
|
|
17
|
+
/** `0xfff8000000000000` as a signed i64. */
|
|
18
|
+
export const XT_NUMBER_MASK = "-2251799813685248";
|
|
12
19
|
|
|
13
|
-
export const XT_UNDEFINED =
|
|
14
|
-
export const XT_NULL =
|
|
15
|
-
export const XT_FALSE =
|
|
16
|
-
export const XT_TRUE =
|
|
17
|
-
export const XT_STRING =
|
|
18
|
-
export const XT_OBJECT =
|
|
19
|
-
export const XT_ARRAY =
|
|
20
|
-
export const XT_FUNCTION =
|
|
20
|
+
export const XT_UNDEFINED = "-2251799813685248"; // 0xfff8000000000000
|
|
21
|
+
export const XT_NULL = "-1970324836974592"; // 0xfff9000000000000
|
|
22
|
+
export const XT_FALSE = "-1688849860263936"; // 0xfffa000000000000
|
|
23
|
+
export const XT_TRUE = "-1407374883553280"; // 0xfffb000000000000
|
|
24
|
+
export const XT_STRING = "-1125899906842624"; // 0xfffc000000000000
|
|
25
|
+
export const XT_OBJECT = "-844424930131968"; // 0xfffd000000000000
|
|
26
|
+
export const XT_ARRAY = "-562949953421312"; // 0xfffe000000000000
|
|
27
|
+
export const XT_FUNCTION = "-281474976710656"; // 0xffff000000000000
|
|
21
28
|
|
|
22
29
|
/** Render a 64-bit constant the way LLVM expects signed i64 literals. */
|
|
23
|
-
export function i64(value:
|
|
24
|
-
return
|
|
30
|
+
export function i64(value: string): string {
|
|
31
|
+
return value;
|
|
25
32
|
}
|
|
26
33
|
|
|
27
34
|
/** The raw 64 bits of an IEEE-754 double, as a signed i64 literal. */
|
|
28
35
|
export function numberLiteral(value: number): string {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
36
|
+
if (value !== value) return "9221120237041090560"; // 0x7ff8000000000000 (NaN)
|
|
37
|
+
const negative = value < 0 || (value === 0 && 1 / value < 0);
|
|
38
|
+
const abs = negative ? -value : value;
|
|
39
|
+
let high: number;
|
|
40
|
+
let low: number;
|
|
41
|
+
if (abs === 0) {
|
|
42
|
+
high = 0;
|
|
43
|
+
low = 0;
|
|
44
|
+
} else if (abs === Infinity) {
|
|
45
|
+
high = 0x7ff00000;
|
|
46
|
+
low = 0;
|
|
47
|
+
} else {
|
|
48
|
+
let exponent = Math.floor(Math.log2(abs));
|
|
49
|
+
while (exponent < 1023 && Math.pow(2, exponent + 1) <= abs) exponent++;
|
|
50
|
+
while (exponent > -1022 && Math.pow(2, exponent) > abs) exponent--;
|
|
51
|
+
let mantissa = Math.round((abs / Math.pow(2, exponent) - 1) * 4503599627370496);
|
|
52
|
+
let biased = exponent + 1023;
|
|
53
|
+
if (biased <= 0) {
|
|
54
|
+
mantissa = Math.round((abs / Math.pow(2, -1022)) * 4503599627370496);
|
|
55
|
+
biased = 0;
|
|
56
|
+
} else if (mantissa >= 4503599627370496) {
|
|
57
|
+
mantissa = 0;
|
|
58
|
+
biased += 1;
|
|
59
|
+
}
|
|
60
|
+
high = biased * 0x100000 + Math.floor(mantissa / 0x100000000);
|
|
61
|
+
low = mantissa % 0x100000000;
|
|
62
|
+
}
|
|
63
|
+
if (negative) high += 0x80000000;
|
|
64
|
+
return signedDecimal(high >>> 0, low >>> 0);
|
|
32
65
|
}
|
|
33
66
|
|
|
34
67
|
export function booleanLiteral(value: boolean): string {
|
|
35
|
-
return
|
|
68
|
+
return value ? XT_TRUE : XT_FALSE;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Two's-complement (high, low) 32-bit halves as a signed decimal string. */
|
|
72
|
+
function signedDecimal(high: number, low: number): string {
|
|
73
|
+
if (high >= 0x80000000) {
|
|
74
|
+
let h = (~high) >>> 0;
|
|
75
|
+
let l = (~low) >>> 0;
|
|
76
|
+
l = (l + 1) >>> 0;
|
|
77
|
+
if (l === 0) h = (h + 1) >>> 0;
|
|
78
|
+
return "-" + unsignedDecimal(h, l);
|
|
79
|
+
}
|
|
80
|
+
return unsignedDecimal(high, low);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Unsigned 64-bit `high * 2^32 + low` as a decimal string. */
|
|
84
|
+
function unsignedDecimal(high: number, low: number): string {
|
|
85
|
+
const digits: number[] = [];
|
|
86
|
+
let h = high;
|
|
87
|
+
if (h === 0) digits.push(0);
|
|
88
|
+
while (h > 0) {
|
|
89
|
+
digits.push(h % 10);
|
|
90
|
+
h = Math.floor(h / 10);
|
|
91
|
+
}
|
|
92
|
+
let carry = low;
|
|
93
|
+
for (let i = 0; i < digits.length; i++) {
|
|
94
|
+
const current = digits[i]! * 4294967296 + carry;
|
|
95
|
+
digits[i] = current % 10;
|
|
96
|
+
carry = Math.floor(current / 10);
|
|
97
|
+
}
|
|
98
|
+
while (carry > 0) {
|
|
99
|
+
digits.push(carry % 10);
|
|
100
|
+
carry = Math.floor(carry / 10);
|
|
101
|
+
}
|
|
102
|
+
let out = "";
|
|
103
|
+
for (let i = digits.length - 1; i >= 0; i--) out += digits[i]!;
|
|
104
|
+
return out;
|
|
36
105
|
}
|
|
@@ -80,15 +80,20 @@ export class SourceFile {
|
|
|
80
80
|
* and stable across platforms which is exactly what the incremental cache
|
|
81
81
|
* needs. Collision resistance is not a security property here.
|
|
82
82
|
*/
|
|
83
|
-
export function hashText(text: string, seed =
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
83
|
+
export function hashText(text: string, seed = 0): string {
|
|
84
|
+
// Two independent 32-bit FNV-1a lanes give a wide, stable fingerprint
|
|
85
|
+
// without relying on BigInt, which the compiler cannot assume at runtime.
|
|
86
|
+
let a = (0xcbf29ce4 ^ seed) >>> 0;
|
|
87
|
+
let b = (0x84222325 ^ seed) >>> 0;
|
|
88
|
+
const prime = 0x01000193;
|
|
87
89
|
for (let i = 0; i < text.length; i++) {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
+
const code = text.charCodeAt(i);
|
|
91
|
+
a = Math.imul(a ^ (code & 0xff), prime) >>> 0;
|
|
92
|
+
a = Math.imul(a ^ (code >>> 8), prime) >>> 0;
|
|
93
|
+
b = Math.imul(b ^ (code >>> 8), prime) >>> 0;
|
|
94
|
+
b = Math.imul(b ^ (code & 0xff), prime) >>> 0;
|
|
90
95
|
}
|
|
91
|
-
return
|
|
96
|
+
return (a >>> 0).toString(16).padStart(8, "0") + (b >>> 0).toString(16).padStart(8, "0");
|
|
92
97
|
}
|
|
93
98
|
|
|
94
99
|
/** Combine several hashes/strings into one stable fingerprint. */
|
package/src/driver/compiler.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* nothing relevant changed.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
10
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
11
11
|
import { basename, dirname, extname, join, resolve } from "node:path";
|
|
12
12
|
import { DiagnosticBag, type Diagnostic } from "../diagnostics/diagnostic.js";
|
|
13
13
|
import { SourceFile } from "../diagnostics/source.js";
|
|
@@ -246,9 +246,22 @@ function ensureRuntimeObjects(
|
|
|
246
246
|
cacheDir: string,
|
|
247
247
|
registry: ExtensionRegistry,
|
|
248
248
|
): RuntimeObjects {
|
|
249
|
+
/* Runtime objects depend on the shared headers (rt.h/rt_internal.h/...),
|
|
250
|
+
so a header change must invalidate every cached object. Hash them all. */
|
|
251
|
+
const headerParts: string[] = [];
|
|
252
|
+
const collectHeaders = (dir: string): void => {
|
|
253
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
254
|
+
const full = join(dir, entry.name);
|
|
255
|
+
if (entry.isDirectory()) collectHeaders(full);
|
|
256
|
+
else if (entry.name.endsWith(".h")) headerParts.push(readFileSync(full, "utf8"));
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
collectHeaders(runtimeDir);
|
|
260
|
+
const headerStamp = hashParts(headerParts);
|
|
261
|
+
|
|
249
262
|
const compileOne = (sourcePath: string, tag: string): string => {
|
|
250
263
|
const body = readFileSync(sourcePath, "utf8");
|
|
251
|
-
const key = hashParts(["runtime", COMPILER_VERSION, tag, body, clang, process.platform]);
|
|
264
|
+
const key = hashParts(["runtime", COMPILER_VERSION, tag, headerStamp, body, clang, process.platform]);
|
|
252
265
|
const objectPath = join(cacheDir, `${tag}-${key}.o`);
|
|
253
266
|
if (!existsSync(objectPath)) {
|
|
254
267
|
compileC(runner, clang, sourcePath, objectPath, runtimeDir);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node's `child_process` module: `spawnSync(command, args, options)`, backed by
|
|
3
|
+
* `runtime/ext_node/child_process`. Captures stdout/stderr as UTF-8 strings and
|
|
4
|
+
* honours `options.cwd`, which is all the compiler's toolchain wrapper needs.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { NodeModule } from "../module.js";
|
|
8
|
+
import { resolveFrom } from "../module.js";
|
|
9
|
+
import type { BuiltinFunction, ModuleExports } from "../../registry.js";
|
|
10
|
+
|
|
11
|
+
const builtins: Record<string, BuiltinFunction> = {
|
|
12
|
+
spawnSync: { symbol: "xt_child_process_spawn_sync" },
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const exports: ModuleExports = {
|
|
16
|
+
spawnSync: { symbol: "xt_child_process_spawn_sync" },
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const childProcessModule: NodeModule = {
|
|
20
|
+
name: "child_process",
|
|
21
|
+
runtimeSources: () => [resolveFrom(import.meta.url, "../../../../runtime/ext_node/child_process/child_process.c")],
|
|
22
|
+
builtins: () => builtins,
|
|
23
|
+
exports: () => exports,
|
|
24
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node's `crypto` module: `createHash(...).update(...).digest("hex")`, backed
|
|
3
|
+
* by a self-contained SHA-256 in `runtime/ext_node/crypto`.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { NodeModule } from "../module.js";
|
|
7
|
+
import { resolveFrom } from "../module.js";
|
|
8
|
+
import type { BuiltinFunction, ModuleExports } from "../../registry.js";
|
|
9
|
+
|
|
10
|
+
const builtins: Record<string, BuiltinFunction> = {
|
|
11
|
+
createHash: { symbol: "xt_crypto_create_hash" },
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const exports: ModuleExports = {
|
|
15
|
+
createHash: { symbol: "xt_crypto_create_hash" },
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const cryptoModule: NodeModule = {
|
|
19
|
+
name: "crypto",
|
|
20
|
+
runtimeSources: () => [resolveFrom(import.meta.url, "../../../../runtime/ext_node/crypto/crypto.c")],
|
|
21
|
+
builtins: () => builtins,
|
|
22
|
+
exports: () => exports,
|
|
23
|
+
};
|
|
@@ -22,6 +22,9 @@ import { streamModule } from "./stream/index.js";
|
|
|
22
22
|
import { netModule } from "./net/index.js";
|
|
23
23
|
import { dgramModule } from "./dgram/index.js";
|
|
24
24
|
import { httpModule } from "./http/index.js";
|
|
25
|
+
import { urlModule } from "./url/index.js";
|
|
26
|
+
import { cryptoModule } from "./crypto/index.js";
|
|
27
|
+
import { childProcessModule } from "./child_process/index.js";
|
|
25
28
|
|
|
26
29
|
/** Every Node module the extension currently provides. */
|
|
27
30
|
const modules: readonly NodeModule[] = [
|
|
@@ -35,6 +38,9 @@ const modules: readonly NodeModule[] = [
|
|
|
35
38
|
netModule,
|
|
36
39
|
dgramModule,
|
|
37
40
|
httpModule,
|
|
41
|
+
urlModule,
|
|
42
|
+
cryptoModule,
|
|
43
|
+
childProcessModule,
|
|
38
44
|
];
|
|
39
45
|
|
|
40
46
|
export const nodeExtension: Extension = {
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import { dirname, resolve } from "node:path";
|
|
9
9
|
import { fileURLToPath } from "node:url";
|
|
10
|
+
import { findRuntimeDir } from "../../driver/paths.js";
|
|
10
11
|
import type { BuiltinFunction, ModuleExports } from "../registry.js";
|
|
11
12
|
|
|
12
13
|
export interface NodeModule {
|
|
@@ -26,7 +27,18 @@ export interface NodeModule {
|
|
|
26
27
|
exports?(): ModuleExports;
|
|
27
28
|
}
|
|
28
29
|
|
|
29
|
-
/**
|
|
30
|
+
/**
|
|
31
|
+
* Resolve a C runtime source that is written relative to an extension module
|
|
32
|
+
* (e.g. `../../../../runtime/ext_node/fs/read_file.c`).
|
|
33
|
+
*
|
|
34
|
+
* When running from source the page is resolved against the calling module's
|
|
35
|
+
* own URL, but inside a compiled xbintsc binary `import.meta.url` points at the
|
|
36
|
+
* executable, so the module-relative form no longer resolves. Anchor on the
|
|
37
|
+
* discovered runtime directory instead, which is correct in both worlds.
|
|
38
|
+
*/
|
|
30
39
|
export function resolveFrom(importMetaUrl: string, relative: string): string {
|
|
40
|
+
const marker = "runtime/";
|
|
41
|
+
const index = relative.indexOf(marker);
|
|
42
|
+
if (index >= 0) return resolve(findRuntimeDir(), relative.slice(index + marker.length));
|
|
31
43
|
return resolve(dirname(fileURLToPath(importMetaUrl)), relative);
|
|
32
44
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node's `url` module: `pathToFileURL` / `fileURLToPath`, backed by
|
|
3
|
+
* `runtime/ext_node/url`. These are the helpers the compiler uses to relate
|
|
4
|
+
* `import.meta.url`, `process.argv[1]` and filesystem paths.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { NodeModule } from "../module.js";
|
|
8
|
+
import { resolveFrom } from "../module.js";
|
|
9
|
+
import type { BuiltinFunction, ModuleExports } from "../../registry.js";
|
|
10
|
+
|
|
11
|
+
const builtins: Record<string, BuiltinFunction> = {
|
|
12
|
+
pathToFileURL: { symbol: "xt_url_path_to_file_url" },
|
|
13
|
+
fileURLToPath: { symbol: "xt_url_file_url_to_path" },
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const exports: ModuleExports = {
|
|
17
|
+
pathToFileURL: { symbol: "xt_url_path_to_file_url" },
|
|
18
|
+
fileURLToPath: { symbol: "xt_url_file_url_to_path" },
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const urlModule: NodeModule = {
|
|
22
|
+
name: "url",
|
|
23
|
+
runtimeSources: () => [resolveFrom(import.meta.url, "../../../../runtime/ext_node/url/url.c")],
|
|
24
|
+
builtins: () => builtins,
|
|
25
|
+
exports: () => exports,
|
|
26
|
+
};
|
package/src/lexer/scanner.ts
CHANGED
|
@@ -240,12 +240,12 @@ export class Scanner {
|
|
|
240
240
|
const cleaned = raw.replace(/_/g, "");
|
|
241
241
|
if (isBigInt) {
|
|
242
242
|
raw = raw.slice(0, -1).replace(/_/g, "");
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
} catch {
|
|
243
|
+
const parsed = Number(raw);
|
|
244
|
+
if (parsed !== parsed) {
|
|
246
245
|
this.error(DiagnosticCode.InvalidNumber, `Invalid BigInt literal '${raw}'`, start, this.pos);
|
|
247
|
-
return this.makeToken(TokenKind.BigIntLiteral, start, this.pos, raw, precededByLineBreak,
|
|
246
|
+
return this.makeToken(TokenKind.BigIntLiteral, start, this.pos, raw, precededByLineBreak, 0);
|
|
248
247
|
}
|
|
248
|
+
return this.makeToken(TokenKind.BigIntLiteral, start, this.pos, raw, precededByLineBreak, parsed);
|
|
249
249
|
}
|
|
250
250
|
let value: number;
|
|
251
251
|
if (/^0[xX]/.test(cleaned)) value = parseInt(cleaned.slice(2), 16);
|
|
@@ -157,9 +157,11 @@ export const declarationMethods: DeclarationMethods = {
|
|
|
157
157
|
dotDotDotToken = true;
|
|
158
158
|
}
|
|
159
159
|
let name: import("../ast/declarations.js").BindingName;
|
|
160
|
+
let isThisParameter = false;
|
|
160
161
|
if (this.at(TokenKind.ThisKeyword)) {
|
|
161
162
|
const thisToken = this.nextToken();
|
|
162
163
|
name = { kind: SyntaxKind.Identifier, text: "this", start: thisToken.start, end: thisToken.end };
|
|
164
|
+
isThisParameter = true;
|
|
163
165
|
} else {
|
|
164
166
|
name = this.parseBindingName();
|
|
165
167
|
}
|
|
@@ -178,7 +180,11 @@ export const declarationMethods: DeclarationMethods = {
|
|
|
178
180
|
this.nextToken();
|
|
179
181
|
initializer = this.parseAssignmentExpression();
|
|
180
182
|
}
|
|
181
|
-
|
|
183
|
+
// A `this` parameter is a type-level annotation only and is never a
|
|
184
|
+
// runtime argument, so it must not occupy an `argv` slot.
|
|
185
|
+
if (!isThisParameter) {
|
|
186
|
+
params.push({ kind: SyntaxKind.Parameter, name, modifiers, dotDotDotToken, questionToken, type, initializer, start, end: initializer?.end ?? type?.end ?? name.end });
|
|
187
|
+
}
|
|
182
188
|
if (this.at(TokenKind.Comma)) this.nextToken();
|
|
183
189
|
else break;
|
|
184
190
|
}
|
|
@@ -452,13 +458,10 @@ export const declarationMethods: DeclarationMethods = {
|
|
|
452
458
|
return { kind: SyntaxKind.NumericLiteral, text: token.text, value: Number(token.value ?? 0), start: token.start, end: token.end };
|
|
453
459
|
}
|
|
454
460
|
if (token.kind === TokenKind.OpenBracket) {
|
|
455
|
-
this.nextToken();
|
|
461
|
+
const start = this.nextToken().start;
|
|
456
462
|
const expr = this.parseAssignmentExpression();
|
|
457
|
-
this.parseExpected(TokenKind.CloseBracket);
|
|
458
|
-
|
|
459
|
-
if (name) return name;
|
|
460
|
-
this.error(DiagnosticCode.InvalidTypeSyntax, "Computed property names are not supported by xbintsc");
|
|
461
|
-
return { kind: SyntaxKind.Identifier, text: "<computed>", start: expr.start, end: expr.end };
|
|
463
|
+
const close = this.parseExpected(TokenKind.CloseBracket);
|
|
464
|
+
return { kind: SyntaxKind.ComputedPropertyName, expression: expr, start, end: close.end };
|
|
462
465
|
}
|
|
463
466
|
return this.parseIdentifierName();
|
|
464
467
|
},
|
|
@@ -331,7 +331,7 @@ export const expressionMethods: ExpressionMethods = {
|
|
|
331
331
|
}
|
|
332
332
|
case TokenKind.BigIntLiteral:
|
|
333
333
|
this.nextToken();
|
|
334
|
-
return { kind: SyntaxKind.BigIntLiteral, text: token.text, value:
|
|
334
|
+
return { kind: SyntaxKind.BigIntLiteral, text: token.text, value: Number(token.value ?? 0), start: token.start, end: token.end };
|
|
335
335
|
case TokenKind.StringLiteral:
|
|
336
336
|
this.nextToken();
|
|
337
337
|
return this.stringLiteralFromToken(token);
|