xbintsc 0.1.8 → 0.1.9
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/binder/binder.d.ts +35 -1
- package/dist/src/binder/binder.js +138 -0
- package/dist/src/binder/binder.js.map +1 -1
- package/dist/src/codegen/generator/calls.d.ts +4 -0
- package/dist/src/codegen/generator/calls.js +104 -32
- package/dist/src/codegen/generator/calls.js.map +1 -1
- package/dist/src/codegen/generator/context.d.ts +2 -0
- package/dist/src/codegen/generator/context.js +36 -0
- package/dist/src/codegen/generator/context.js.map +1 -1
- package/dist/src/codegen/generator/expressions.d.ts +4 -1
- package/dist/src/codegen/generator/expressions.js +73 -2
- package/dist/src/codegen/generator/expressions.js.map +1 -1
- package/dist/src/codegen/generator/module.d.ts +5 -1
- package/dist/src/codegen/generator/module.js +106 -6
- package/dist/src/codegen/generator/module.js.map +1 -1
- package/dist/src/codegen/generator/state.d.ts +2 -0
- package/dist/src/codegen/generator/state.js +41 -0
- package/dist/src/codegen/generator/state.js.map +1 -1
- package/dist/src/codegen/generator/statements.js +9 -1
- package/dist/src/codegen/generator/statements.js.map +1 -1
- package/dist/src/codegen/generator/tables.d.ts +4 -0
- package/dist/src/codegen/generator/tables.js +33 -0
- package/dist/src/codegen/generator/tables.js.map +1 -1
- package/dist/src/driver/compiler.js +23 -7
- package/dist/src/driver/compiler.js.map +1 -1
- package/dist/src/driver/modules.d.ts +24 -0
- package/dist/src/driver/modules.js +347 -0
- package/dist/src/driver/modules.js.map +1 -0
- package/dist/src/parser/expressions.js +3 -0
- package/dist/src/parser/expressions.js.map +1 -1
- package/dist/tests/codegen/llvm.test.js +1 -1
- package/dist/tests/codegen/llvm.test.js.map +1 -1
- package/dist/tests/e2e/compile.test.js +86 -0
- package/dist/tests/e2e/compile.test.js.map +1 -1
- package/package.json +1 -1
- package/runtime/rt.h +55 -4
- package/runtime/rt_internal.h +63 -0
- package/runtime/xt_builtins.c +47 -1
- package/runtime/xt_containers.c +95 -8
- package/runtime/xt_io.c +113 -0
- package/runtime/xt_promise.c +292 -0
- package/runtime/xt_stdlib.c +59 -5
- package/runtime/xt_stdlib2.c +1043 -0
- package/src/binder/binder.ts +196 -0
- package/src/codegen/generator/calls.ts +111 -33
- package/src/codegen/generator/context.ts +35 -1
- package/src/codegen/generator/expressions.ts +79 -2
- package/src/codegen/generator/module.ts +111 -6
- package/src/codegen/generator/state.ts +43 -0
- package/src/codegen/generator/statements.ts +7 -1
- package/src/codegen/generator/tables.ts +35 -0
- package/src/driver/compiler.ts +28 -7
- package/src/driver/modules.ts +377 -0
- package/src/parser/expressions.ts +3 -0
|
@@ -7,14 +7,17 @@ import {
|
|
|
7
7
|
SyntaxKind,
|
|
8
8
|
type ArrayLiteralExpression,
|
|
9
9
|
type ArrowFunction,
|
|
10
|
+
type AwaitExpression,
|
|
10
11
|
type BinaryExpression,
|
|
11
12
|
type CallExpression,
|
|
13
|
+
type ClassExpression,
|
|
12
14
|
type ConditionalExpression,
|
|
13
15
|
type DeleteExpression,
|
|
14
16
|
type ElementAccessExpression,
|
|
15
17
|
type Expression,
|
|
16
18
|
type FunctionExpression,
|
|
17
19
|
type Identifier,
|
|
20
|
+
type NewExpression,
|
|
18
21
|
type ObjectLiteralExpression,
|
|
19
22
|
type PostfixUnaryExpression,
|
|
20
23
|
type PrefixUnaryExpression,
|
|
@@ -30,7 +33,7 @@ import {
|
|
|
30
33
|
import { SymbolKind, type SymbolInfo } from "../../binder/binder.js";
|
|
31
34
|
import { DiagnosticCode } from "../../diagnostics/diagnostic.js";
|
|
32
35
|
import { i64, numberLiteral, XT_FALSE, XT_NULL, XT_TRUE, XT_UNDEFINED } from "../values.js";
|
|
33
|
-
import { BINARY_RUNTIME, compoundToBinary, isAssignmentOperator } from "./tables.js";
|
|
36
|
+
import { BINARY_RUNTIME, compoundToBinary, isAssignmentOperator, CTOR_FUNCTIONS } from "./tables.js";
|
|
34
37
|
import type { Generator } from "./generator.js";
|
|
35
38
|
|
|
36
39
|
export interface ExpressionMethods {
|
|
@@ -46,6 +49,9 @@ export interface ExpressionMethods {
|
|
|
46
49
|
emitAssignment(this: Generator, node: BinaryExpression): string;
|
|
47
50
|
emitLogicalAssignment(this: Generator, node: BinaryExpression): string;
|
|
48
51
|
emitAssignmentTarget(this: Generator, target: Expression, value: string): void;
|
|
52
|
+
emitThis(this: Generator): string;
|
|
53
|
+
emitNew(this: Generator, node: NewExpression): string;
|
|
54
|
+
emitAwait(this: Generator, node: AwaitExpression): string;
|
|
49
55
|
}
|
|
50
56
|
|
|
51
57
|
export const expressionMethods: ExpressionMethods = {
|
|
@@ -81,6 +87,21 @@ export const expressionMethods: ExpressionMethods = {
|
|
|
81
87
|
return this.emitExpression((node as { expression: Expression }).expression);
|
|
82
88
|
case SyntaxKind.DeleteExpression:
|
|
83
89
|
return this.emitDelete(node as DeleteExpression);
|
|
90
|
+
case SyntaxKind.ThisKeyword:
|
|
91
|
+
return this.emitThis();
|
|
92
|
+
case SyntaxKind.NewExpression:
|
|
93
|
+
return this.emitNew(node as NewExpression);
|
|
94
|
+
case SyntaxKind.AwaitExpression:
|
|
95
|
+
return this.emitAwait(node as AwaitExpression);
|
|
96
|
+
case SyntaxKind.ClassExpression: {
|
|
97
|
+
const info = this.binding.classOfNode.get(node);
|
|
98
|
+
if (!info) {
|
|
99
|
+
this.unsupported(node, "class expression");
|
|
100
|
+
return i64(XT_UNDEFINED);
|
|
101
|
+
}
|
|
102
|
+
this.emitClassSetup(info);
|
|
103
|
+
return this.emitClassReference(info);
|
|
104
|
+
}
|
|
84
105
|
case SyntaxKind.BinaryExpression:
|
|
85
106
|
return this.emitBinaryOrAssignment(node as BinaryExpression);
|
|
86
107
|
case SyntaxKind.PrefixUnaryExpression:
|
|
@@ -111,6 +132,11 @@ export const expressionMethods: ExpressionMethods = {
|
|
|
111
132
|
emitIdentifier(identifier: Identifier): string {
|
|
112
133
|
const symbol = this.binding.symbolOfIdentifier.get(identifier);
|
|
113
134
|
if (symbol) {
|
|
135
|
+
if (symbol.kind === SymbolKind.Class) {
|
|
136
|
+
const declaration = symbol.declarations[0];
|
|
137
|
+
const info = declaration ? this.binding.classOfNode.get(declaration) : undefined;
|
|
138
|
+
if (info) return this.emitClassReference(info);
|
|
139
|
+
}
|
|
114
140
|
if (symbol.kind === SymbolKind.Function && !this.current.slots.has(symbol.id)) {
|
|
115
141
|
return this.emitFunctionValue(symbol);
|
|
116
142
|
}
|
|
@@ -150,12 +176,63 @@ export const expressionMethods: ExpressionMethods = {
|
|
|
150
176
|
const fn = declaration ? this.binding.functionOfNode.get(declaration) : undefined;
|
|
151
177
|
if (!fn) return i64(XT_UNDEFINED);
|
|
152
178
|
const cast = this.reg();
|
|
153
|
-
this.emit(` ${cast} = bitcast i64 (i64, i32, i64*)* @${this.functionName(fn)} to i8*`);
|
|
179
|
+
this.emit(` ${cast} = bitcast i64 (i64, i64, i32, i64*)* @${this.functionName(fn)} to i8*`);
|
|
154
180
|
const closure = this.reg();
|
|
155
181
|
this.emit(` ${closure} = call i64 @xt_closure_new(i8* ${cast}, i32 0, i64* null)`);
|
|
156
182
|
return closure;
|
|
157
183
|
},
|
|
158
184
|
|
|
185
|
+
emitThis(): string {
|
|
186
|
+
if (!this.current.thisPtr) return i64(XT_UNDEFINED);
|
|
187
|
+
const value = this.reg();
|
|
188
|
+
this.emit(` ${value} = load i64, i64* ${this.current.thisPtr}`);
|
|
189
|
+
return value;
|
|
190
|
+
},
|
|
191
|
+
|
|
192
|
+
emitAwait(node: AwaitExpression): string {
|
|
193
|
+
const value = this.emitExpression(node.expression);
|
|
194
|
+
const result = this.reg();
|
|
195
|
+
this.emit(` ${result} = call i64 @xt_await(i64 ${value})`);
|
|
196
|
+
return result;
|
|
197
|
+
},
|
|
198
|
+
|
|
199
|
+
emitNew(node: NewExpression): string {
|
|
200
|
+
const callee = node.expression;
|
|
201
|
+
if (callee.kind === SyntaxKind.Identifier) {
|
|
202
|
+
const identifier = callee as Identifier;
|
|
203
|
+
const symbol = this.binding.symbolOfIdentifier.get(identifier);
|
|
204
|
+
if (symbol && symbol.kind === SymbolKind.Class) {
|
|
205
|
+
const declaration = symbol.declarations[0];
|
|
206
|
+
const info = declaration ? this.binding.classOfNode.get(declaration) : undefined;
|
|
207
|
+
if (info) {
|
|
208
|
+
const ctor = this.emitClassReference(info);
|
|
209
|
+
const args = this.emitArguments(node.arguments);
|
|
210
|
+
const result = this.reg();
|
|
211
|
+
this.emit(` ${result} = call i64 @xt_new(i64 ${ctor}, i32 ${args.argc}, i64* ${args.ptr})`);
|
|
212
|
+
return result;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
if (!symbol) {
|
|
216
|
+
const ctor = CTOR_FUNCTIONS[identifier.text];
|
|
217
|
+
if (ctor) {
|
|
218
|
+
const args = this.emitArguments(node.arguments);
|
|
219
|
+
const result = this.reg();
|
|
220
|
+
this.emit(` ${result} = call i64 @${ctor}(i32 ${args.argc}, i64* ${args.ptr})`);
|
|
221
|
+
return result;
|
|
222
|
+
}
|
|
223
|
+
if (identifier.text === "Object") {
|
|
224
|
+
if (node.arguments.length > 0) return this.emitExpression(node.arguments[0]!);
|
|
225
|
+
return this.runtimeCall("xt_object_new", []);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
const ctorValue = this.emitExpression(callee);
|
|
230
|
+
const args = this.emitArguments(node.arguments);
|
|
231
|
+
const result = this.reg();
|
|
232
|
+
this.emit(` ${result} = call i64 @xt_new(i64 ${ctorValue}, i32 ${args.argc}, i64* ${args.ptr})`);
|
|
233
|
+
return result;
|
|
234
|
+
},
|
|
235
|
+
|
|
159
236
|
emitTemplate(node: TemplateLiteral): string {
|
|
160
237
|
let accumulator = this.stringValue(node.head);
|
|
161
238
|
for (const span of node.spans) {
|
|
@@ -10,7 +10,8 @@ import {
|
|
|
10
10
|
type Expression,
|
|
11
11
|
type Parameter,
|
|
12
12
|
} from "../../ast/nodes.js";
|
|
13
|
-
import type { FunctionInfo, SymbolInfo } from "../../binder/binder.js";
|
|
13
|
+
import type { ClassInfo, FunctionInfo, SymbolInfo } from "../../binder/binder.js";
|
|
14
|
+
import { SymbolKind } from "../../binder/binder.js";
|
|
14
15
|
import { i64, XT_UNDEFINED } from "../values.js";
|
|
15
16
|
import { RUNTIME_DECLARATIONS, type FunctionState } from "./state.js";
|
|
16
17
|
import type { Generator } from "./generator.js";
|
|
@@ -20,11 +21,32 @@ export interface ModuleMethods {
|
|
|
20
21
|
emitFunction(this: Generator, fn: FunctionInfo): void;
|
|
21
22
|
emitMain(this: Generator): void;
|
|
22
23
|
functionName(this: Generator, fn: FunctionInfo): string;
|
|
24
|
+
wrapAsync(this: Generator, value: string): string;
|
|
25
|
+
classGlobal(this: Generator, classInfo: ClassInfo): string;
|
|
26
|
+
emitClassReference(this: Generator, classInfo: ClassInfo): string;
|
|
27
|
+
emitClassSetup(this: Generator, classInfo: ClassInfo): void;
|
|
23
28
|
emitDefaultParameter(this: Generator, symbol: SymbolInfo, value: string, initializer: Expression): void;
|
|
24
29
|
}
|
|
25
30
|
|
|
26
31
|
export const moduleMethods: ModuleMethods = {
|
|
27
32
|
run(): string {
|
|
33
|
+
for (const classInfo of this.binding.classes) {
|
|
34
|
+
const globalName = `@class.${classInfo.id}`;
|
|
35
|
+
this.classGlobals.set(classInfo.id, globalName);
|
|
36
|
+
this.globals.push(`${globalName} = internal global i64 0`);
|
|
37
|
+
}
|
|
38
|
+
// Module-level bindings captured by functions live in globals so that a
|
|
39
|
+
// top-level function can see them without a closure environment.
|
|
40
|
+
const moduleScope = this.binding.scopes.get(this.sourceFile);
|
|
41
|
+
if (moduleScope) {
|
|
42
|
+
for (const symbol of moduleScope.symbols.values()) {
|
|
43
|
+
const isVariable = symbol.kind === SymbolKind.Var || symbol.kind === SymbolKind.Let || symbol.kind === SymbolKind.Const;
|
|
44
|
+
if (!isVariable || !symbol.boxed || !symbol.fn.isModule) continue;
|
|
45
|
+
const globalName = `@g.${symbol.id}`;
|
|
46
|
+
this.moduleGlobals.set(symbol.id, globalName);
|
|
47
|
+
this.globals.push(`${globalName} = internal global i64 ${i64(XT_UNDEFINED)}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
28
50
|
for (const fn of this.binding.functions) this.emitFunction(fn);
|
|
29
51
|
this.emitMain();
|
|
30
52
|
const header = ["; ModuleID = 'xbintsc'", "source_filename = \"" + this.sourceFile.fileName + "\"", ""];
|
|
@@ -50,9 +72,12 @@ export const moduleMethods: ModuleMethods = {
|
|
|
50
72
|
};
|
|
51
73
|
this.current = state;
|
|
52
74
|
|
|
53
|
-
const header = `define i64 @${name}(i64 %env, i32 %argc, i64* %argv) {`;
|
|
75
|
+
const header = `define i64 @${name}(i64 %this, i64 %env, i32 %argc, i64* %argv) {`;
|
|
54
76
|
this.emit(`%saved.env = alloca i64`);
|
|
55
77
|
this.emit(`store i64 %env, i64* %saved.env`);
|
|
78
|
+
this.emit(`%saved.this = alloca i64`);
|
|
79
|
+
this.emit(`store i64 %this, i64* %saved.this`);
|
|
80
|
+
state.thisPtr = "%saved.this";
|
|
56
81
|
this.emit(`%saved.argc = alloca i32`);
|
|
57
82
|
this.emit(`store i32 %argc, i32* %saved.argc`);
|
|
58
83
|
this.emit(`%saved.argv = alloca i64*`);
|
|
@@ -77,24 +102,46 @@ export const moduleMethods: ModuleMethods = {
|
|
|
77
102
|
|
|
78
103
|
// Captures threaded through the environment.
|
|
79
104
|
for (const symbol of fn.captures) {
|
|
105
|
+
if (this.moduleGlobals.has(symbol.id)) continue;
|
|
80
106
|
const index = fn.captureIndex.get(symbol.id)!;
|
|
81
107
|
const value = this.reg();
|
|
82
108
|
this.emit(` ${value} = call i64 @xt_closure_env(i64 %env, i32 ${index})`);
|
|
83
109
|
this.defineCaptureSlot(symbol, value);
|
|
84
110
|
}
|
|
85
111
|
|
|
112
|
+
// Arrow functions inherit `this` lexically; it travels as an extra capture.
|
|
113
|
+
if (fn.isArrow && fn.capturesThis) {
|
|
114
|
+
const inherited = this.reg();
|
|
115
|
+
this.emit(` ${inherited} = call i64 @xt_closure_env(i64 %env, i32 ${fn.captures.length})`);
|
|
116
|
+
this.emit(` store i64 ${inherited}, i64* %saved.this`);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Instance field initializers run before the constructor body.
|
|
120
|
+
if (fn.isConstructor && fn.classInfo) {
|
|
121
|
+
const thisValue = this.emitThis();
|
|
122
|
+
for (const field of fn.classInfo.fields) {
|
|
123
|
+
if (field.isStatic) continue;
|
|
124
|
+
const key = this.stringValue(field.name);
|
|
125
|
+
const value = field.initializer ? this.emitExpression(field.initializer) : i64(XT_UNDEFINED);
|
|
126
|
+
this.emit(` call i64 @xt_set(i64 ${thisValue}, i64 ${key}, i64 ${value})`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
86
130
|
// Body.
|
|
87
131
|
if (fn.node.kind === SyntaxKind.SourceFile) {
|
|
88
132
|
this.emitStatements(this.sourceFile.statements);
|
|
89
133
|
} else if (fn.node.kind === SyntaxKind.ArrowFunction && (fn.node as ArrowFunction).body.kind !== SyntaxKind.Block) {
|
|
90
134
|
const value = this.emitExpression((fn.node as ArrowFunction).body as Expression);
|
|
91
|
-
this.terminate(`ret i64 ${value}`);
|
|
135
|
+
this.terminate(`ret i64 ${fn.isAsync ? this.wrapAsync(value) : value}`);
|
|
92
136
|
} else {
|
|
93
137
|
const body = (fn.node as { body: Block }).body;
|
|
94
|
-
this.emitStatements(body.statements);
|
|
138
|
+
if (body) this.emitStatements(body.statements);
|
|
95
139
|
}
|
|
96
140
|
|
|
97
|
-
if (!this.current.terminated)
|
|
141
|
+
if (!this.current.terminated) {
|
|
142
|
+
const fallback = fn.isAsync ? this.wrapAsync(i64(XT_UNDEFINED)) : i64(XT_UNDEFINED);
|
|
143
|
+
this.terminate(`ret i64 ${fallback}`);
|
|
144
|
+
}
|
|
98
145
|
|
|
99
146
|
const escapes = state.usesTry
|
|
100
147
|
? state.escapePointers.map((ptr) => ` call void asm sideeffect "", "r"(i64* ${ptr})`)
|
|
@@ -108,7 +155,8 @@ export const moduleMethods: ModuleMethods = {
|
|
|
108
155
|
this.functions.push(
|
|
109
156
|
[
|
|
110
157
|
"define i32 @main(i32 %argc, i8** %argv) {",
|
|
111
|
-
` %result = call i64 @${moduleName}(i64 ${i64(XT_UNDEFINED)}, i32 0, i64* null)`,
|
|
158
|
+
` %result = call i64 @${moduleName}(i64 ${i64(XT_UNDEFINED)}, i64 ${i64(XT_UNDEFINED)}, i32 0, i64* null)`,
|
|
159
|
+
" call void @xt_drain_microtasks()",
|
|
112
160
|
" ret i32 0",
|
|
113
161
|
"}",
|
|
114
162
|
"",
|
|
@@ -120,6 +168,63 @@ export const moduleMethods: ModuleMethods = {
|
|
|
120
168
|
return fn.isModule ? "xt_module" : `xt_fn_${fn.id}`;
|
|
121
169
|
},
|
|
122
170
|
|
|
171
|
+
/** Wrap a returned value in a resolved promise (for `async` functions). */
|
|
172
|
+
wrapAsync(value: string): string {
|
|
173
|
+
const result = this.reg();
|
|
174
|
+
this.emit(` ${result} = call i64 @xt_promise_resolve(i64 ${value})`);
|
|
175
|
+
return result;
|
|
176
|
+
},
|
|
177
|
+
|
|
178
|
+
classGlobal(classInfo: ClassInfo): string {
|
|
179
|
+
const existing = this.classGlobals.get(classInfo.id);
|
|
180
|
+
if (existing) return existing;
|
|
181
|
+
const name = `@class.${classInfo.id}`;
|
|
182
|
+
this.classGlobals.set(classInfo.id, name);
|
|
183
|
+
this.globals.push(`${name} = internal global i64 0`);
|
|
184
|
+
return name;
|
|
185
|
+
},
|
|
186
|
+
|
|
187
|
+
emitClassReference(classInfo: ClassInfo): string {
|
|
188
|
+
const globalName = this.classGlobal(classInfo);
|
|
189
|
+
const value = this.reg();
|
|
190
|
+
this.emit(` ${value} = load i64, i64* ${globalName}`);
|
|
191
|
+
return value;
|
|
192
|
+
},
|
|
193
|
+
|
|
194
|
+
/** Build a class value (constructor closure + prototype) and store it in its global. */
|
|
195
|
+
emitClassSetup(classInfo: ClassInfo): void {
|
|
196
|
+
const globalName = this.classGlobal(classInfo);
|
|
197
|
+
const proto = this.reg();
|
|
198
|
+
this.emit(` ${proto} = call i64 @xt_object_new()`);
|
|
199
|
+
if (classInfo.parentExpression) {
|
|
200
|
+
const parent = this.emitExpression(classInfo.parentExpression);
|
|
201
|
+
const parentProto = this.reg();
|
|
202
|
+
this.emit(` ${parentProto} = call i64 @xt_function_get_prototype(i64 ${parent})`);
|
|
203
|
+
this.emit(` call i64 @xt_object_set_prototype(i64 ${proto}, i64 ${parentProto})`);
|
|
204
|
+
}
|
|
205
|
+
for (const method of classInfo.methods) {
|
|
206
|
+
const fnValue = this.emitClosureValue(method);
|
|
207
|
+
const key = this.stringValue(method.name);
|
|
208
|
+
this.emit(` call i64 @xt_set(i64 ${proto}, i64 ${key}, i64 ${fnValue})`);
|
|
209
|
+
}
|
|
210
|
+
const ctor = this.emitClosureValue(classInfo.ctor!);
|
|
211
|
+
this.emit(` call i64 @xt_function_set_prototype(i64 ${ctor}, i64 ${proto})`);
|
|
212
|
+
const ctorKey = this.stringValue("__ctor");
|
|
213
|
+
this.emit(` call i64 @xt_set(i64 ${proto}, i64 ${ctorKey}, i64 ${ctor})`);
|
|
214
|
+
for (const method of classInfo.statics) {
|
|
215
|
+
const fnValue = this.emitClosureValue(method);
|
|
216
|
+
const key = this.stringValue(method.name);
|
|
217
|
+
this.emit(` call i64 @xt_set(i64 ${ctor}, i64 ${key}, i64 ${fnValue})`);
|
|
218
|
+
}
|
|
219
|
+
for (const field of classInfo.fields) {
|
|
220
|
+
if (!field.isStatic) continue;
|
|
221
|
+
const key = this.stringValue(field.name);
|
|
222
|
+
const value = field.initializer ? this.emitExpression(field.initializer) : i64(XT_UNDEFINED);
|
|
223
|
+
this.emit(` call i64 @xt_set(i64 ${ctor}, i64 ${key}, i64 ${value})`);
|
|
224
|
+
}
|
|
225
|
+
this.emit(` store i64 ${ctor}, i64* ${globalName}`);
|
|
226
|
+
},
|
|
227
|
+
|
|
123
228
|
emitDefaultParameter(symbol: SymbolInfo, value: string, initializer: Expression): void {
|
|
124
229
|
const isUndefined = this.reg();
|
|
125
230
|
this.emit(` ${isUndefined} = call i64 @xt_seq(i64 ${value}, i64 ${i64(XT_UNDEFINED)})`);
|
|
@@ -30,6 +30,8 @@ export interface FunctionState {
|
|
|
30
30
|
readonly loops: LoopLabels[];
|
|
31
31
|
readonly tryFrames: string[];
|
|
32
32
|
readonly escapePointers: string[];
|
|
33
|
+
/** Alloca holding the `this` binding for the current function. */
|
|
34
|
+
thisPtr?: string;
|
|
33
35
|
/** Set when the function contains a `try`, forcing locals to live in memory. */
|
|
34
36
|
usesTry: boolean;
|
|
35
37
|
}
|
|
@@ -55,6 +57,47 @@ export const RUNTIME_DECLARATIONS: readonly string[] = [
|
|
|
55
57
|
"declare i64 @xt_closure_new(i8*, i32, i64*)",
|
|
56
58
|
"declare i64 @xt_closure_call(i64, i32, i64*)",
|
|
57
59
|
"declare i64 @xt_closure_env(i64, i32)",
|
|
60
|
+
"declare i64 @xt_call_with_this(i64, i64, i32, i64*)",
|
|
61
|
+
"declare i64 @xt_this()",
|
|
62
|
+
"declare i64 @xt_new(i64, i32, i64*)",
|
|
63
|
+
"declare i64 @xt_instance_of(i64, i64)",
|
|
64
|
+
"declare i64 @xt_function_set_prototype(i64, i64)",
|
|
65
|
+
"declare i64 @xt_function_get_prototype(i64)",
|
|
66
|
+
"declare i64 @xt_object_new_with_proto(i64)",
|
|
67
|
+
"declare i64 @xt_object_get_prototype(i64)",
|
|
68
|
+
"declare i64 @xt_object_set_prototype(i64, i64)",
|
|
69
|
+
"declare i64 @xt_await(i64)",
|
|
70
|
+
"declare void @xt_drain_microtasks()",
|
|
71
|
+
"declare i64 @xt_promise_resolve(i64)",
|
|
72
|
+
"declare i64 @xt_promise_reject(i64)",
|
|
73
|
+
"declare i64 @xt_promise_ctor(i32, i64*)",
|
|
74
|
+
"declare i64 @xt_promise_static(i64, i32, i64*)",
|
|
75
|
+
"declare i64 @xt_array_static(i64, i32, i64*)",
|
|
76
|
+
"declare i64 @xt_object_static(i64, i32, i64*)",
|
|
77
|
+
"declare i64 @xt_number_static(i64, i32, i64*)",
|
|
78
|
+
"declare i64 @xt_string_static(i64, i32, i64*)",
|
|
79
|
+
"declare i64 @xt_date_static(i64, i32, i64*)",
|
|
80
|
+
"declare i64 @xt_map_ctor(i32, i64*)",
|
|
81
|
+
"declare i64 @xt_set_ctor(i32, i64*)",
|
|
82
|
+
"declare i64 @xt_date_ctor(i32, i64*)",
|
|
83
|
+
"declare i64 @xt_regexp_ctor(i32, i64*)",
|
|
84
|
+
"declare i64 @xt_json_parse(i32, i64*)",
|
|
85
|
+
"declare i64 @xt_json_stringify(i32, i64*)",
|
|
86
|
+
"declare i64 @xt_object_from_entries(i64)",
|
|
87
|
+
"declare i64 @xt_object_freeze(i64)",
|
|
88
|
+
"declare i32 @xt_object_is_frozen(i64)",
|
|
89
|
+
"declare i64 @xt_object_has_own(i64, i64)",
|
|
90
|
+
"declare i64 @xt_console_dir(i32, i64*)",
|
|
91
|
+
"declare i64 @xt_console_trace(i32, i64*)",
|
|
92
|
+
"declare i64 @xt_console_assert(i32, i64*)",
|
|
93
|
+
"declare i64 @xt_console_count(i32, i64*)",
|
|
94
|
+
"declare i64 @xt_console_count_reset(i32, i64*)",
|
|
95
|
+
"declare i64 @xt_console_group(i32, i64*)",
|
|
96
|
+
"declare i64 @xt_console_group_end(i32, i64*)",
|
|
97
|
+
"declare i64 @xt_console_table(i32, i64*)",
|
|
98
|
+
"declare i64 @xt_console_time(i32, i64*)",
|
|
99
|
+
"declare i64 @xt_console_time_end(i32, i64*)",
|
|
100
|
+
"declare i64 @xt_console_time_log(i32, i64*)",
|
|
58
101
|
"declare i32 @xt_truthy(i64)",
|
|
59
102
|
"declare double @xt_to_number(i64)",
|
|
60
103
|
"declare i64 @xt_to_string(i64)",
|
|
@@ -67,6 +67,11 @@ export const statementMethods: StatementMethods = {
|
|
|
67
67
|
case SyntaxKind.InterfaceDeclaration:
|
|
68
68
|
case SyntaxKind.TypeAliasDeclaration:
|
|
69
69
|
return; // functions are emitted at module scope
|
|
70
|
+
case SyntaxKind.ClassDeclaration: {
|
|
71
|
+
const info = this.binding.classOfNode.get(statement);
|
|
72
|
+
if (info) this.emitClassSetup(info);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
70
75
|
case SyntaxKind.IfStatement:
|
|
71
76
|
this.emitIf(statement as IfStatement);
|
|
72
77
|
return;
|
|
@@ -457,7 +462,8 @@ export const statementMethods: StatementMethods = {
|
|
|
457
462
|
},
|
|
458
463
|
|
|
459
464
|
emitReturn(statement: ReturnStatement): void {
|
|
460
|
-
|
|
465
|
+
let value = statement.expression ? this.emitExpression(statement.expression) : i64(XT_UNDEFINED);
|
|
466
|
+
if (this.current.fn.isAsync) value = this.wrapAsync(value);
|
|
461
467
|
this.popTryFramesTo(0);
|
|
462
468
|
this.terminate(`ret i64 ${value}`);
|
|
463
469
|
},
|
|
@@ -31,6 +31,7 @@ export const BINARY_RUNTIME: Record<string, string | undefined> = {
|
|
|
31
31
|
[BinaryOperator.GreaterThanGreaterThan]: "xt_shr",
|
|
32
32
|
[BinaryOperator.GreaterThanGreaterThanGreaterThan]: "xt_ushr",
|
|
33
33
|
[BinaryOperator.In]: "xt_in",
|
|
34
|
+
[BinaryOperator.InstanceOf]: "xt_instance_of",
|
|
34
35
|
};
|
|
35
36
|
|
|
36
37
|
export const CONSOLE_METHODS: Record<string, string> = {
|
|
@@ -38,11 +39,23 @@ export const CONSOLE_METHODS: Record<string, string> = {
|
|
|
38
39
|
info: "xt_console_info",
|
|
39
40
|
warn: "xt_console_warn",
|
|
40
41
|
error: "xt_console_error",
|
|
42
|
+
dir: "xt_console_dir",
|
|
43
|
+
trace: "xt_console_trace",
|
|
44
|
+
assert: "xt_console_assert",
|
|
45
|
+
count: "xt_console_count",
|
|
46
|
+
countReset: "xt_console_count_reset",
|
|
47
|
+
group: "xt_console_group",
|
|
48
|
+
groupEnd: "xt_console_group_end",
|
|
49
|
+
table: "xt_console_table",
|
|
50
|
+
time: "xt_console_time",
|
|
51
|
+
timeEnd: "xt_console_time_end",
|
|
52
|
+
timeLog: "xt_console_time_log",
|
|
41
53
|
};
|
|
42
54
|
|
|
43
55
|
export const MATH_FUNCTIONS = new Set<string>([
|
|
44
56
|
"abs", "floor", "ceil", "round", "trunc", "sqrt", "cbrt", "pow", "exp", "log", "log2", "log10",
|
|
45
57
|
"sin", "cos", "tan", "asin", "acos", "atan", "atan2", "hypot", "sign", "random", "min", "max",
|
|
58
|
+
"log1p", "expm1", "sinh", "cosh", "tanh", "asinh", "acosh", "atanh", "fround", "imul", "clz32",
|
|
46
59
|
]);
|
|
47
60
|
|
|
48
61
|
export const MATH_CONSTANTS: Record<string, number> = {
|
|
@@ -66,6 +79,28 @@ export const GLOBAL_FUNCTIONS: Record<string, string> = {
|
|
|
66
79
|
Boolean: "xt_boolean_ctor",
|
|
67
80
|
};
|
|
68
81
|
|
|
82
|
+
/** Global namespaces whose static methods map to runtime dispatchers. */
|
|
83
|
+
export const NAMESPACE_STATICS: Record<string, string> = {
|
|
84
|
+
Math: "xt_math_call",
|
|
85
|
+
JSON: "xt_json",
|
|
86
|
+
Array: "xt_array_static",
|
|
87
|
+
Object: "xt_object_static",
|
|
88
|
+
Number: "xt_number_static",
|
|
89
|
+
String: "xt_string_static",
|
|
90
|
+
Date: "xt_date_static",
|
|
91
|
+
Promise: "xt_promise_static",
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
/** Global constructors called as `new X(...)` (all have signature `(i32, i64*)`). */
|
|
95
|
+
export const CTOR_FUNCTIONS: Record<string, string> = {
|
|
96
|
+
Array: "xt_array_new",
|
|
97
|
+
Map: "xt_map_ctor",
|
|
98
|
+
Set: "xt_set_ctor",
|
|
99
|
+
Date: "xt_date_ctor",
|
|
100
|
+
RegExp: "xt_regexp_ctor",
|
|
101
|
+
Promise: "xt_promise_ctor",
|
|
102
|
+
};
|
|
103
|
+
|
|
69
104
|
export const BUILTIN_METHODS = new Set<string>([
|
|
70
105
|
"push", "pop", "shift", "unshift", "join", "slice", "indexOf", "includes", "map", "forEach", "filter",
|
|
71
106
|
"reduce", "concat", "reverse", "charAt", "charCodeAt", "substring", "substr", "split", "toUpperCase",
|
package/src/driver/compiler.ts
CHANGED
|
@@ -13,6 +13,8 @@ import { DiagnosticBag, type Diagnostic } from "../diagnostics/diagnostic.js";
|
|
|
13
13
|
import { SourceFile } from "../diagnostics/source.js";
|
|
14
14
|
import { Parser } from "../parser/parser.js";
|
|
15
15
|
import { generate } from "../codegen/llvm.js";
|
|
16
|
+
import { SyntaxKind } from "../ast/nodes.js";
|
|
17
|
+
import { bundleModules } from "./modules.js";
|
|
16
18
|
import { createDefaultRegistry, type ExtensionRegistry } from "../extensions/registry.js";
|
|
17
19
|
import { BuildCache, hashParts } from "./cache.js";
|
|
18
20
|
import { findRuntimeDir } from "./paths.js";
|
|
@@ -94,9 +96,31 @@ export function build(entryPath: string, options: BuildOptions = {}): BuildResul
|
|
|
94
96
|
? resolve(options.output ?? join(outDir, baseName + ".o"))
|
|
95
97
|
: executableName(absoluteEntry, outDir, options.output);
|
|
96
98
|
|
|
99
|
+
// -- front end -----------------------------------------------------------
|
|
100
|
+
const file = new SourceFile(absoluteEntry, sourceText);
|
|
101
|
+
const diagnostics = new DiagnosticBag();
|
|
102
|
+
const parser = new Parser(file, diagnostics);
|
|
103
|
+
let sourceFile = parser.parseSourceFile();
|
|
104
|
+
|
|
105
|
+
// Lower `import`/`export` by merging every reachable module into one file.
|
|
106
|
+
const isModule = sourceFile.statements.some(
|
|
107
|
+
(statement) =>
|
|
108
|
+
statement.kind === SyntaxKind.ImportDeclaration ||
|
|
109
|
+
statement.kind === SyntaxKind.ExportDeclaration ||
|
|
110
|
+
statement.kind === SyntaxKind.ExportAssignment,
|
|
111
|
+
);
|
|
112
|
+
let cacheText = sourceText;
|
|
113
|
+
if (isModule) {
|
|
114
|
+
const bundled = bundleModules(absoluteEntry, diagnostics);
|
|
115
|
+
if (bundled) {
|
|
116
|
+
sourceFile = bundled.sourceFile;
|
|
117
|
+
cacheText = bundled.text;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
97
121
|
const cacheKey = hashParts([
|
|
98
122
|
COMPILER_VERSION,
|
|
99
|
-
|
|
123
|
+
cacheText,
|
|
100
124
|
emit,
|
|
101
125
|
optimize,
|
|
102
126
|
process.platform,
|
|
@@ -105,15 +129,10 @@ export function build(entryPath: string, options: BuildOptions = {}): BuildResul
|
|
|
105
129
|
|
|
106
130
|
const cache = new BuildCache(cacheDir);
|
|
107
131
|
const outputs = [outputPath];
|
|
108
|
-
if (!options.force && cache.isFresh(cacheKey, outputs)) {
|
|
132
|
+
if (!options.force && !diagnostics.hasErrors && cache.isFresh(cacheKey, outputs)) {
|
|
109
133
|
return { outputPath, cached: true, diagnostics: [] };
|
|
110
134
|
}
|
|
111
135
|
|
|
112
|
-
// -- front end -----------------------------------------------------------
|
|
113
|
-
const file = new SourceFile(absoluteEntry, sourceText);
|
|
114
|
-
const diagnostics = new DiagnosticBag();
|
|
115
|
-
const parser = new Parser(file, diagnostics);
|
|
116
|
-
const sourceFile = parser.parseSourceFile();
|
|
117
136
|
const { ir } = generate(sourceFile, diagnostics, { builtins: registry.builtins() });
|
|
118
137
|
|
|
119
138
|
if (options.verbose) {
|
|
@@ -174,6 +193,8 @@ const RUNTIME_SOURCES = [
|
|
|
174
193
|
"xt_values.c",
|
|
175
194
|
"xt_containers.c",
|
|
176
195
|
"xt_stdlib.c",
|
|
196
|
+
"xt_stdlib2.c",
|
|
197
|
+
"xt_promise.c",
|
|
177
198
|
"xt_builtins.c",
|
|
178
199
|
"xt_io.c",
|
|
179
200
|
] as const;
|