xbintsc 0.2.5 → 0.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/README.md +1 -1
  2. package/README.zh-CN.md +1 -1
  3. package/dist/src/ast/expressions.d.ts +6 -2
  4. package/dist/src/ast/kinds.d.ts +1 -0
  5. package/dist/src/ast/kinds.js +1 -0
  6. package/dist/src/ast/kinds.js.map +1 -1
  7. package/dist/src/binder/binder.js +29 -0
  8. package/dist/src/binder/binder.js.map +1 -1
  9. package/dist/src/codegen/generator/calls.d.ts +1 -1
  10. package/dist/src/codegen/generator/calls.js +57 -14
  11. package/dist/src/codegen/generator/calls.js.map +1 -1
  12. package/dist/src/codegen/generator/context.d.ts +1 -1
  13. package/dist/src/codegen/generator/expressions.js +14 -3
  14. package/dist/src/codegen/generator/expressions.js.map +1 -1
  15. package/dist/src/codegen/generator/module.js +57 -2
  16. package/dist/src/codegen/generator/module.js.map +1 -1
  17. package/dist/src/codegen/generator/state.js +15 -0
  18. package/dist/src/codegen/generator/state.js.map +1 -1
  19. package/dist/src/codegen/generator/statements.js +25 -8
  20. package/dist/src/codegen/generator/statements.js.map +1 -1
  21. package/dist/src/codegen/generator/tables.d.ts +3 -0
  22. package/dist/src/codegen/generator/tables.js +12 -0
  23. package/dist/src/codegen/generator/tables.js.map +1 -1
  24. package/dist/src/codegen/values.d.ts +18 -11
  25. package/dist/src/codegen/values.js +92 -15
  26. package/dist/src/codegen/values.js.map +1 -1
  27. package/dist/src/diagnostics/source.d.ts +1 -1
  28. package/dist/src/diagnostics/source.js +12 -7
  29. package/dist/src/diagnostics/source.js.map +1 -1
  30. package/dist/src/driver/compiler.js +16 -2
  31. package/dist/src/driver/compiler.js.map +1 -1
  32. package/dist/src/extensions/node/child_process/index.d.ts +7 -0
  33. package/dist/src/extensions/node/child_process/index.js +19 -0
  34. package/dist/src/extensions/node/child_process/index.js.map +1 -0
  35. package/dist/src/extensions/node/crypto/index.d.ts +6 -0
  36. package/dist/src/extensions/node/crypto/index.js +18 -0
  37. package/dist/src/extensions/node/crypto/index.js.map +1 -0
  38. package/dist/src/extensions/node/index.js +6 -0
  39. package/dist/src/extensions/node/index.js.map +1 -1
  40. package/dist/src/extensions/node/module.d.ts +9 -1
  41. package/dist/src/extensions/node/module.js +14 -1
  42. package/dist/src/extensions/node/module.js.map +1 -1
  43. package/dist/src/extensions/node/url/index.d.ts +7 -0
  44. package/dist/src/extensions/node/url/index.js +21 -0
  45. package/dist/src/extensions/node/url/index.js.map +1 -0
  46. package/dist/src/lexer/scanner.js +4 -5
  47. package/dist/src/lexer/scanner.js.map +1 -1
  48. package/dist/src/parser/declarations.js +10 -9
  49. package/dist/src/parser/declarations.js.map +1 -1
  50. package/dist/src/parser/expressions.js +1 -1
  51. package/dist/src/parser/expressions.js.map +1 -1
  52. package/dist/tests/e2e/compile.test.js +36 -0
  53. package/dist/tests/e2e/compile.test.js.map +1 -1
  54. package/package.json +1 -1
  55. package/runtime/ext_node/child_process/child_process.c +213 -0
  56. package/runtime/ext_node/crypto/crypto.c +177 -0
  57. package/runtime/ext_node/process/process.c +37 -3
  58. package/runtime/ext_node/url/url.c +85 -0
  59. package/runtime/rt.h +20 -0
  60. package/runtime/rt_internal.h +11 -0
  61. package/runtime/xt_builtins.c +23 -0
  62. package/runtime/xt_containers.c +136 -10
  63. package/runtime/xt_io.c +53 -0
  64. package/runtime/xt_regex.h +16 -6
  65. package/runtime/xt_stdlib.c +11 -2
  66. package/runtime/xt_stdlib2.c +239 -13
  67. package/runtime/xt_values.c +2 -1
  68. package/src/ast/expressions.ts +7 -2
  69. package/src/ast/kinds.ts +1 -0
  70. package/src/binder/binder.ts +29 -0
  71. package/src/codegen/generator/calls.ts +59 -16
  72. package/src/codegen/generator/context.ts +1 -1
  73. package/src/codegen/generator/expressions.ts +14 -3
  74. package/src/codegen/generator/module.ts +60 -1
  75. package/src/codegen/generator/state.ts +15 -0
  76. package/src/codegen/generator/statements.ts +23 -8
  77. package/src/codegen/generator/tables.ts +13 -0
  78. package/src/codegen/values.ts +85 -16
  79. package/src/diagnostics/source.ts +12 -7
  80. package/src/driver/compiler.ts +15 -2
  81. package/src/extensions/node/child_process/index.ts +24 -0
  82. package/src/extensions/node/crypto/index.ts +23 -0
  83. package/src/extensions/node/index.ts +6 -0
  84. package/src/extensions/node/module.ts +13 -1
  85. package/src/extensions/node/url/index.ts +26 -0
  86. package/src/lexer/scanner.ts +4 -4
  87. package/src/parser/declarations.ts +10 -7
  88. 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) continue;
