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
package/src/binder/binder.ts
CHANGED
|
@@ -9,14 +9,21 @@
|
|
|
9
9
|
* cached, serialized for the incremental build, and unit tested in isolation.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
+
import { NodeFlags, ModifierKind } from "../ast/kinds.js";
|
|
12
13
|
import {
|
|
13
14
|
SyntaxKind,
|
|
14
15
|
type ArrowFunction,
|
|
15
16
|
type Block,
|
|
17
|
+
type ClassDeclaration,
|
|
18
|
+
type ClassExpression,
|
|
19
|
+
type ConstructorDeclaration,
|
|
16
20
|
type FunctionDeclaration,
|
|
17
21
|
type FunctionExpression,
|
|
18
22
|
type Identifier,
|
|
23
|
+
type MethodDeclaration,
|
|
19
24
|
type Node,
|
|
25
|
+
type PropertyDeclaration,
|
|
26
|
+
type PropertyName,
|
|
20
27
|
type SourceFileNode,
|
|
21
28
|
type Statement,
|
|
22
29
|
type Expression,
|
|
@@ -76,6 +83,40 @@ export interface FunctionInfo {
|
|
|
76
83
|
readonly captureIndex: Map<number, number>;
|
|
77
84
|
readonly isModule: boolean;
|
|
78
85
|
readonly isArrow: boolean;
|
|
86
|
+
/** True when the function was declared `async`. */
|
|
87
|
+
readonly isAsync: boolean;
|
|
88
|
+
/** True when this function's body mentions `this` directly. */
|
|
89
|
+
usesThis: boolean;
|
|
90
|
+
/** True when an arrow function must receive `this` from its enclosing scope. */
|
|
91
|
+
capturesThis: boolean;
|
|
92
|
+
/** Set for class methods/constructors; carries the owning class. */
|
|
93
|
+
classInfo?: ClassInfo;
|
|
94
|
+
/** True for `static` class members. */
|
|
95
|
+
isStatic?: boolean;
|
|
96
|
+
/** True for an explicit `constructor`. */
|
|
97
|
+
isConstructor?: boolean;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export interface ClassInfo {
|
|
101
|
+
readonly id: number;
|
|
102
|
+
readonly name: string;
|
|
103
|
+
readonly node: Node;
|
|
104
|
+
readonly scope: Scope;
|
|
105
|
+
readonly parent?: ClassInfo;
|
|
106
|
+
readonly parentExpression?: Expression;
|
|
107
|
+
/** `this`-bearing fields declared on the class. */
|
|
108
|
+
readonly fields: ClassFieldInfo[];
|
|
109
|
+
readonly methods: FunctionInfo[];
|
|
110
|
+
readonly statics: FunctionInfo[];
|
|
111
|
+
ctor?: FunctionInfo;
|
|
112
|
+
/** The symbol the class name binds to, when there is one. */
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export interface ClassFieldInfo {
|
|
116
|
+
readonly name: string;
|
|
117
|
+
readonly initializer: Expression | undefined;
|
|
118
|
+
readonly fn: FunctionInfo;
|
|
119
|
+
readonly isStatic: boolean;
|
|
79
120
|
}
|
|
80
121
|
|
|
81
122
|
export enum ScopeKind {
|
|
@@ -98,6 +139,9 @@ export interface Scope {
|
|
|
98
139
|
export interface BindResult {
|
|
99
140
|
readonly moduleFunction: FunctionInfo;
|
|
100
141
|
readonly functions: FunctionInfo[];
|
|
142
|
+
readonly classes: ClassInfo[];
|
|
143
|
+
/** Maps a class-like AST node to the ClassInfo the binder built. */
|
|
144
|
+
readonly classOfNode: Map<Node, ClassInfo>;
|
|
101
145
|
/** Maps a function-like AST node to the FunctionInfo the binder built for it. */
|
|
102
146
|
readonly functionOfNode: Map<Node, FunctionInfo>;
|
|
103
147
|
readonly symbolOfDeclaration: Map<Node, SymbolInfo>;
|
|
@@ -120,12 +164,15 @@ export function bind(sourceFile: SourceFileNode): BindResult {
|
|
|
120
164
|
|
|
121
165
|
class Binder {
|
|
122
166
|
private readonly functions: FunctionInfo[] = [];
|
|
167
|
+
private readonly classes: ClassInfo[] = [];
|
|
168
|
+
private readonly classOfNode = new Map<Node, ClassInfo>();
|
|
123
169
|
private readonly symbolOfDeclaration = new Map<Node, SymbolInfo>();
|
|
124
170
|
private readonly symbolOfIdentifier = new Map<Identifier, SymbolInfo>();
|
|
125
171
|
private readonly scopes = new Map<Node, Scope>();
|
|
126
172
|
private readonly unresolved: Identifier[] = [];
|
|
127
173
|
private nextSymbolId = 1;
|
|
128
174
|
private nextFunctionId = 1;
|
|
175
|
+
private nextClassId = 1;
|
|
129
176
|
private current!: MutableFunction;
|
|
130
177
|
|
|
131
178
|
constructor(private readonly sourceFile: SourceFileNode) {}
|
|
@@ -140,9 +187,22 @@ class Binder {
|
|
|
140
187
|
this.collectFunctionScoped(this.sourceFile.statements, this.current);
|
|
141
188
|
this.bindStatements(this.sourceFile.statements, scope);
|
|
142
189
|
|
|
190
|
+
// Arrows inherit `this` lexically: propagate the need to capture it up the
|
|
191
|
+
// chain of arrow functions until reaching a regular function.
|
|
192
|
+
for (let index = this.functions.length - 1; index >= 0; index--) {
|
|
193
|
+
const fn = this.functions[index]! as MutableFunction;
|
|
194
|
+
if (fn.usesThis && fn.isArrow) {
|
|
195
|
+
fn.capturesThis = true;
|
|
196
|
+
const parent = fn.parent as MutableFunction | undefined;
|
|
197
|
+
if (parent && parent.isArrow) parent.usesThis = true;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
143
201
|
return {
|
|
144
202
|
moduleFunction: this.current,
|
|
145
203
|
functions: this.functions,
|
|
204
|
+
classes: this.classes,
|
|
205
|
+
classOfNode: this.classOfNode,
|
|
146
206
|
functionOfNode: new Map(this.functions.map((fn) => [fn.node as Node, fn])),
|
|
147
207
|
symbolOfDeclaration: this.symbolOfDeclaration,
|
|
148
208
|
symbolOfIdentifier: this.symbolOfIdentifier,
|
|
@@ -172,6 +232,9 @@ class Binder {
|
|
|
172
232
|
captureIndex: new Map(),
|
|
173
233
|
isModule,
|
|
174
234
|
isArrow,
|
|
235
|
+
isAsync: (((node as { flags?: number }).flags ?? 0) & NodeFlags.Async) !== 0,
|
|
236
|
+
usesThis: false,
|
|
237
|
+
capturesThis: false,
|
|
175
238
|
};
|
|
176
239
|
this.functions.push(fn);
|
|
177
240
|
return fn;
|
|
@@ -369,6 +432,13 @@ class Binder {
|
|
|
369
432
|
case SyntaxKind.Identifier:
|
|
370
433
|
this.reference(node as Identifier, scope);
|
|
371
434
|
return;
|
|
435
|
+
case SyntaxKind.ThisKeyword:
|
|
436
|
+
this.current.usesThis = true;
|
|
437
|
+
return;
|
|
438
|
+
case SyntaxKind.ClassDeclaration:
|
|
439
|
+
case SyntaxKind.ClassExpression:
|
|
440
|
+
this.bindClass(node as ClassDeclaration | ClassExpression, scope);
|
|
441
|
+
return;
|
|
372
442
|
case SyntaxKind.InterfaceDeclaration:
|
|
373
443
|
case SyntaxKind.TypeAliasDeclaration:
|
|
374
444
|
// Type-only: skip the body entirely.
|
|
@@ -490,7 +560,118 @@ class Binder {
|
|
|
490
560
|
this.current = parentFn;
|
|
491
561
|
}
|
|
492
562
|
|
|
563
|
+
private bindClass(node: ClassDeclaration | ClassExpression, scope: Scope): void {
|
|
564
|
+
const parentFn = this.current;
|
|
565
|
+
const name = node.name ? node.name.text : "(anonymous)";
|
|
566
|
+
|
|
567
|
+
let parentExpression: Expression | undefined;
|
|
568
|
+
let parentClass: ClassInfo | undefined;
|
|
569
|
+
const heritage = node.heritage.find((clause) => clause.token === "extends");
|
|
570
|
+
if (heritage && heritage.types.length > 0) {
|
|
571
|
+
parentExpression = heritage.types[0]!.expression;
|
|
572
|
+
this.bindNode(parentExpression, scope);
|
|
573
|
+
if (parentExpression.kind === SyntaxKind.Identifier) {
|
|
574
|
+
const symbol = this.symbolOfIdentifier.get(parentExpression as Identifier);
|
|
575
|
+
const declaration = symbol?.declarations[0];
|
|
576
|
+
if (declaration) parentClass = this.classOfNode.get(declaration);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
const info: ClassInfo = {
|
|
581
|
+
id: this.nextClassId++,
|
|
582
|
+
name,
|
|
583
|
+
node,
|
|
584
|
+
scope,
|
|
585
|
+
parent: parentClass,
|
|
586
|
+
parentExpression,
|
|
587
|
+
fields: [],
|
|
588
|
+
methods: [],
|
|
589
|
+
statics: [],
|
|
590
|
+
};
|
|
591
|
+
this.classes.push(info);
|
|
592
|
+
this.classOfNode.set(node, info);
|
|
593
|
+
|
|
594
|
+
const classScope = this.createScope(ScopeKind.Block, node, scope);
|
|
595
|
+
this.scopes.set(node, classScope);
|
|
596
|
+
|
|
597
|
+
let constructor: FunctionInfo | undefined;
|
|
598
|
+
for (const member of node.members) {
|
|
599
|
+
if (member.kind === SyntaxKind.ConstructorDeclaration) {
|
|
600
|
+
const ctor = member as ConstructorDeclaration;
|
|
601
|
+
constructor = this.createClassFunction(ctor, "constructor", ctor.parameters, ctor.body, classScope, info, false, true);
|
|
602
|
+
} else if (member.kind === SyntaxKind.MethodDeclaration) {
|
|
603
|
+
const method = member as MethodDeclaration;
|
|
604
|
+
const isStatic = method.modifiers.some((modifier) => modifier.modifierKind === ModifierKind.Static);
|
|
605
|
+
const fn = this.createClassFunction(method, classMemberName(method.name), method.parameters, method.body, classScope, info, isStatic, false);
|
|
606
|
+
if (isStatic) (info.statics as FunctionInfo[]).push(fn);
|
|
607
|
+
else (info.methods as FunctionInfo[]).push(fn);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
if (!constructor) {
|
|
611
|
+
constructor = this.createClassFunction(node, "constructor", [], undefined, classScope, info, false, true);
|
|
612
|
+
}
|
|
613
|
+
info.ctor = constructor;
|
|
614
|
+
|
|
615
|
+
// Field initializers run in the constructor's context so they can capture
|
|
616
|
+
// enclosing variables.
|
|
617
|
+
for (const member of node.members) {
|
|
618
|
+
if (member.kind === SyntaxKind.PropertyDeclaration) {
|
|
619
|
+
const property = member as PropertyDeclaration;
|
|
620
|
+
const isStatic = property.modifiers.some((modifier) => modifier.modifierKind === ModifierKind.Static);
|
|
621
|
+
const owner = this.current;
|
|
622
|
+
this.current = constructor as MutableFunction;
|
|
623
|
+
if (property.initializer) this.bindNode(property.initializer, classScope);
|
|
624
|
+
this.current = owner;
|
|
625
|
+
info.fields.push({ name: classMemberName(property.name), initializer: property.initializer, fn: constructor, isStatic });
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
this.current = parentFn;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
private createClassFunction(
|
|
633
|
+
member: Node,
|
|
634
|
+
name: string,
|
|
635
|
+
parameters: readonly import("../ast/declarations.js").Parameter[],
|
|
636
|
+
body: Block | undefined,
|
|
637
|
+
parentScope: Scope,
|
|
638
|
+
info: ClassInfo,
|
|
639
|
+
isStatic: boolean,
|
|
640
|
+
isConstructor: boolean,
|
|
641
|
+
): FunctionInfo {
|
|
642
|
+
const parentFn = this.current;
|
|
643
|
+
const fn = this.createFunction(name, member as unknown as FunctionNode, parentFn, false, false);
|
|
644
|
+
(fn as MutableFunction & { classInfo: ClassInfo }).classInfo = info;
|
|
645
|
+
(fn as MutableFunction & { isStatic: boolean }).isStatic = isStatic;
|
|
646
|
+
(fn as MutableFunction & { isConstructor: boolean }).isConstructor = isConstructor;
|
|
647
|
+
this.current = fn as MutableFunction;
|
|
648
|
+
const scope = this.createScope(ScopeKind.Function, member, parentScope);
|
|
649
|
+
(fn as { scope: Scope }).scope = scope;
|
|
650
|
+
this.scopes.set(member, scope);
|
|
651
|
+
|
|
652
|
+
const params: SymbolInfo[] = [];
|
|
653
|
+
for (const parameter of parameters) {
|
|
654
|
+
const paramSymbol = this.declare(parameter.name.text, SymbolKind.Parameter, parameter, scope, true);
|
|
655
|
+
params.push(paramSymbol);
|
|
656
|
+
if (parameter.initializer) this.bindNode(parameter.initializer, scope);
|
|
657
|
+
}
|
|
658
|
+
(fn as { params: SymbolInfo[] }).params = params;
|
|
659
|
+
|
|
660
|
+
if (body) {
|
|
661
|
+
this.collectFunctionScoped([body], fn as MutableFunction);
|
|
662
|
+
this.predeclareStatements(body.statements, scope);
|
|
663
|
+
this.bindStatements(body.statements, scope);
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
this.current = parentFn;
|
|
667
|
+
return fn;
|
|
668
|
+
}
|
|
669
|
+
|
|
493
670
|
private reference(identifier: Identifier, scope: Scope): void {
|
|
671
|
+
if (identifier.text === "super") {
|
|
672
|
+
this.current.usesThis = true;
|
|
673
|
+
return;
|
|
674
|
+
}
|
|
494
675
|
let current: Scope | undefined = scope;
|
|
495
676
|
while (current) {
|
|
496
677
|
const symbol = current.symbols.get(identifier.text);
|
|
@@ -547,3 +728,18 @@ function isCapturable(kind: SymbolKind): boolean {
|
|
|
547
728
|
kind === SymbolKind.Parameter
|
|
548
729
|
);
|
|
549
730
|
}
|
|
731
|
+
|
|
732
|
+
/** Extract the textual name of a class member (`method`, `"key"`, `0`). */
|
|
733
|
+
function classMemberName(name: PropertyName): string {
|
|
734
|
+
switch (name.kind) {
|
|
735
|
+
case SyntaxKind.Identifier:
|
|
736
|
+
case SyntaxKind.PrivateIdentifier:
|
|
737
|
+
return (name as unknown as { text: string }).text;
|
|
738
|
+
case SyntaxKind.StringLiteral:
|
|
739
|
+
return (name as unknown as { value: string }).value;
|
|
740
|
+
case SyntaxKind.NumericLiteral:
|
|
741
|
+
return String((name as unknown as { value: number }).value);
|
|
742
|
+
default:
|
|
743
|
+
return "";
|
|
744
|
+
}
|
|
745
|
+
}
|
|
@@ -15,14 +15,16 @@ import {
|
|
|
15
15
|
type ObjectLiteralExpression,
|
|
16
16
|
type PropertyAccessExpression,
|
|
17
17
|
} from "../../ast/nodes.js";
|
|
18
|
-
import { SymbolKind } from "../../binder/binder.js";
|
|
18
|
+
import { SymbolKind, type FunctionInfo } from "../../binder/binder.js";
|
|
19
19
|
import { i64, numberLiteral, XT_TRUE, XT_UNDEFINED } from "../values.js";
|
|
20
20
|
import {
|
|
21
21
|
BUILTIN_METHODS,
|
|
22
22
|
CONSOLE_METHODS,
|
|
23
|
+
CTOR_FUNCTIONS,
|
|
23
24
|
GLOBAL_FUNCTIONS,
|
|
24
25
|
MATH_CONSTANTS,
|
|
25
26
|
MATH_FUNCTIONS,
|
|
27
|
+
NAMESPACE_STATICS,
|
|
26
28
|
propertyNameText,
|
|
27
29
|
} from "./tables.js";
|
|
28
30
|
import type { Generator } from "./generator.js";
|
|
@@ -39,6 +41,9 @@ export interface CallMethods {
|
|
|
39
41
|
emitArrayLiteral(this: Generator, node: ArrayLiteralExpression): string;
|
|
40
42
|
emitObjectLiteral(this: Generator, node: ObjectLiteralExpression): string;
|
|
41
43
|
emitClosure(this: Generator, node: ArrowFunction | FunctionExpression): string;
|
|
44
|
+
emitClosureValue(this: Generator, fn: FunctionInfo): string;
|
|
45
|
+
emitSuperConstructor(this: Generator, node: CallExpression): string;
|
|
46
|
+
emitSuperCall(this: Generator, node: CallExpression, access: PropertyAccessExpression): string;
|
|
42
47
|
}
|
|
43
48
|
|
|
44
49
|
export const callMethods: CallMethods = {
|
|
@@ -76,10 +81,30 @@ export const callMethods: CallMethods = {
|
|
|
76
81
|
});
|
|
77
82
|
}
|
|
78
83
|
if (callee.kind === SyntaxKind.PropertyAccessExpression) {
|
|
79
|
-
const
|
|
84
|
+
const access = callee as PropertyAccessExpression;
|
|
85
|
+
const special = this.tryEmitBuiltinCall(node, access);
|
|
80
86
|
if (special) return special;
|
|
87
|
+
if (access.expression.kind === SyntaxKind.Identifier && (access.expression as Identifier).text === "super") {
|
|
88
|
+
return this.emitSuperCall(node, access);
|
|
89
|
+
}
|
|
90
|
+
const object = this.emitExpression(access.expression);
|
|
91
|
+
const name = this.stringValue(access.name.text);
|
|
92
|
+
const args = this.emitArguments(node.arguments);
|
|
93
|
+
const result = this.reg();
|
|
94
|
+
this.emit(` ${result} = call i64 @xt_call_method(i64 ${object}, i64 ${name}, i32 ${args.argc}, i64* ${args.ptr})`);
|
|
95
|
+
return result;
|
|
96
|
+
}
|
|
97
|
+
if (callee.kind === SyntaxKind.ElementAccessExpression) {
|
|
98
|
+
const access = callee as ElementAccessExpression;
|
|
99
|
+
const object = this.emitExpression(access.expression);
|
|
100
|
+
const name = this.emitExpression(access.argumentExpression);
|
|
101
|
+
const args = this.emitArguments(node.arguments);
|
|
102
|
+
const result = this.reg();
|
|
103
|
+
this.emit(` ${result} = call i64 @xt_call_method(i64 ${object}, i64 ${name}, i32 ${args.argc}, i64* ${args.ptr})`);
|
|
104
|
+
return result;
|
|
81
105
|
}
|
|
82
106
|
if (callee.kind === SyntaxKind.Identifier) {
|
|
107
|
+
if ((callee as Identifier).text === "super") return this.emitSuperConstructor(node);
|
|
83
108
|
const symbol = this.binding.symbolOfIdentifier.get(callee as Identifier);
|
|
84
109
|
if (symbol && symbol.kind === SymbolKind.Function) {
|
|
85
110
|
const declaration = symbol.declarations[0];
|
|
@@ -88,7 +113,7 @@ export const callMethods: CallMethods = {
|
|
|
88
113
|
const args = this.emitArguments(node.arguments);
|
|
89
114
|
const result = this.reg();
|
|
90
115
|
this.emit(
|
|
91
|
-
` ${result} = call i64 @${this.functionName(fn)}(i64 ${i64(XT_UNDEFINED)}, i32 ${args.argc}, i64* ${args.ptr})`,
|
|
116
|
+
` ${result} = call i64 @${this.functionName(fn)}(i64 ${i64(XT_UNDEFINED)}, i64 ${i64(XT_UNDEFINED)}, i32 ${args.argc}, i64* ${args.ptr})`,
|
|
92
117
|
);
|
|
93
118
|
return result;
|
|
94
119
|
}
|
|
@@ -114,6 +139,13 @@ export const callMethods: CallMethods = {
|
|
|
114
139
|
this.emit(` ${result} = call i64 @${builtin.symbol}(i32 ${args.argc}, i64* ${args.ptr})`);
|
|
115
140
|
return result;
|
|
116
141
|
}
|
|
142
|
+
const ctor = !symbol ? CTOR_FUNCTIONS[(callee as Identifier).text] : undefined;
|
|
143
|
+
if (ctor) {
|
|
144
|
+
const args = this.emitArguments(node.arguments);
|
|
145
|
+
const result = this.reg();
|
|
146
|
+
this.emit(` ${result} = call i64 @${ctor}(i32 ${args.argc}, i64* ${args.ptr})`);
|
|
147
|
+
return result;
|
|
148
|
+
}
|
|
117
149
|
}
|
|
118
150
|
const calleeValue = this.emitExpression(callee);
|
|
119
151
|
const args = this.emitArguments(node.arguments);
|
|
@@ -147,31 +179,27 @@ export const callMethods: CallMethods = {
|
|
|
147
179
|
return result;
|
|
148
180
|
}
|
|
149
181
|
|
|
150
|
-
if (targetIdentifier && targetIdentifier.text === "
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
}
|
|
156
|
-
|
|
182
|
+
if (targetIdentifier && targetIdentifier.text === "JSON" && !targetSymbol) {
|
|
183
|
+
const fn = method === "parse" ? "xt_json_parse" : method === "stringify" ? "xt_json_stringify" : undefined;
|
|
184
|
+
if (!fn) return undefined;
|
|
185
|
+
const args = this.emitArguments(node.arguments);
|
|
186
|
+
const result = this.reg();
|
|
187
|
+
this.emit(` ${result} = call i64 @${fn}(i32 ${args.argc}, i64* ${args.ptr})`);
|
|
188
|
+
return result;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (targetIdentifier && !targetSymbol) {
|
|
192
|
+
const dispatcher = NAMESPACE_STATICS[targetIdentifier.text];
|
|
193
|
+
if (dispatcher && targetIdentifier.text !== "Math" && targetIdentifier.text !== "JSON") {
|
|
194
|
+
const name = this.stringValue(method);
|
|
157
195
|
const args = this.emitArguments(node.arguments);
|
|
158
196
|
const result = this.reg();
|
|
159
|
-
this.emit(` ${result} = call i64
|
|
197
|
+
this.emit(` ${result} = call i64 @${dispatcher}(i64 ${name}, i32 ${args.argc}, i64* ${args.ptr})`);
|
|
160
198
|
return result;
|
|
161
199
|
}
|
|
162
|
-
return undefined;
|
|
163
200
|
}
|
|
164
201
|
|
|
165
|
-
|
|
166
|
-
const object = this.emitExpression(target);
|
|
167
|
-
const name = this.stringValue(method);
|
|
168
|
-
const args = this.emitArguments(node.arguments);
|
|
169
|
-
const result = this.reg();
|
|
170
|
-
this.emit(
|
|
171
|
-
` ${result} = call i64 @xt_call_method(i64 ${object}, i64 ${name}, i32 ${args.argc}, i64* ${args.ptr})`,
|
|
172
|
-
);
|
|
173
|
-
return result;
|
|
174
|
-
}
|
|
202
|
+
void BUILTIN_METHODS;
|
|
175
203
|
return undefined;
|
|
176
204
|
},
|
|
177
205
|
|
|
@@ -212,6 +240,17 @@ export const callMethods: CallMethods = {
|
|
|
212
240
|
},
|
|
213
241
|
|
|
214
242
|
emitPropertyAccess(node: PropertyAccessExpression): string {
|
|
243
|
+
if (node.expression.kind === SyntaxKind.Identifier && (node.expression as Identifier).text === "super") {
|
|
244
|
+
const thisValue = this.emitThis();
|
|
245
|
+
const proto = this.reg();
|
|
246
|
+
this.emit(` ${proto} = call i64 @xt_object_get_prototype(i64 ${thisValue})`);
|
|
247
|
+
const parentProto = this.reg();
|
|
248
|
+
this.emit(` ${parentProto} = call i64 @xt_object_get_prototype(i64 ${proto})`);
|
|
249
|
+
const key = this.stringValue(node.name.text);
|
|
250
|
+
const value = this.reg();
|
|
251
|
+
this.emit(` ${value} = call i64 @xt_get(i64 ${parentProto}, i64 ${key})`);
|
|
252
|
+
return value;
|
|
253
|
+
}
|
|
215
254
|
if (
|
|
216
255
|
node.name.text in MATH_CONSTANTS &&
|
|
217
256
|
node.expression.kind === SyntaxKind.Identifier &&
|
|
@@ -323,33 +362,72 @@ export const callMethods: CallMethods = {
|
|
|
323
362
|
this.unsupported(node, "closure");
|
|
324
363
|
return i64(XT_UNDEFINED);
|
|
325
364
|
}
|
|
365
|
+
return this.emitClosureValue(fn);
|
|
366
|
+
},
|
|
367
|
+
|
|
368
|
+
emitClosureValue(fn: FunctionInfo): string {
|
|
369
|
+
const count = fn.captures.length + (fn.capturesThis ? 1 : 0);
|
|
326
370
|
let envPtr = "null";
|
|
327
|
-
if (
|
|
371
|
+
if (count > 0) {
|
|
328
372
|
envPtr = `%env${this.current.allocas.length}`;
|
|
329
|
-
this.current.allocas.push(`${envPtr} = alloca i64, i32 ${
|
|
373
|
+
this.current.allocas.push(`${envPtr} = alloca i64, i32 ${count}`);
|
|
330
374
|
for (let index = 0; index < fn.captures.length; index++) {
|
|
331
375
|
const symbol = fn.captures[index]!;
|
|
332
|
-
// The current function must be able to read the capture: either from
|
|
333
|
-
// its own slot (local) or from one of its own capture slots.
|
|
334
376
|
const slot = this.current.slots.get(symbol.id);
|
|
335
|
-
let raw
|
|
377
|
+
let raw = i64(XT_UNDEFINED);
|
|
336
378
|
if (slot) {
|
|
337
379
|
raw = this.reg();
|
|
338
380
|
this.emit(` ${raw} = load i64, i64* ${slot.ptr}`);
|
|
339
|
-
} else {
|
|
340
|
-
const outer = this.binding.functionOfNode.get(node);
|
|
341
|
-
void outer;
|
|
342
|
-
raw = i64(XT_UNDEFINED);
|
|
343
381
|
}
|
|
344
382
|
const target = this.reg();
|
|
345
383
|
this.emit(` ${target} = getelementptr i64, i64* ${envPtr}, i32 ${index}`);
|
|
346
384
|
this.emit(` store i64 ${raw}, i64* ${target}`);
|
|
347
385
|
}
|
|
386
|
+
if (fn.capturesThis) {
|
|
387
|
+
let thisValue = i64(XT_UNDEFINED);
|
|
388
|
+
if (this.current.thisPtr) {
|
|
389
|
+
thisValue = this.reg();
|
|
390
|
+
this.emit(` ${thisValue} = load i64, i64* ${this.current.thisPtr}`);
|
|
391
|
+
}
|
|
392
|
+
const target = this.reg();
|
|
393
|
+
this.emit(` ${target} = getelementptr i64, i64* ${envPtr}, i32 ${fn.captures.length}`);
|
|
394
|
+
this.emit(` store i64 ${thisValue}, i64* ${target}`);
|
|
395
|
+
}
|
|
348
396
|
}
|
|
349
397
|
const cast = this.reg();
|
|
350
|
-
this.emit(` ${cast} = bitcast i64 (i64, i32, i64*)* @${this.functionName(fn)} to i8*`);
|
|
398
|
+
this.emit(` ${cast} = bitcast i64 (i64, i64, i32, i64*)* @${this.functionName(fn)} to i8*`);
|
|
351
399
|
const closure = this.reg();
|
|
352
|
-
this.emit(` ${closure} = call i64 @xt_closure_new(i8* ${cast}, i32 ${
|
|
400
|
+
this.emit(` ${closure} = call i64 @xt_closure_new(i8* ${cast}, i32 ${count}, i64* ${envPtr})`);
|
|
353
401
|
return closure;
|
|
354
402
|
},
|
|
403
|
+
|
|
404
|
+
emitSuperConstructor(node: CallExpression): string {
|
|
405
|
+
const thisValue = this.emitThis();
|
|
406
|
+
const proto = this.reg();
|
|
407
|
+
this.emit(` ${proto} = call i64 @xt_object_get_prototype(i64 ${thisValue})`);
|
|
408
|
+
const parentProto = this.reg();
|
|
409
|
+
this.emit(` ${parentProto} = call i64 @xt_object_get_prototype(i64 ${proto})`);
|
|
410
|
+
const key = this.stringValue("__ctor");
|
|
411
|
+
const ctor = this.reg();
|
|
412
|
+
this.emit(` ${ctor} = call i64 @xt_get(i64 ${parentProto}, i64 ${key})`);
|
|
413
|
+
const args = this.emitArguments(node.arguments);
|
|
414
|
+
const result = this.reg();
|
|
415
|
+
this.emit(` ${result} = call i64 @xt_call_with_this(i64 ${ctor}, i64 ${thisValue}, i32 ${args.argc}, i64* ${args.ptr})`);
|
|
416
|
+
return result;
|
|
417
|
+
},
|
|
418
|
+
|
|
419
|
+
emitSuperCall(node: CallExpression, access: PropertyAccessExpression): string {
|
|
420
|
+
const thisValue = this.emitThis();
|
|
421
|
+
const proto = this.reg();
|
|
422
|
+
this.emit(` ${proto} = call i64 @xt_object_get_prototype(i64 ${thisValue})`);
|
|
423
|
+
const parentProto = this.reg();
|
|
424
|
+
this.emit(` ${parentProto} = call i64 @xt_object_get_prototype(i64 ${proto})`);
|
|
425
|
+
const key = this.stringValue(access.name.text);
|
|
426
|
+
const method = this.reg();
|
|
427
|
+
this.emit(` ${method} = call i64 @xt_get(i64 ${parentProto}, i64 ${key})`);
|
|
428
|
+
const args = this.emitArguments(node.arguments);
|
|
429
|
+
const result = this.reg();
|
|
430
|
+
this.emit(` ${result} = call i64 @xt_call_with_this(i64 ${method}, i64 ${thisValue}, i32 ${args.argc}, i64* ${args.ptr})`);
|
|
431
|
+
return result;
|
|
432
|
+
},
|
|
355
433
|
};
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* helpers every group relies on.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { bind, type BindResult, type SymbolInfo } from "../../binder/binder.js";
|
|
9
|
+
import { bind, type BindResult, type ClassInfo, type SymbolInfo } from "../../binder/binder.js";
|
|
10
10
|
import type { DiagnosticBag } from "../../diagnostics/diagnostic.js";
|
|
11
11
|
import { DiagnosticCode } from "../../diagnostics/diagnostic.js";
|
|
12
12
|
import type { SourceFileNode, Node } from "../../ast/nodes.js";
|
|
@@ -23,6 +23,8 @@ export class GeneratorContext {
|
|
|
23
23
|
readonly globals: string[] = [];
|
|
24
24
|
readonly functions: string[] = [];
|
|
25
25
|
readonly strings = new Map<string, { label: string; length: number }>();
|
|
26
|
+
readonly classGlobals = new Map<number, string>();
|
|
27
|
+
readonly moduleGlobals = new Map<number, string>();
|
|
26
28
|
readonly extraDeclarations = new Set<string>();
|
|
27
29
|
stringCounter = 0;
|
|
28
30
|
current!: FunctionState;
|
|
@@ -72,6 +74,18 @@ export class GeneratorContext {
|
|
|
72
74
|
// -- variables -----------------------------------------------------------
|
|
73
75
|
|
|
74
76
|
declareSlot(symbol: SymbolInfo, initial: string): void {
|
|
77
|
+
const globalName = this.moduleGlobals.get(symbol.id);
|
|
78
|
+
if (globalName) {
|
|
79
|
+
if (symbol.boxed) {
|
|
80
|
+
const box = this.reg();
|
|
81
|
+
this.emit(` ${box} = call i64 @xt_box_new(i64 ${initial})`);
|
|
82
|
+
this.emit(` store i64 ${box}, i64* ${globalName}`);
|
|
83
|
+
} else {
|
|
84
|
+
this.emit(` store i64 ${initial}, i64* ${globalName}`);
|
|
85
|
+
}
|
|
86
|
+
this.current.slots.set(symbol.id, { ptr: globalName, boxed: symbol.boxed });
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
75
89
|
const ptr = this.alloca();
|
|
76
90
|
if (symbol.boxed) {
|
|
77
91
|
const box = this.reg();
|
|
@@ -91,6 +105,15 @@ export class GeneratorContext {
|
|
|
91
105
|
}
|
|
92
106
|
|
|
93
107
|
readSlot(symbol: SymbolInfo): string {
|
|
108
|
+
const globalName = this.moduleGlobals.get(symbol.id);
|
|
109
|
+
if (globalName) {
|
|
110
|
+
const value = this.reg();
|
|
111
|
+
this.emit(` ${value} = load i64, i64* ${globalName}`);
|
|
112
|
+
if (!symbol.boxed) return value;
|
|
113
|
+
const unboxed = this.reg();
|
|
114
|
+
this.emit(` ${unboxed} = call i64 @xt_box_get(i64 ${value})`);
|
|
115
|
+
return unboxed;
|
|
116
|
+
}
|
|
94
117
|
const slot = this.current.slots.get(symbol.id);
|
|
95
118
|
if (!slot) {
|
|
96
119
|
// Referenced from an inner function without a capture slot: this should
|
|
@@ -110,6 +133,17 @@ export class GeneratorContext {
|
|
|
110
133
|
}
|
|
111
134
|
|
|
112
135
|
writeSlot(symbol: SymbolInfo, value: string): void {
|
|
136
|
+
const globalName = this.moduleGlobals.get(symbol.id);
|
|
137
|
+
if (globalName) {
|
|
138
|
+
if (!symbol.boxed) {
|
|
139
|
+
this.emit(` store i64 ${value}, i64* ${globalName}`);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
const box = this.reg();
|
|
143
|
+
this.emit(` ${box} = load i64, i64* ${globalName}`);
|
|
144
|
+
this.emit(` call i64 @xt_box_set(i64 ${box}, i64 ${value})`);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
113
147
|
const slot = this.current.slots.get(symbol.id);
|
|
114
148
|
if (!slot) {
|
|
115
149
|
this.diagnostics.error(DiagnosticCode.CodegenError, `Internal: no slot for '${symbol.name}'`);
|