xbintsc 0.3.3 → 0.3.5
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 +8 -0
- package/dist/src/binder/binder.js +1 -0
- package/dist/src/binder/binder.js.map +1 -1
- package/dist/src/binder/references.d.ts +1 -1
- package/dist/src/binder/references.js +34 -5
- package/dist/src/binder/references.js.map +1 -1
- package/dist/src/binder/types.d.ts +4 -0
- package/dist/src/binder/types.js.map +1 -1
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/codegen/generator/calls/access.d.ts +9 -1
- package/dist/src/codegen/generator/calls/access.js +146 -13
- package/dist/src/codegen/generator/calls/access.js.map +1 -1
- package/dist/src/codegen/generator/calls/closures.d.ts +1 -0
- package/dist/src/codegen/generator/calls/closures.js +25 -0
- package/dist/src/codegen/generator/calls/closures.js.map +1 -1
- package/dist/src/codegen/generator/calls/invocation.js +12 -1
- package/dist/src/codegen/generator/calls/invocation.js.map +1 -1
- package/dist/src/codegen/generator/calls/literals.js +12 -4
- package/dist/src/codegen/generator/calls/literals.js.map +1 -1
- package/dist/src/codegen/generator/calls/super.js +4 -7
- package/dist/src/codegen/generator/calls/super.js.map +1 -1
- package/dist/src/codegen/generator/context.d.ts +21 -2
- package/dist/src/codegen/generator/context.js +42 -5
- package/dist/src/codegen/generator/context.js.map +1 -1
- package/dist/src/codegen/generator/expressions/assignment.js +1 -3
- package/dist/src/codegen/generator/expressions/assignment.js.map +1 -1
- package/dist/src/codegen/generator/expressions/primary.d.ts +3 -1
- package/dist/src/codegen/generator/expressions/primary.js +99 -1
- package/dist/src/codegen/generator/expressions/primary.js.map +1 -1
- package/dist/src/codegen/generator/generator.js.map +1 -1
- package/dist/src/codegen/generator/module.js +25 -15
- package/dist/src/codegen/generator/module.js.map +1 -1
- package/dist/src/codegen/generator/state.d.ts +15 -0
- package/dist/src/codegen/generator/state.js +28 -1
- package/dist/src/codegen/generator/state.js.map +1 -1
- package/dist/src/codegen/generator/statements/control-flow.d.ts +3 -1
- package/dist/src/codegen/generator/statements/control-flow.js +85 -0
- package/dist/src/codegen/generator/statements/control-flow.js.map +1 -1
- package/dist/src/codegen/generator/statements/dispatch.js +15 -6
- package/dist/src/codegen/generator/statements/dispatch.js.map +1 -1
- package/dist/src/codegen/generator/statements/loops.d.ts +3 -0
- package/dist/src/codegen/generator/statements/loops.js +25 -11
- package/dist/src/codegen/generator/statements/loops.js.map +1 -1
- package/dist/src/codegen/generator/tables.d.ts +7 -0
- package/dist/src/codegen/generator/tables.js +41 -1
- package/dist/src/codegen/generator/tables.js.map +1 -1
- package/dist/src/driver/compiler.d.ts +1 -1
- package/dist/src/driver/compiler.js +33 -1
- package/dist/src/driver/compiler.js.map +1 -1
- package/dist/src/driver/modules.d.ts +4 -3
- package/dist/src/driver/modules.js +61 -4
- package/dist/src/driver/modules.js.map +1 -1
- package/dist/src/driver/toolchain.js +1 -1
- package/dist/src/driver/toolchain.js.map +1 -1
- package/dist/src/lexer/scanner.js.map +1 -1
- package/dist/src/parser/declarations/classes.js +4 -0
- package/dist/src/parser/declarations/classes.js.map +1 -1
- package/dist/src/parser/declarations/type-members.js.map +1 -1
- package/dist/src/parser/expressions.d.ts +2 -1
- package/dist/src/parser/expressions.js +10 -1
- package/dist/src/parser/expressions.js.map +1 -1
- package/dist/src/parser/helpers.js +2 -2
- package/dist/src/parser/helpers.js.map +1 -1
- package/dist/src/parser/literals.js +79 -6
- package/dist/src/parser/literals.js.map +1 -1
- package/dist/src/parser/modules.js.map +1 -1
- package/dist/src/parser/parser.js +0 -1
- package/dist/src/parser/parser.js.map +1 -1
- package/dist/src/parser/statements.d.ts +1 -0
- package/dist/src/parser/statements.js +13 -1
- package/dist/src/parser/statements.js.map +1 -1
- package/dist/src/parser/types.js +2 -2
- package/dist/src/parser/types.js.map +1 -1
- package/dist/tests/ast/factory.test.d.ts +1 -0
- package/dist/tests/ast/factory.test.js +35 -0
- package/dist/tests/ast/factory.test.js.map +1 -0
- package/dist/tests/ast/visitor.test.d.ts +1 -0
- package/dist/tests/ast/visitor.test.js +53 -0
- package/dist/tests/ast/visitor.test.js.map +1 -0
- package/dist/tests/cli/main.test.js +109 -9
- package/dist/tests/cli/main.test.js.map +1 -1
- package/dist/tests/codegen/closures.test.d.ts +1 -0
- package/dist/tests/codegen/closures.test.js +32 -0
- package/dist/tests/codegen/closures.test.js.map +1 -0
- package/dist/tests/codegen/expressions.test.d.ts +1 -0
- package/dist/tests/codegen/expressions.test.js +149 -0
- package/dist/tests/codegen/expressions.test.js.map +1 -0
- package/dist/tests/codegen/llvm.test.js +14 -0
- package/dist/tests/codegen/llvm.test.js.map +1 -1
- package/dist/tests/codegen/values.test.d.ts +1 -0
- package/dist/tests/codegen/values.test.js +48 -0
- package/dist/tests/codegen/values.test.js.map +1 -0
- package/dist/tests/diagnostics/diagnostic.test.d.ts +1 -0
- package/dist/tests/diagnostics/diagnostic.test.js +117 -0
- package/dist/tests/diagnostics/diagnostic.test.js.map +1 -0
- package/dist/tests/diagnostics/source.test.d.ts +1 -0
- package/dist/tests/diagnostics/source.test.js +58 -0
- package/dist/tests/diagnostics/source.test.js.map +1 -0
- package/dist/tests/driver/modules.test.d.ts +1 -0
- package/dist/tests/driver/modules.test.js +140 -0
- package/dist/tests/driver/modules.test.js.map +1 -0
- package/dist/tests/driver/paths.test.js +17 -1
- package/dist/tests/driver/paths.test.js.map +1 -1
- package/dist/tests/driver/runtime-lib.test.d.ts +1 -0
- package/dist/tests/driver/runtime-lib.test.js +19 -0
- package/dist/tests/driver/runtime-lib.test.js.map +1 -0
- package/dist/tests/driver/toolchain-provider.test.js +91 -0
- package/dist/tests/driver/toolchain-provider.test.js.map +1 -1
- package/dist/tests/driver/toolchain.test.js +11 -1
- package/dist/tests/driver/toolchain.test.js.map +1 -1
- package/dist/tests/e2e/compile-node.test.js +14 -1
- package/dist/tests/e2e/compile-node.test.js.map +1 -1
- package/dist/tests/e2e/compile.test.js +465 -2
- package/dist/tests/e2e/compile.test.js.map +1 -1
- package/dist/tests/e2e/differential.test.d.ts +7 -0
- package/dist/tests/e2e/differential.test.js +581 -0
- package/dist/tests/e2e/differential.test.js.map +1 -0
- package/dist/tests/e2e/harness.d.ts +21 -0
- package/dist/tests/e2e/harness.js +26 -0
- package/dist/tests/e2e/harness.js.map +1 -1
- package/dist/tests/extensions/node.test.d.ts +1 -0
- package/dist/tests/extensions/node.test.js +32 -0
- package/dist/tests/extensions/node.test.js.map +1 -0
- package/dist/tests/lexer/identifiers.test.d.ts +1 -0
- package/dist/tests/lexer/identifiers.test.js +13 -0
- package/dist/tests/lexer/identifiers.test.js.map +1 -0
- package/dist/tests/lexer/numbers.test.d.ts +1 -0
- package/dist/tests/lexer/numbers.test.js +23 -0
- package/dist/tests/lexer/numbers.test.js.map +1 -0
- package/dist/tests/lexer/strings.test.d.ts +1 -0
- package/dist/tests/lexer/strings.test.js +72 -0
- package/dist/tests/lexer/strings.test.js.map +1 -0
- package/dist/tests/parser/declarations.test.d.ts +1 -0
- package/dist/tests/parser/declarations.test.js +70 -0
- package/dist/tests/parser/declarations.test.js.map +1 -0
- package/dist/tests/parser/expressions.test.d.ts +1 -0
- package/dist/tests/parser/expressions.test.js +47 -0
- package/dist/tests/parser/expressions.test.js.map +1 -0
- package/dist/tests/parser/helpers.test.d.ts +1 -0
- package/dist/tests/parser/helpers.test.js +58 -0
- package/dist/tests/parser/helpers.test.js.map +1 -0
- package/dist/tests/parser/literals.test.d.ts +1 -0
- package/dist/tests/parser/literals.test.js +70 -0
- package/dist/tests/parser/literals.test.js.map +1 -0
- package/dist/tests/parser/module-syntax.test.d.ts +1 -0
- package/dist/tests/parser/module-syntax.test.js +91 -0
- package/dist/tests/parser/module-syntax.test.js.map +1 -0
- package/dist/tests/parser/statements.test.d.ts +1 -0
- package/dist/tests/parser/statements.test.js +85 -0
- package/dist/tests/parser/statements.test.js.map +1 -0
- package/dist/tests/parser/type-members.test.d.ts +1 -0
- package/dist/tests/parser/type-members.test.js +86 -0
- package/dist/tests/parser/type-members.test.js.map +1 -0
- package/dist/tests/parser/types.test.d.ts +1 -0
- package/dist/tests/parser/types.test.js +143 -0
- package/dist/tests/parser/types.test.js.map +1 -0
- package/package.json +6 -1
- package/runtime/ext_node/fs/fs_ops.c +36 -1
- package/runtime/rt.h +17 -0
- package/runtime/rt_internal.h +88 -2
- package/runtime/xt_builtins.c +174 -6
- package/runtime/xt_containers.c +234 -42
- package/runtime/xt_generator.c +304 -0
- package/runtime/xt_io.c +170 -30
- package/runtime/xt_promise.c +6 -1
- package/runtime/xt_regex.h +105 -35
- package/runtime/xt_stdlib/dispatch.inc +255 -4
- package/runtime/xt_stdlib/strings.inc +17 -13
- package/runtime/xt_stdlib2/arrays.inc +126 -19
- package/runtime/xt_stdlib2/date.inc +9 -2
- package/runtime/xt_stdlib2/error.inc +162 -12
- package/runtime/xt_stdlib2/helpers.inc +4 -0
- package/runtime/xt_stdlib2/json.inc +19 -6
- package/runtime/xt_stdlib2/number-string-statics.inc +1 -1
- package/runtime/xt_stdlib2/numbers.inc +4 -5
- package/runtime/xt_stdlib2/objects.inc +29 -0
- package/runtime/xt_stdlib2/regexp-match.inc +293 -51
- package/runtime/xt_stdlib2/set.inc +58 -2
- package/runtime/xt_stdlib2/strings.inc +38 -0
- package/runtime/xt_symbol.c +149 -0
- package/runtime/xt_values.c +221 -15
- package/scripts/build-runtime.ts +1 -1
- package/src/ast/expressions.ts +8 -0
- package/src/binder/binder.ts +1 -1
- package/src/binder/references.ts +47 -4
- package/src/binder/types.ts +4 -0
- package/src/cli/main.ts +1 -2
- package/src/codegen/generator/calls/access.ts +158 -13
- package/src/codegen/generator/calls/closures.ts +24 -0
- package/src/codegen/generator/calls/invocation.ts +11 -1
- package/src/codegen/generator/calls/literals.ts +10 -4
- package/src/codegen/generator/calls/super.ts +6 -9
- package/src/codegen/generator/context.ts +44 -7
- package/src/codegen/generator/expressions/assignment.ts +1 -3
- package/src/codegen/generator/expressions/primary.ts +108 -2
- package/src/codegen/generator/generator.ts +0 -1
- package/src/codegen/generator/module.ts +24 -18
- package/src/codegen/generator/state.ts +44 -1
- package/src/codegen/generator/statements/control-flow.ts +86 -0
- package/src/codegen/generator/statements/dispatch.ts +18 -6
- package/src/codegen/generator/statements/loops.ts +29 -11
- package/src/codegen/generator/tables.ts +44 -1
- package/src/driver/compiler.ts +32 -2
- package/src/driver/modules.ts +64 -4
- package/src/driver/toolchain.ts +1 -1
- package/src/lexer/scanner.ts +0 -1
- package/src/parser/declarations/classes.ts +4 -0
- package/src/parser/declarations/type-members.ts +0 -2
- package/src/parser/expressions.ts +13 -1
- package/src/parser/helpers.ts +2 -2
- package/src/parser/literals.ts +82 -7
- package/src/parser/modules.ts +0 -1
- package/src/parser/parser.ts +0 -1
- package/src/parser/statements.ts +14 -0
- package/src/parser/types.ts +2 -2
|
@@ -9,7 +9,6 @@ import {
|
|
|
9
9
|
type ArrowFunction,
|
|
10
10
|
type Block,
|
|
11
11
|
type Expression,
|
|
12
|
-
type Identifier,
|
|
13
12
|
type Parameter,
|
|
14
13
|
} from "../../ast/nodes.js";
|
|
15
14
|
import type { ClassInfo, FunctionInfo, SymbolInfo } from "../../binder/binder.js";
|
|
@@ -94,7 +93,9 @@ export const moduleMethods: ModuleMethods = {
|
|
|
94
93
|
label: 0,
|
|
95
94
|
terminated: false,
|
|
96
95
|
loops: [],
|
|
96
|
+
pendingLabels: [],
|
|
97
97
|
tryFrames: [],
|
|
98
|
+
finallyStack: [],
|
|
98
99
|
escapePointers: [],
|
|
99
100
|
usesTry: false,
|
|
100
101
|
};
|
|
@@ -157,11 +158,8 @@ export const moduleMethods: ModuleMethods = {
|
|
|
157
158
|
if (fn.isConstructor && fn.classInfo?.parentExpression) {
|
|
158
159
|
const explicitBody = (fn.node as { body?: Block }).body;
|
|
159
160
|
const parent = fn.classInfo.parentExpression;
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
(parent as Identifier).text === "Error" &&
|
|
163
|
-
!this.binding.symbolOfIdentifier.get(parent as Identifier);
|
|
164
|
-
if (!explicitBody && !unboundBuiltin) {
|
|
161
|
+
const errorName = this.errorFamilyName(parent);
|
|
162
|
+
if (!explicitBody && !errorName) {
|
|
165
163
|
const parentValue = this.emitExpression(parent);
|
|
166
164
|
const parentProto = this.reg();
|
|
167
165
|
this.emit(` ${parentProto} = call i64 @xt_function_get_prototype(i64 ${parentValue})`);
|
|
@@ -170,10 +168,12 @@ export const moduleMethods: ModuleMethods = {
|
|
|
170
168
|
this.emit(` ${parentCtor} = call i64 @xt_get(i64 ${parentProto}, i64 ${ctorKey})`);
|
|
171
169
|
const superThis = this.emitThis();
|
|
172
170
|
this.emit(` call i64 @xt_call_with_this(i64 ${parentCtor}, i64 ${superThis}, i32 %argc, i64* %argv)`);
|
|
173
|
-
} else if (!explicitBody &&
|
|
174
|
-
// `extends Error` with no explicit constructor:
|
|
171
|
+
} else if (!explicitBody && errorName) {
|
|
172
|
+
// `extends Error` / `extends TypeError` with no explicit constructor:
|
|
173
|
+
// initialise the instance's name and message from the arguments.
|
|
175
174
|
const superThis = this.emitThis();
|
|
176
|
-
this.
|
|
175
|
+
const nameValue = this.stringValue(errorName);
|
|
176
|
+
this.emit(` call i64 @xt_error_init(i64 ${superThis}, i64 ${nameValue}, i32 %argc, i64* %argv)`);
|
|
177
177
|
}
|
|
178
178
|
}
|
|
179
179
|
|
|
@@ -274,14 +274,16 @@ export const moduleMethods: ModuleMethods = {
|
|
|
274
274
|
const proto = this.reg();
|
|
275
275
|
this.emit(` ${proto} = call i64 @xt_object_new()`);
|
|
276
276
|
if (classInfo.parentExpression) {
|
|
277
|
-
// `extends Error`
|
|
278
|
-
//
|
|
277
|
+
// `extends Error` / `extends TypeError` / other host builtins: chain the
|
|
278
|
+
// subclass prototype onto the builtin's prototype so `instanceof` works.
|
|
279
279
|
const parent = classInfo.parentExpression;
|
|
280
|
-
const
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
280
|
+
const errorName = this.errorFamilyName(parent);
|
|
281
|
+
if (errorName) {
|
|
282
|
+
const nameValue = this.stringValue(errorName);
|
|
283
|
+
const parentProto = this.reg();
|
|
284
|
+
this.emit(` ${parentProto} = call i64 @xt_error_prototype(i64 ${nameValue})`);
|
|
285
|
+
this.emit(` call i64 @xt_object_set_prototype(i64 ${proto}, i64 ${parentProto})`);
|
|
286
|
+
} else {
|
|
285
287
|
const parentValue = this.emitExpression(parent);
|
|
286
288
|
const parentProto = this.reg();
|
|
287
289
|
this.emit(` ${parentProto} = call i64 @xt_function_get_prototype(i64 ${parentValue})`);
|
|
@@ -290,7 +292,9 @@ export const moduleMethods: ModuleMethods = {
|
|
|
290
292
|
}
|
|
291
293
|
for (const method of classInfo.methods) {
|
|
292
294
|
const fnValue = this.emitClosureValue(method);
|
|
293
|
-
const key =
|
|
295
|
+
const key = method.computedKey
|
|
296
|
+
? this.emitExpression(method.computedKey)
|
|
297
|
+
: this.stringValue(method.name);
|
|
294
298
|
const accessor = (method.node as { accessor?: "get" | "set" }).accessor;
|
|
295
299
|
if (accessor === "get") {
|
|
296
300
|
this.emit(` call i64 @xt_object_define_getter(i64 ${proto}, i64 ${key}, i64 ${fnValue})`);
|
|
@@ -306,7 +310,9 @@ export const moduleMethods: ModuleMethods = {
|
|
|
306
310
|
this.emit(` call i64 @xt_set(i64 ${proto}, i64 ${ctorKey}, i64 ${ctor})`);
|
|
307
311
|
for (const method of classInfo.statics) {
|
|
308
312
|
const fnValue = this.emitClosureValue(method);
|
|
309
|
-
const key =
|
|
313
|
+
const key = method.computedKey
|
|
314
|
+
? this.emitExpression(method.computedKey)
|
|
315
|
+
: this.stringValue(method.name);
|
|
310
316
|
this.emit(` call i64 @xt_set(i64 ${ctor}, i64 ${key}, i64 ${fnValue})`);
|
|
311
317
|
}
|
|
312
318
|
for (const field of classInfo.fields) {
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import type { FunctionInfo } from "../../binder/binder.js";
|
|
6
6
|
import type { BindResult } from "../../binder/binder.js";
|
|
7
7
|
import type { BuiltinFunction, ExtensionModule } from "../../extensions/registry.js";
|
|
8
|
+
import type { Block } from "../../ast/nodes.js";
|
|
8
9
|
|
|
9
10
|
export interface Slot {
|
|
10
11
|
/** Register holding the `i64*` to the storage. */
|
|
@@ -12,11 +13,23 @@ export interface Slot {
|
|
|
12
13
|
readonly boxed: boolean;
|
|
13
14
|
}
|
|
14
15
|
|
|
16
|
+
/**
|
|
17
|
+
* One enclosing `try`/`finally` region, used to run the `finally` block when an
|
|
18
|
+
* abrupt completion (`return`/`break`/`continue`) exits it.
|
|
19
|
+
*/
|
|
20
|
+
export interface FinallyContext {
|
|
21
|
+
/** Number of active `try` frames just before this region's frame. */
|
|
22
|
+
readonly frameDepth: number;
|
|
23
|
+
readonly block: Block;
|
|
24
|
+
}
|
|
25
|
+
|
|
15
26
|
export interface LoopLabels {
|
|
16
27
|
readonly breakLabel: string;
|
|
17
28
|
readonly continueLabel: string;
|
|
18
29
|
/** Number of enclosing `try` frames when the loop was entered. */
|
|
19
30
|
readonly tryDepth?: number;
|
|
31
|
+
/** Labels attached to this loop (outermost first), for `break`/`continue`. */
|
|
32
|
+
readonly labels?: readonly string[];
|
|
20
33
|
}
|
|
21
34
|
|
|
22
35
|
export interface FunctionState {
|
|
@@ -28,7 +41,10 @@ export interface FunctionState {
|
|
|
28
41
|
label: number;
|
|
29
42
|
terminated: boolean;
|
|
30
43
|
readonly loops: LoopLabels[];
|
|
44
|
+
/** Labels on enclosing `LabeledStatement`s, consumed by the next loop. */
|
|
45
|
+
readonly pendingLabels: string[];
|
|
31
46
|
readonly tryFrames: string[];
|
|
47
|
+
readonly finallyStack: FinallyContext[];
|
|
32
48
|
readonly escapePointers: string[];
|
|
33
49
|
/** Alloca holding the `this` binding for the current function. */
|
|
34
50
|
thisPtr?: string;
|
|
@@ -57,6 +73,8 @@ export const RUNTIME_DECLARATIONS: readonly string[] = [
|
|
|
57
73
|
"declare i64 @xt_string_from_cstr(i8*)",
|
|
58
74
|
"declare i64 @xt_arg(i32, i64*, i32)",
|
|
59
75
|
"declare i64 @xt_closure_new(i8*, i32, i64*)",
|
|
76
|
+
"declare i64 @xt_function_set_metadata(i64, i64, i32)",
|
|
77
|
+
"declare i64 @xt_function_set_generator(i64)",
|
|
60
78
|
"declare i64 @xt_closure_call(i64, i32, i64*)",
|
|
61
79
|
"declare i64 @xt_closure_env(i64, i32)",
|
|
62
80
|
"declare i64 @xt_call_with_this(i64, i64, i32, i64*)",
|
|
@@ -108,7 +126,16 @@ export const RUNTIME_DECLARATIONS: readonly string[] = [
|
|
|
108
126
|
"declare i64 @xt_date_ctor(i32, i64*)",
|
|
109
127
|
"declare i64 @xt_regexp_ctor(i32, i64*)",
|
|
110
128
|
"declare i64 @xt_error_ctor(i32, i64*)",
|
|
111
|
-
"declare i64 @
|
|
129
|
+
"declare i64 @xt_type_error_ctor(i32, i64*)",
|
|
130
|
+
"declare i64 @xt_range_error_ctor(i32, i64*)",
|
|
131
|
+
"declare i64 @xt_syntax_error_ctor(i32, i64*)",
|
|
132
|
+
"declare i64 @xt_reference_error_ctor(i32, i64*)",
|
|
133
|
+
"declare i64 @xt_eval_error_ctor(i32, i64*)",
|
|
134
|
+
"declare i64 @xt_uri_error_ctor(i32, i64*)",
|
|
135
|
+
"declare i64 @xt_aggregate_error_ctor(i32, i64*)",
|
|
136
|
+
"declare i64 @xt_error_constructor(i64)",
|
|
137
|
+
"declare i64 @xt_error_prototype(i64)",
|
|
138
|
+
"declare i64 @xt_error_init(i64, i64, i32, i64*)",
|
|
112
139
|
"declare i64 @xt_json_parse(i32, i64*)",
|
|
113
140
|
"declare i64 @xt_json_stringify(i32, i64*)",
|
|
114
141
|
"declare i64 @xt_object_from_entries(i64)",
|
|
@@ -169,6 +196,10 @@ export const RUNTIME_DECLARATIONS: readonly string[] = [
|
|
|
169
196
|
"declare i64 @xt_parse_float(i32, i64*)",
|
|
170
197
|
"declare i64 @xt_is_nan(i32, i64*)",
|
|
171
198
|
"declare i64 @xt_is_finite(i32, i64*)",
|
|
199
|
+
"declare i64 @xt_encode_uri_component(i32, i64*)",
|
|
200
|
+
"declare i64 @xt_encode_uri(i32, i64*)",
|
|
201
|
+
"declare i64 @xt_decode_uri_component(i32, i64*)",
|
|
202
|
+
"declare i64 @xt_decode_uri(i32, i64*)",
|
|
172
203
|
"declare i64 @xt_number_ctor(i32, i64*)",
|
|
173
204
|
"declare i64 @xt_bigint_ctor(i32, i64*)",
|
|
174
205
|
"declare i64 @xt_bigint_static(i64, i32, i64*)",
|
|
@@ -185,17 +216,29 @@ export const RUNTIME_DECLARATIONS: readonly string[] = [
|
|
|
185
216
|
"declare i64 @xt_array_length(i64)",
|
|
186
217
|
"declare i64 @xt_array_spread(i64, i64)",
|
|
187
218
|
"declare i64 @xt_iter_length(i64)",
|
|
219
|
+
"declare i64 @xt_iter_open(i64)",
|
|
220
|
+
"declare i64 @xt_iter_has(i64, i64)",
|
|
188
221
|
"declare i64 @xt_iter_value(i64, i64)",
|
|
222
|
+
"declare i64 @xt_yield(i64)",
|
|
223
|
+
"declare i64 @xt_yield_star(i64)",
|
|
189
224
|
"declare i32 @xt_array_size(i64)",
|
|
190
225
|
"declare i64* @xt_array_items(i64)",
|
|
191
226
|
"declare i64 @xt_builtin_value_parseInt(i64, i64, i32, i64*)",
|
|
192
227
|
"declare i64 @xt_builtin_value_parseFloat(i64, i64, i32, i64*)",
|
|
193
228
|
"declare i64 @xt_builtin_value_isNaN(i64, i64, i32, i64*)",
|
|
194
229
|
"declare i64 @xt_builtin_value_isFinite(i64, i64, i32, i64*)",
|
|
230
|
+
"declare i64 @xt_builtin_value_encodeURIComponent(i64, i64, i32, i64*)",
|
|
231
|
+
"declare i64 @xt_builtin_value_encodeURI(i64, i64, i32, i64*)",
|
|
232
|
+
"declare i64 @xt_builtin_value_decodeURIComponent(i64, i64, i32, i64*)",
|
|
233
|
+
"declare i64 @xt_builtin_value_decodeURI(i64, i64, i32, i64*)",
|
|
195
234
|
"declare i64 @xt_builtin_value_number(i64, i64, i32, i64*)",
|
|
196
235
|
"declare i64 @xt_builtin_value_string(i64, i64, i32, i64*)",
|
|
197
236
|
"declare i64 @xt_builtin_value_boolean(i64, i64, i32, i64*)",
|
|
198
237
|
"declare i64 @xt_builtin_value_bigint(i64, i64, i32, i64*)",
|
|
238
|
+
"declare i64 @xt_builtin_value_symbol(i64, i64, i32, i64*)",
|
|
239
|
+
"declare i64 @xt_symbol(i32, i64*)",
|
|
240
|
+
"declare i64 @xt_symbol_static(i64, i32, i64*)",
|
|
241
|
+
"declare i64 @xt_symbol_get(i64)",
|
|
199
242
|
"declare i64 @xt_get(i64, i64)",
|
|
200
243
|
"declare i64 @xt_set(i64, i64, i64)",
|
|
201
244
|
"declare i64 @xt_box_new(i64)",
|
|
@@ -7,7 +7,9 @@ import {
|
|
|
7
7
|
type CaseClause,
|
|
8
8
|
type Identifier,
|
|
9
9
|
type IfStatement,
|
|
10
|
+
type LabeledStatement,
|
|
10
11
|
type ReturnStatement,
|
|
12
|
+
type Statement,
|
|
11
13
|
type SwitchStatement,
|
|
12
14
|
type TryStatement,
|
|
13
15
|
} from "../../../ast/nodes.js";
|
|
@@ -16,14 +18,54 @@ import type { Generator } from "../generator.js";
|
|
|
16
18
|
|
|
17
19
|
export interface ControlFlowStatementMethods {
|
|
18
20
|
emitIf(this: Generator, statement: IfStatement): void;
|
|
21
|
+
emitLabeled(this: Generator, statement: LabeledStatement): void;
|
|
19
22
|
emitSwitch(this: Generator, statement: SwitchStatement): void;
|
|
20
23
|
emitTry(this: Generator, statement: TryStatement): void;
|
|
21
24
|
bindCatchVariable(this: Generator, variable: Identifier | undefined, value: string): void;
|
|
22
25
|
emitReturn(this: Generator, statement: ReturnStatement): void;
|
|
23
26
|
popTryFramesTo(this: Generator, depth: number): void;
|
|
27
|
+
runFinallysBeforeExit(this: Generator, targetTryDepth: number): void;
|
|
24
28
|
}
|
|
25
29
|
|
|
26
30
|
export const controlFlowStatementMethods: ControlFlowStatementMethods = {
|
|
31
|
+
/**
|
|
32
|
+
* `label: statement`. A label on a loop is attached to that loop's break and
|
|
33
|
+
* continue targets; a label on any other statement becomes a breakable
|
|
34
|
+
* region so `break label` can jump to its end.
|
|
35
|
+
*/
|
|
36
|
+
emitLabeled(statement: LabeledStatement): void {
|
|
37
|
+
const labels: string[] = [];
|
|
38
|
+
let current: Statement = statement;
|
|
39
|
+
while (current.kind === SyntaxKind.LabeledStatement) {
|
|
40
|
+
labels.push((current as LabeledStatement).label.text);
|
|
41
|
+
current = (current as LabeledStatement).statement;
|
|
42
|
+
}
|
|
43
|
+
const isLoop =
|
|
44
|
+
current.kind === SyntaxKind.WhileStatement ||
|
|
45
|
+
current.kind === SyntaxKind.DoStatement ||
|
|
46
|
+
current.kind === SyntaxKind.ForStatement ||
|
|
47
|
+
current.kind === SyntaxKind.ForOfStatement ||
|
|
48
|
+
current.kind === SyntaxKind.ForInStatement;
|
|
49
|
+
if (isLoop) {
|
|
50
|
+
this.current.pendingLabels.push(...labels);
|
|
51
|
+
this.emitStatement(current);
|
|
52
|
+
/* The loop consumed them; guard against a loop kind that did not. */
|
|
53
|
+
this.current.pendingLabels.length = 0;
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const endLabel = this.label("label.end");
|
|
57
|
+
this.current.loops.push({
|
|
58
|
+
breakLabel: endLabel,
|
|
59
|
+
continueLabel: endLabel,
|
|
60
|
+
labels,
|
|
61
|
+
tryDepth: this.current.tryFrames.length,
|
|
62
|
+
});
|
|
63
|
+
this.emitStatement(current);
|
|
64
|
+
this.current.loops.pop();
|
|
65
|
+
if (!this.current.terminated) this.terminate(`br label %${endLabel}`);
|
|
66
|
+
this.startBlock(endLabel);
|
|
67
|
+
},
|
|
68
|
+
|
|
27
69
|
emitIf(statement: IfStatement): void {
|
|
28
70
|
const thenLabel = this.label("if.then");
|
|
29
71
|
const elseLabel = statement.elseStatement ? this.label("if.else") : undefined;
|
|
@@ -115,6 +157,10 @@ export const controlFlowStatementMethods: ControlFlowStatementMethods = {
|
|
|
115
157
|
const finallyLabel = statement.finallyBlock ? this.label("try.finally") : undefined;
|
|
116
158
|
const endLabel = this.label("try.end");
|
|
117
159
|
const hasCatch = !!statement.catchClause;
|
|
160
|
+
const finallyContext =
|
|
161
|
+
statement.finallyBlock && finallyLabel
|
|
162
|
+
? { frameDepth: this.current.tryFrames.length, block: statement.finallyBlock }
|
|
163
|
+
: undefined;
|
|
118
164
|
|
|
119
165
|
const frame = this.reg();
|
|
120
166
|
this.emit(` ${frame} = call i8* @xt_try_enter()`);
|
|
@@ -136,7 +182,9 @@ export const controlFlowStatementMethods: ControlFlowStatementMethods = {
|
|
|
136
182
|
// Normal completion of the try block.
|
|
137
183
|
this.startBlock(tryLabel);
|
|
138
184
|
this.current.tryFrames.push(frameSlot);
|
|
185
|
+
if (finallyContext) this.current.finallyStack.push(finallyContext);
|
|
139
186
|
this.emitStatements(statement.tryBlock.statements);
|
|
187
|
+
if (finallyContext) this.current.finallyStack.pop();
|
|
140
188
|
this.current.tryFrames.pop();
|
|
141
189
|
if (!this.current.terminated) {
|
|
142
190
|
const currentFrame = this.reg();
|
|
@@ -153,7 +201,9 @@ export const controlFlowStatementMethods: ControlFlowStatementMethods = {
|
|
|
153
201
|
this.emit(` ${exception} = call i64 @xt_try_exception(i8* ${currentFrame})`);
|
|
154
202
|
this.emit(` call void @xt_try_leave(i8* ${currentFrame})`);
|
|
155
203
|
this.bindCatchVariable(statement.catchClause!.variable, exception);
|
|
204
|
+
if (finallyContext) this.current.finallyStack.push(finallyContext);
|
|
156
205
|
this.emitStatements(statement.catchClause!.block.statements);
|
|
206
|
+
if (finallyContext) this.current.finallyStack.pop();
|
|
157
207
|
if (!this.current.terminated) this.terminate(`br label %${finallyLabel ?? endLabel}`);
|
|
158
208
|
} else {
|
|
159
209
|
// Without a catch clause the exception is remembered, then rethrown
|
|
@@ -209,6 +259,8 @@ export const controlFlowStatementMethods: ControlFlowStatementMethods = {
|
|
|
209
259
|
emitReturn(statement: ReturnStatement): void {
|
|
210
260
|
let value = statement.expression ? this.emitExpression(statement.expression) : i64(XT_UNDEFINED);
|
|
211
261
|
if (this.current.fn.isAsync) value = this.wrapAsync(value);
|
|
262
|
+
this.runFinallysBeforeExit(0);
|
|
263
|
+
if (this.current.terminated) return;
|
|
212
264
|
this.popTryFramesTo(0);
|
|
213
265
|
this.terminate(`ret i64 ${value}`);
|
|
214
266
|
},
|
|
@@ -222,4 +274,38 @@ export const controlFlowStatementMethods: ControlFlowStatementMethods = {
|
|
|
222
274
|
this.emit(` call void @xt_try_leave(i8* ${frame})`);
|
|
223
275
|
}
|
|
224
276
|
},
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Run the `finally` blocks of every region whose frame is above
|
|
280
|
+
* `targetTryDepth`, innermost first, so an abrupt completion
|
|
281
|
+
* (`return`/`break`/`continue`) observes JavaScript's guarantee that each
|
|
282
|
+
* `finally` runs before control leaves its `try`.
|
|
283
|
+
*
|
|
284
|
+
* The blocks are emitted inline on the exiting path (the shared `finally`
|
|
285
|
+
* block still serves normal and exceptional completion). While a block runs,
|
|
286
|
+
* only the *outer* regions stay on {@link FunctionState.finallyStack}, so a
|
|
287
|
+
* `return` written inside `finally` overrides the pending completion without
|
|
288
|
+
* re-entering the same block. The stack is restored afterwards because the
|
|
289
|
+
* emission is path-local: sibling branches must still see every region.
|
|
290
|
+
*/
|
|
291
|
+
runFinallysBeforeExit(targetTryDepth: number): void {
|
|
292
|
+
const saved = this.current.finallyStack.slice();
|
|
293
|
+
const restore = (): void => {
|
|
294
|
+
this.current.finallyStack.length = 0;
|
|
295
|
+
this.current.finallyStack.push(...saved);
|
|
296
|
+
};
|
|
297
|
+
for (let index = saved.length - 1; index >= 0; index--) {
|
|
298
|
+
const context = saved[index]!;
|
|
299
|
+
if (context.frameDepth < targetTryDepth) break;
|
|
300
|
+
this.current.finallyStack.length = 0;
|
|
301
|
+
this.current.finallyStack.push(...saved.slice(0, index));
|
|
302
|
+
this.popTryFramesTo(context.frameDepth);
|
|
303
|
+
this.emitStatements(context.block.statements);
|
|
304
|
+
if (this.current.terminated) {
|
|
305
|
+
restore();
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
restore();
|
|
310
|
+
},
|
|
225
311
|
};
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
import {
|
|
8
8
|
SyntaxKind,
|
|
9
9
|
type Block,
|
|
10
|
+
type BreakStatement,
|
|
11
|
+
type ContinueStatement,
|
|
10
12
|
type DoStatement,
|
|
11
13
|
type EnumDeclaration,
|
|
12
14
|
type Expression,
|
|
@@ -14,6 +16,7 @@ import {
|
|
|
14
16
|
type ForOfStatement,
|
|
15
17
|
type ForStatement,
|
|
16
18
|
type IfStatement,
|
|
19
|
+
type LabeledStatement,
|
|
17
20
|
type ReturnStatement,
|
|
18
21
|
type Statement,
|
|
19
22
|
type SwitchStatement,
|
|
@@ -63,6 +66,9 @@ export const statementDispatchMethods: StatementDispatchMethods = {
|
|
|
63
66
|
case SyntaxKind.IfStatement:
|
|
64
67
|
this.emitIf(statement as IfStatement);
|
|
65
68
|
return;
|
|
69
|
+
case SyntaxKind.LabeledStatement:
|
|
70
|
+
this.emitLabeled(statement as LabeledStatement);
|
|
71
|
+
return;
|
|
66
72
|
case SyntaxKind.WhileStatement:
|
|
67
73
|
this.emitWhile(statement as WhileStatement);
|
|
68
74
|
return;
|
|
@@ -86,18 +92,24 @@ export const statementDispatchMethods: StatementDispatchMethods = {
|
|
|
86
92
|
this.emitReturn(statement as ReturnStatement);
|
|
87
93
|
return;
|
|
88
94
|
case SyntaxKind.BreakStatement: {
|
|
89
|
-
const loop = this.
|
|
95
|
+
const loop = this.findLoop((statement as BreakStatement).label?.text);
|
|
90
96
|
if (loop) {
|
|
91
|
-
this.
|
|
92
|
-
this.
|
|
97
|
+
this.runFinallysBeforeExit(loop.tryDepth ?? 0);
|
|
98
|
+
if (!this.current.terminated) {
|
|
99
|
+
this.popTryFramesTo(loop.tryDepth ?? 0);
|
|
100
|
+
this.terminate(`br label %${loop.breakLabel}`);
|
|
101
|
+
}
|
|
93
102
|
}
|
|
94
103
|
return;
|
|
95
104
|
}
|
|
96
105
|
case SyntaxKind.ContinueStatement: {
|
|
97
|
-
const loop = this.
|
|
106
|
+
const loop = this.findLoop((statement as ContinueStatement).label?.text);
|
|
98
107
|
if (loop) {
|
|
99
|
-
this.
|
|
100
|
-
this.
|
|
108
|
+
this.runFinallysBeforeExit(loop.tryDepth ?? 0);
|
|
109
|
+
if (!this.current.terminated) {
|
|
110
|
+
this.popTryFramesTo(loop.tryDepth ?? 0);
|
|
111
|
+
this.terminate(`br label %${loop.continueLabel}`);
|
|
112
|
+
}
|
|
101
113
|
}
|
|
102
114
|
return;
|
|
103
115
|
}
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
} from "../../../ast/nodes.js";
|
|
15
15
|
import { numberLiteral } from "../../values.js";
|
|
16
16
|
import type { Generator } from "../generator.js";
|
|
17
|
+
import type { LoopLabels } from "../state.js";
|
|
17
18
|
|
|
18
19
|
export interface LoopStatementMethods {
|
|
19
20
|
emitWhile(this: Generator, statement: WhileStatement): void;
|
|
@@ -21,9 +22,25 @@ export interface LoopStatementMethods {
|
|
|
21
22
|
emitFor(this: Generator, statement: ForStatement): void;
|
|
22
23
|
emitForOf(this: Generator, statement: ForOfStatement | ForInStatement): void;
|
|
23
24
|
bindLoopVariable(this: Generator, initializer: VariableDeclarationList | Expression, value: string): void;
|
|
25
|
+
takePendingLabels(this: Generator): string[];
|
|
26
|
+
findLoop(this: Generator, label: string | undefined): LoopLabels | undefined;
|
|
24
27
|
}
|
|
25
28
|
|
|
26
29
|
export const loopStatementMethods: LoopStatementMethods = {
|
|
30
|
+
/** Consume labels accumulated by enclosing `LabeledStatement`s. */
|
|
31
|
+
takePendingLabels(): string[] {
|
|
32
|
+
return this.current.pendingLabels.splice(0, this.current.pendingLabels.length);
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
/** Innermost loop, or the innermost loop carrying `label`. */
|
|
36
|
+
findLoop(label: string | undefined) {
|
|
37
|
+
for (let i = this.current.loops.length - 1; i >= 0; i--) {
|
|
38
|
+
const loop = this.current.loops[i]!;
|
|
39
|
+
if (!label || loop.labels?.includes(label)) return loop;
|
|
40
|
+
}
|
|
41
|
+
return undefined;
|
|
42
|
+
},
|
|
43
|
+
|
|
27
44
|
emitWhile(statement: WhileStatement): void {
|
|
28
45
|
const condLabel = this.label("while.cond");
|
|
29
46
|
const bodyLabel = this.label("while.body");
|
|
@@ -37,7 +54,7 @@ export const loopStatementMethods: LoopStatementMethods = {
|
|
|
37
54
|
this.emit(` ${nonzero} = icmp ne i32 ${truthy}, 0`);
|
|
38
55
|
this.terminate(`br i1 ${nonzero}, label %${bodyLabel}, label %${endLabel}`);
|
|
39
56
|
this.startBlock(bodyLabel);
|
|
40
|
-
this.current.loops.push({ breakLabel: endLabel, continueLabel: condLabel, tryDepth: this.current.tryFrames.length });
|
|
57
|
+
this.current.loops.push({ breakLabel: endLabel, continueLabel: condLabel, tryDepth: this.current.tryFrames.length, labels: this.takePendingLabels() });
|
|
41
58
|
this.emitStatement(statement.statement);
|
|
42
59
|
this.current.loops.pop();
|
|
43
60
|
if (!this.current.terminated) this.terminate(`br label %${condLabel}`);
|
|
@@ -50,7 +67,7 @@ export const loopStatementMethods: LoopStatementMethods = {
|
|
|
50
67
|
const endLabel = this.label("do.end");
|
|
51
68
|
this.terminate(`br label %${bodyLabel}`);
|
|
52
69
|
this.startBlock(bodyLabel);
|
|
53
|
-
this.current.loops.push({ breakLabel: endLabel, continueLabel: condLabel, tryDepth: this.current.tryFrames.length });
|
|
70
|
+
this.current.loops.push({ breakLabel: endLabel, continueLabel: condLabel, tryDepth: this.current.tryFrames.length, labels: this.takePendingLabels() });
|
|
54
71
|
this.emitStatement(statement.statement);
|
|
55
72
|
this.current.loops.pop();
|
|
56
73
|
if (!this.current.terminated) this.terminate(`br label %${condLabel}`);
|
|
@@ -96,7 +113,7 @@ export const loopStatementMethods: LoopStatementMethods = {
|
|
|
96
113
|
}
|
|
97
114
|
|
|
98
115
|
this.startBlock(bodyLabel);
|
|
99
|
-
this.current.loops.push({ breakLabel: endLabel, continueLabel: updateLabel, tryDepth: this.current.tryFrames.length });
|
|
116
|
+
this.current.loops.push({ breakLabel: endLabel, continueLabel: updateLabel, tryDepth: this.current.tryFrames.length, labels: this.takePendingLabels() });
|
|
100
117
|
this.emitStatement(statement.statement);
|
|
101
118
|
this.current.loops.pop();
|
|
102
119
|
if (!this.current.terminated) this.terminate(`br label %${updateLabel}`);
|
|
@@ -112,12 +129,12 @@ export const loopStatementMethods: LoopStatementMethods = {
|
|
|
112
129
|
const source = this.emitExpression(statement.expression);
|
|
113
130
|
const isForIn = statement.kind === SyntaxKind.ForInStatement;
|
|
114
131
|
// `for...in` iterates the enumerable keys (indices become strings);
|
|
115
|
-
// `for...of`
|
|
116
|
-
const iterable = isForIn
|
|
132
|
+
// `for...of` resolves the iteration protocol once, then walks it.
|
|
133
|
+
const iterable = isForIn
|
|
134
|
+
? this.runtimeCall("xt_object_keys", [source])
|
|
135
|
+
: this.runtimeCall("xt_iter_open", [source]);
|
|
117
136
|
const indexPtr = this.alloca();
|
|
118
137
|
this.emit(` store i64 ${numberLiteral(0)}, i64* ${indexPtr}`);
|
|
119
|
-
const lengthValue = this.reg();
|
|
120
|
-
this.emit(` ${lengthValue} = call i64 @xt_iter_length(i64 ${iterable})`);
|
|
121
138
|
|
|
122
139
|
const condLabel = this.label("forof.cond");
|
|
123
140
|
const bodyLabel = this.label("forof.body");
|
|
@@ -128,10 +145,11 @@ export const loopStatementMethods: LoopStatementMethods = {
|
|
|
128
145
|
this.startBlock(condLabel);
|
|
129
146
|
const index = this.reg();
|
|
130
147
|
this.emit(` ${index} = load i64, i64* ${indexPtr}`);
|
|
131
|
-
|
|
132
|
-
|
|
148
|
+
/* `xt_iter_has` covers generators too, which cannot be sized up front. */
|
|
149
|
+
const hasValue = this.reg();
|
|
150
|
+
this.emit(` ${hasValue} = call i64 @xt_iter_has(i64 ${iterable}, i64 ${index})`);
|
|
133
151
|
const truthy = this.reg();
|
|
134
|
-
this.emit(` ${truthy} = call i32 @xt_truthy(i64 ${
|
|
152
|
+
this.emit(` ${truthy} = call i32 @xt_truthy(i64 ${hasValue})`);
|
|
135
153
|
const nonzero = this.reg();
|
|
136
154
|
this.emit(` ${nonzero} = icmp ne i32 ${truthy}, 0`);
|
|
137
155
|
this.terminate(`br i1 ${nonzero}, label %${bodyLabel}, label %${endLabel}`);
|
|
@@ -140,7 +158,7 @@ export const loopStatementMethods: LoopStatementMethods = {
|
|
|
140
158
|
const element = this.reg();
|
|
141
159
|
this.emit(` ${element} = call i64 @xt_iter_value(i64 ${iterable}, i64 ${index})`);
|
|
142
160
|
this.bindLoopVariable(statement.initializer, element);
|
|
143
|
-
this.current.loops.push({ breakLabel: endLabel, continueLabel: updateLabel, tryDepth: this.current.tryFrames.length });
|
|
161
|
+
this.current.loops.push({ breakLabel: endLabel, continueLabel: updateLabel, tryDepth: this.current.tryFrames.length, labels: this.takePendingLabels() });
|
|
144
162
|
this.emitStatement(statement.statement);
|
|
145
163
|
this.current.loops.pop();
|
|
146
164
|
if (!this.current.terminated) this.terminate(`br label %${updateLabel}`);
|
|
@@ -69,15 +69,51 @@ export const MATH_CONSTANTS: Record<string, number> = {
|
|
|
69
69
|
SQRT1_2: Math.SQRT1_2,
|
|
70
70
|
};
|
|
71
71
|
|
|
72
|
+
/** Read-only numeric constants exposed on the global `Number` namespace. */
|
|
73
|
+
export const NUMBER_CONSTANTS = new Set<string>([
|
|
74
|
+
"MAX_SAFE_INTEGER",
|
|
75
|
+
"MIN_SAFE_INTEGER",
|
|
76
|
+
"MAX_VALUE",
|
|
77
|
+
"MIN_VALUE",
|
|
78
|
+
"EPSILON",
|
|
79
|
+
"POSITIVE_INFINITY",
|
|
80
|
+
"NEGATIVE_INFINITY",
|
|
81
|
+
"NaN",
|
|
82
|
+
]);
|
|
83
|
+
|
|
84
|
+
/** The Error family. Each name is a first-class constructor: usable with `new`,
|
|
85
|
+
* as a value (`instanceof`, `typeof`) and as a superclass (`extends TypeError`).
|
|
86
|
+
* All map to runtime constructors with signature `(i32, i64*)`. */
|
|
87
|
+
export const ERROR_CONSTRUCTORS: Record<string, string> = {
|
|
88
|
+
Error: "xt_error_ctor",
|
|
89
|
+
TypeError: "xt_type_error_ctor",
|
|
90
|
+
RangeError: "xt_range_error_ctor",
|
|
91
|
+
SyntaxError: "xt_syntax_error_ctor",
|
|
92
|
+
ReferenceError: "xt_reference_error_ctor",
|
|
93
|
+
EvalError: "xt_eval_error_ctor",
|
|
94
|
+
URIError: "xt_uri_error_ctor",
|
|
95
|
+
AggregateError: "xt_aggregate_error_ctor",
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export function isErrorFamily(name: string): boolean {
|
|
99
|
+
return ERROR_CONSTRUCTORS[name] !== undefined;
|
|
100
|
+
}
|
|
101
|
+
|
|
72
102
|
export const GLOBAL_FUNCTIONS: Record<string, string> = {
|
|
73
103
|
parseInt: "xt_parse_int",
|
|
74
104
|
parseFloat: "xt_parse_float",
|
|
75
105
|
isNaN: "xt_is_nan",
|
|
76
106
|
isFinite: "xt_is_finite",
|
|
107
|
+
encodeURIComponent: "xt_encode_uri_component",
|
|
108
|
+
encodeURI: "xt_encode_uri",
|
|
109
|
+
decodeURIComponent: "xt_decode_uri_component",
|
|
110
|
+
decodeURI: "xt_decode_uri",
|
|
77
111
|
Number: "xt_number_ctor",
|
|
78
112
|
String: "xt_string_ctor",
|
|
79
113
|
Boolean: "xt_boolean_ctor",
|
|
80
114
|
BigInt: "xt_bigint_ctor",
|
|
115
|
+
Symbol: "xt_symbol",
|
|
116
|
+
...ERROR_CONSTRUCTORS,
|
|
81
117
|
};
|
|
82
118
|
|
|
83
119
|
/** Global functions that are also usable as first-class values, each mapped to
|
|
@@ -87,10 +123,15 @@ export const BUILTIN_FUNCTION_VALUES: Record<string, string> = {
|
|
|
87
123
|
parseFloat: "xt_builtin_value_parseFloat",
|
|
88
124
|
isNaN: "xt_builtin_value_isNaN",
|
|
89
125
|
isFinite: "xt_builtin_value_isFinite",
|
|
126
|
+
encodeURIComponent: "xt_builtin_value_encodeURIComponent",
|
|
127
|
+
encodeURI: "xt_builtin_value_encodeURI",
|
|
128
|
+
decodeURIComponent: "xt_builtin_value_decodeURIComponent",
|
|
129
|
+
decodeURI: "xt_builtin_value_decodeURI",
|
|
90
130
|
Number: "xt_builtin_value_number",
|
|
91
131
|
String: "xt_builtin_value_string",
|
|
92
132
|
Boolean: "xt_builtin_value_boolean",
|
|
93
133
|
BigInt: "xt_builtin_value_bigint",
|
|
134
|
+
Symbol: "xt_builtin_value_symbol",
|
|
94
135
|
};
|
|
95
136
|
|
|
96
137
|
/** Global namespaces whose static methods map to runtime dispatchers. */
|
|
@@ -102,6 +143,7 @@ export const NAMESPACE_STATICS: Record<string, string> = {
|
|
|
102
143
|
Number: "xt_number_static",
|
|
103
144
|
String: "xt_string_static",
|
|
104
145
|
BigInt: "xt_bigint_static",
|
|
146
|
+
Symbol: "xt_symbol_static",
|
|
105
147
|
Date: "xt_date_static",
|
|
106
148
|
Promise: "xt_promise_static",
|
|
107
149
|
path: "xt_path_static",
|
|
@@ -126,6 +168,7 @@ export const NAMESPACE_STATICS: Record<string, string> = {
|
|
|
126
168
|
export const NAMESPACE_PROPERTIES: Record<string, string> = {
|
|
127
169
|
process: "xt_process_get",
|
|
128
170
|
"import.meta": "xt_import_meta",
|
|
171
|
+
Symbol: "xt_symbol_get",
|
|
129
172
|
};
|
|
130
173
|
|
|
131
174
|
/** Global constructors called as `new X(...)` (all have signature `(i32, i64*)`). */
|
|
@@ -135,7 +178,6 @@ export const CTOR_FUNCTIONS: Record<string, string> = {
|
|
|
135
178
|
Set: "xt_set_ctor",
|
|
136
179
|
Date: "xt_date_ctor",
|
|
137
180
|
RegExp: "xt_regexp_ctor",
|
|
138
|
-
Error: "xt_error_ctor",
|
|
139
181
|
Promise: "xt_promise_ctor",
|
|
140
182
|
Buffer: "xt_buffer_ctor",
|
|
141
183
|
Readable: "xt_readable_ctor",
|
|
@@ -146,6 +188,7 @@ export const CTOR_FUNCTIONS: Record<string, string> = {
|
|
|
146
188
|
Socket: "xt_net_socket_ctor",
|
|
147
189
|
Server: "xt_net_server_ctor",
|
|
148
190
|
EventEmitter: "xt_event_emitter_ctor",
|
|
191
|
+
...ERROR_CONSTRUCTORS,
|
|
149
192
|
};
|
|
150
193
|
|
|
151
194
|
export const BUILTIN_METHODS = new Set<string>([
|
package/src/driver/compiler.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
11
|
-
import { basename,
|
|
11
|
+
import { basename, extname, join, resolve } from "node:path";
|
|
12
12
|
import { DiagnosticBag, type Diagnostic } from "../diagnostics/diagnostic.js";
|
|
13
13
|
import { SourceFile } from "../diagnostics/source.js";
|
|
14
14
|
import { Parser } from "../parser/parser.js";
|
|
@@ -153,6 +153,7 @@ export function build(entryPath: string, options: BuildOptions = {}): BuildResul
|
|
|
153
153
|
}
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
+
const runtimeDir = findRuntimeDir();
|
|
156
157
|
const cacheKey = hashParts([
|
|
157
158
|
COMPILER_VERSION,
|
|
158
159
|
cacheText,
|
|
@@ -160,6 +161,7 @@ export function build(entryPath: string, options: BuildOptions = {}): BuildResul
|
|
|
160
161
|
optimize,
|
|
161
162
|
process.platform,
|
|
162
163
|
registry.all().map((e) => e.name).join(","),
|
|
164
|
+
runtimeFingerprint(runtimeDir),
|
|
163
165
|
]);
|
|
164
166
|
|
|
165
167
|
const cache = new BuildCache(cacheDir);
|
|
@@ -207,7 +209,6 @@ export function build(entryPath: string, options: BuildOptions = {}): BuildResul
|
|
|
207
209
|
return { outputPath, irPath, cached: false, diagnostics: [], ir };
|
|
208
210
|
}
|
|
209
211
|
|
|
210
|
-
const runtimeDir = findRuntimeDir();
|
|
211
212
|
const { runtimeObjects, extensionObjects } = ensureRuntimeObjects(
|
|
212
213
|
runner,
|
|
213
214
|
clang,
|
|
@@ -251,10 +252,39 @@ export const RUNTIME_SOURCES = [
|
|
|
251
252
|
"xt_stdlib2.c",
|
|
252
253
|
"xt_promise.c",
|
|
253
254
|
"xt_loop.c",
|
|
255
|
+
"xt_generator.c",
|
|
256
|
+
"xt_symbol.c",
|
|
254
257
|
"xt_builtins.c",
|
|
255
258
|
"xt_io.c",
|
|
256
259
|
] as const;
|
|
257
260
|
|
|
261
|
+
/**
|
|
262
|
+
* Fingerprint every runtime artifact that can change a linked executable: the
|
|
263
|
+
* C sources, shared headers and `#include`d `.inc` fragments plus the shipped
|
|
264
|
+
* prebuilt archives. It feeds the executable cache key, so a runtime change
|
|
265
|
+
* invalidates a cached binary even when the entry source is untouched — the
|
|
266
|
+
* per-object cache cannot express that, because the freshness check runs before
|
|
267
|
+
* any compilation.
|
|
268
|
+
*/
|
|
269
|
+
function runtimeFingerprint(runtimeDir: string): string {
|
|
270
|
+
if (!existsSync(runtimeDir)) return "";
|
|
271
|
+
const parts: string[] = [];
|
|
272
|
+
const walk = (dir: string): void => {
|
|
273
|
+
for (const entry of readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
274
|
+
const full = join(dir, entry.name);
|
|
275
|
+
if (entry.isDirectory()) {
|
|
276
|
+
walk(full);
|
|
277
|
+
} else if (/\.(c|h|inc)$/.test(entry.name)) {
|
|
278
|
+
parts.push(entry.name, readFileSync(full, "utf8"));
|
|
279
|
+
} else {
|
|
280
|
+
parts.push(entry.name, readFileSync(full).toString("base64"));
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
walk(runtimeDir);
|
|
285
|
+
return hashParts(parts);
|
|
286
|
+
}
|
|
287
|
+
|
|
258
288
|
/**
|
|
259
289
|
* Compile the core runtime and every extension source, reusing cached object
|
|
260
290
|
* files keyed on the C source hash. Returns the object paths to link.
|