152
- if (this.current.slots.has(symbol.id)) continue; // hoisted `var`
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 @xt_array_length(i64 ${iterable})`);
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 @xt_get(i64 ${iterable}, i64 ${index})`);
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*)`). */
@@ -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
- export const XT_TAG_MASK = 0xffff000000000000n;
11
- export const XT_NUMBER_MASK = 0xfff8000000000000n;
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 = 0xfff8000000000000n;
14
- export const XT_NULL = 0xfff9000000000000n;
15
- export const XT_FALSE = 0xfffa000000000000n;
16
- export const XT_TRUE = 0xfffb000000000000n;
17
- export const XT_STRING = 0xfffc000000000000n;
18
- export const XT_OBJECT = 0xfffd000000000000n;
19
- export const XT_ARRAY = 0xfffe000000000000n;
20
- export const XT_FUNCTION = 0xffff000000000000n;
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: bigint): string {
24
- return BigInt.asIntN(64, value).toString();
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
- const view = new DataView(new ArrayBuffer(8));
30
- view.setFloat64(0, value);
31
- return i64(view.getBigInt64(0));
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 i64(value ? XT_TRUE : XT_FALSE);
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 = 0xcbf29ce484222325n): string {
84
- let hash = seed;
85
- const prime = 0x100000001b3n;
86
- const mask = 0xffffffffffffffffn;
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
- hash ^= BigInt(text.charCodeAt(i));
89
- hash = (hash * prime) & mask;
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 hash.toString(16).padStart(16, "0");
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. */
@@ -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
- /** Resolve a path relative to the folder of the module that calls this. */
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
+ };
@@ -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
- try {
244
- return this.makeToken(TokenKind.BigIntLiteral, start, this.pos, raw, precededByLineBreak, BigInt(raw));
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, 0n);
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
- params.push({ kind: SyntaxKind.Parameter, name, modifiers, dotDotDotToken, questionToken, type, initializer, start, end: initializer?.end ?? type?.end ?? name.end });
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
- const name = expressionToPropertyName(expr);
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: BigInt(token.value ?? 0n), start: token.start, end: token.end };
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);