gofront 1.6.0 → 1.7.0
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/README.md +35 -39
- package/package.json +37 -4
- package/src/backend/js/expressions.js +559 -144
- package/src/backend/js/index.js +174 -49
- package/src/backend/js/runtime.js +1518 -54
- package/src/backend/js/statements.js +91 -73
- package/src/backend/js/stdlib/base64.js +12 -0
- package/src/backend/js/stdlib/binary.js +102 -0
- package/src/backend/js/stdlib/bits.js +15 -1
- package/src/backend/js/stdlib/builder.js +28 -9
- package/src/backend/js/stdlib/bytes.js +75 -18
- package/src/backend/js/stdlib/errors.js +25 -1
- package/src/backend/js/stdlib/fmt.js +70 -13
- package/src/backend/js/stdlib/hex.js +17 -0
- package/src/backend/js/stdlib/html.js +1 -1
- package/src/backend/js/stdlib/index.js +28 -0
- package/src/backend/js/stdlib/io.js +105 -3
- package/src/backend/js/stdlib/json.js +972 -0
- package/src/backend/js/stdlib/math.js +13 -1
- package/src/backend/js/stdlib/path.js +18 -4
- package/src/backend/js/stdlib/rand.js +10 -5
- package/src/backend/js/stdlib/sha256.js +12 -0
- package/src/backend/js/stdlib/simd.js +24 -0
- package/src/backend/js/stdlib/slices.js +52 -26
- package/src/backend/js/stdlib/strconv.js +21 -5
- package/src/backend/js/stdlib/strings.js +148 -15
- package/src/backend/js/stdlib/sync.js +13 -0
- package/src/backend/js/stdlib/time.js +90 -19
- package/src/backend/js/stdlib/unicode.js +2 -2
- package/src/backend/wasm/boundary.js +1225 -41
- package/src/backend/wasm/const-fold.js +205 -0
- package/src/backend/wasm/emit-builtins.js +220 -76
- package/src/backend/wasm/emit-exprs.js +475 -276
- package/src/backend/wasm/emit-maps.js +144 -8
- package/src/backend/wasm/emit-stdlib.js +12 -3005
- package/src/backend/wasm/emit-stmts.js +264 -270
- package/src/backend/wasm/emit.js +124 -87
- package/src/backend/wasm/encode.js +140 -29
- package/src/backend/wasm/index.js +1001 -32
- package/src/backend/wasm/optimize.js +17 -6
- package/src/backend/wasm/stdlib/bytes.js +5039 -0
- package/src/backend/wasm/stdlib/core.js +8903 -0
- package/src/backend/wasm/stdlib/index.js +43 -0
- package/src/backend/wasm/stdlib/io.js +2161 -0
- package/src/backend/wasm/stdlib/packages.js +2400 -0
- package/src/backend/wasm/stdlib/pure.js +720 -0
- package/src/backend/wasm/stdlib/simd.js +234 -0
- package/src/backend/wasm/stdlib/time.js +1200 -0
- package/src/backend/wasm/types.js +174 -1
- package/src/backend/wasm/wat.js +37 -0
- package/src/cli-core.js +1 -1
- package/src/compiler.js +18 -4
- package/src/dts-parser.js +150 -140
- package/src/index.js +2 -6
- package/src/lexer.js +80 -29
- package/src/lower/boxing.js +1 -7
- package/src/lower/captures.js +2 -6
- package/src/lower/embedding.js +2 -1
- package/src/lower/ownership.js +30 -9
- package/src/minifier.js +20 -68
- package/src/parser/declarations.js +3 -6
- package/src/parser/expressions.js +3 -8
- package/src/parser/index.js +18 -3
- package/src/parser/statements.js +25 -38
- package/src/parser/struct-tag.js +144 -0
- package/src/parser/types.js +64 -8
- package/src/resolver.js +2 -27
- package/src/templ-lexer.js +29 -63
- package/src/templ-parser.js +22 -45
- package/src/typechecker/assignability.js +126 -13
- package/src/typechecker/expressions.js +168 -16
- package/src/typechecker/index.js +35 -8
- package/src/typechecker/resolve.js +187 -8
- package/src/typechecker/statements.js +68 -9
- package/src/typechecker/stdlib/core.js +280 -89
- package/src/typechecker/stdlib/extended.js +490 -1
- package/src/typechecker/stdlib/simd.js +73 -0
- package/src/typechecker/stdlib.js +2 -0
- package/src/typechecker/types.js +362 -9
- package/src/vendor.js +86 -11
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
// Compile-time folding of package-level constant expressions for the wasm
|
|
2
|
+
// backend. Folds literals, other already-folded constants, unary/binary
|
|
3
|
+
// operators and `len("literal")` into a single BasicLit. Anything it cannot
|
|
4
|
+
// reduce returns null and the caller falls back to a runtime-initialised
|
|
5
|
+
// global (see `_collectPackageConsts`).
|
|
6
|
+
|
|
7
|
+
const INT = "INT";
|
|
8
|
+
const FLOAT = "FLOAT";
|
|
9
|
+
const STRING = "STRING";
|
|
10
|
+
const BOOL = "BOOL";
|
|
11
|
+
|
|
12
|
+
function fromLit(lit) {
|
|
13
|
+
switch (lit.litKind) {
|
|
14
|
+
case INT: {
|
|
15
|
+
const s = String(lit.value);
|
|
16
|
+
const clean = /^0[0-7]+$/.test(s) ? `0o${s.slice(1)}` : s;
|
|
17
|
+
return { k: INT, v: BigInt(clean) };
|
|
18
|
+
}
|
|
19
|
+
case FLOAT: {
|
|
20
|
+
const n = Number(lit.value);
|
|
21
|
+
return Number.isFinite(n) ? { k: FLOAT, v: n } : null;
|
|
22
|
+
}
|
|
23
|
+
case STRING:
|
|
24
|
+
return { k: STRING, v: String(lit.value ?? "") };
|
|
25
|
+
case BOOL:
|
|
26
|
+
return { k: BOOL, v: lit.value === "true" };
|
|
27
|
+
default:
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function toLit(val) {
|
|
33
|
+
switch (val.k) {
|
|
34
|
+
case INT:
|
|
35
|
+
return { kind: "BasicLit", litKind: INT, value: val.v.toString() };
|
|
36
|
+
case FLOAT:
|
|
37
|
+
return {
|
|
38
|
+
kind: "BasicLit",
|
|
39
|
+
litKind: FLOAT,
|
|
40
|
+
value: Object.is(val.v, -0) ? "-0" : String(val.v),
|
|
41
|
+
};
|
|
42
|
+
case STRING:
|
|
43
|
+
return { kind: "BasicLit", litKind: STRING, value: val.v };
|
|
44
|
+
default:
|
|
45
|
+
return {
|
|
46
|
+
kind: "BasicLit",
|
|
47
|
+
litKind: BOOL,
|
|
48
|
+
value: val.v ? "true" : "false",
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function asFloat(val) {
|
|
54
|
+
return val.k === INT ? Number(val.v) : val.v;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function foldUnary(op, x) {
|
|
58
|
+
if (op === "-" && x.k === INT) return { k: INT, v: -x.v };
|
|
59
|
+
if (op === "-" && x.k === FLOAT) return { k: FLOAT, v: -x.v };
|
|
60
|
+
if (op === "+" && (x.k === INT || x.k === FLOAT)) return x;
|
|
61
|
+
if (op === "!" && x.k === BOOL) return { k: BOOL, v: !x.v };
|
|
62
|
+
if (op === "^" && x.k === INT) return { k: INT, v: ~x.v };
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function compare(op, a, b) {
|
|
67
|
+
switch (op) {
|
|
68
|
+
case "==":
|
|
69
|
+
return a === b;
|
|
70
|
+
case "!=":
|
|
71
|
+
return a !== b;
|
|
72
|
+
case "<":
|
|
73
|
+
return a < b;
|
|
74
|
+
case "<=":
|
|
75
|
+
return a <= b;
|
|
76
|
+
case ">":
|
|
77
|
+
return a > b;
|
|
78
|
+
case ">=":
|
|
79
|
+
return a >= b;
|
|
80
|
+
default:
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function foldInts(op, a, b) {
|
|
86
|
+
switch (op) {
|
|
87
|
+
case "+":
|
|
88
|
+
return { k: INT, v: a + b };
|
|
89
|
+
case "-":
|
|
90
|
+
return { k: INT, v: a - b };
|
|
91
|
+
case "*":
|
|
92
|
+
return { k: INT, v: a * b };
|
|
93
|
+
case "/":
|
|
94
|
+
if (b === 0n) throw new Error("division by zero");
|
|
95
|
+
return { k: INT, v: a / b };
|
|
96
|
+
case "%":
|
|
97
|
+
if (b === 0n) throw new Error("division by zero");
|
|
98
|
+
return { k: INT, v: a % b };
|
|
99
|
+
case "&":
|
|
100
|
+
return { k: INT, v: a & b };
|
|
101
|
+
case "|":
|
|
102
|
+
return { k: INT, v: a | b };
|
|
103
|
+
case "^":
|
|
104
|
+
return { k: INT, v: a ^ b };
|
|
105
|
+
case "&^":
|
|
106
|
+
return { k: INT, v: a & ~b };
|
|
107
|
+
case "<<":
|
|
108
|
+
if (b < 0n) throw new Error("negative shift count");
|
|
109
|
+
return b > 1024n ? null : { k: INT, v: a << b };
|
|
110
|
+
case ">>":
|
|
111
|
+
if (b < 0n) throw new Error("negative shift count");
|
|
112
|
+
return b > 1024n ? null : { k: INT, v: a >> b };
|
|
113
|
+
default: {
|
|
114
|
+
const r = compare(op, a, b);
|
|
115
|
+
return r === null ? null : { k: BOOL, v: r };
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function foldFloats(op, a, b) {
|
|
121
|
+
let v;
|
|
122
|
+
switch (op) {
|
|
123
|
+
case "+":
|
|
124
|
+
v = a + b;
|
|
125
|
+
break;
|
|
126
|
+
case "-":
|
|
127
|
+
v = a - b;
|
|
128
|
+
break;
|
|
129
|
+
case "*":
|
|
130
|
+
v = a * b;
|
|
131
|
+
break;
|
|
132
|
+
case "/":
|
|
133
|
+
if (b === 0) throw new Error("division by zero");
|
|
134
|
+
v = a / b;
|
|
135
|
+
break;
|
|
136
|
+
default: {
|
|
137
|
+
const r = compare(op, a, b);
|
|
138
|
+
return r === null ? null : { k: BOOL, v: r };
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return Number.isFinite(v) ? { k: FLOAT, v } : null;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function foldBinary(op, a, b) {
|
|
145
|
+
if (a.k === BOOL && b.k === BOOL) {
|
|
146
|
+
if (op === "&&") return { k: BOOL, v: a.v && b.v };
|
|
147
|
+
if (op === "||") return { k: BOOL, v: a.v || b.v };
|
|
148
|
+
const r = op === "==" || op === "!=" ? compare(op, a.v, b.v) : null;
|
|
149
|
+
return r === null ? null : { k: BOOL, v: r };
|
|
150
|
+
}
|
|
151
|
+
if (a.k === STRING && b.k === STRING) {
|
|
152
|
+
if (op === "+") return { k: STRING, v: a.v + b.v };
|
|
153
|
+
const r = compare(op, a.v, b.v);
|
|
154
|
+
return r === null ? null : { k: BOOL, v: r };
|
|
155
|
+
}
|
|
156
|
+
if (a.k === INT && b.k === INT) return foldInts(op, a.v, b.v);
|
|
157
|
+
if (
|
|
158
|
+
(a.k === INT || a.k === FLOAT) &&
|
|
159
|
+
(b.k === INT || b.k === FLOAT) &&
|
|
160
|
+
op !== "<<" &&
|
|
161
|
+
op !== ">>"
|
|
162
|
+
) {
|
|
163
|
+
return foldFloats(op, asFloat(a), asFloat(b));
|
|
164
|
+
}
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function foldExpr(expr, resolve) {
|
|
169
|
+
switch (expr?.kind) {
|
|
170
|
+
case "BasicLit":
|
|
171
|
+
return fromLit(expr);
|
|
172
|
+
case "Ident": {
|
|
173
|
+
if (expr.name === "true" || expr.name === "false")
|
|
174
|
+
return { k: BOOL, v: expr.name === "true" };
|
|
175
|
+
const lit = resolve(expr.name);
|
|
176
|
+
return lit ? fromLit(lit) : null;
|
|
177
|
+
}
|
|
178
|
+
case "UnaryExpr": {
|
|
179
|
+
const x = foldExpr(expr.operand, resolve);
|
|
180
|
+
return x ? foldUnary(expr.op, x) : null;
|
|
181
|
+
}
|
|
182
|
+
case "BinaryExpr": {
|
|
183
|
+
const a = foldExpr(expr.left, resolve);
|
|
184
|
+
const b = a ? foldExpr(expr.right, resolve) : null;
|
|
185
|
+
return a && b ? foldBinary(expr.op, a, b) : null;
|
|
186
|
+
}
|
|
187
|
+
case "CallExpr": {
|
|
188
|
+
if (expr.func?.kind !== "Ident" || expr.func.name !== "len") return null;
|
|
189
|
+
if (expr.args?.length !== 1) return null;
|
|
190
|
+
const s = foldExpr(expr.args[0], resolve);
|
|
191
|
+
if (s?.k !== STRING) return null;
|
|
192
|
+
// Gofront strings across JS and WASM runtime use UTF-16 code units (s.length).
|
|
193
|
+
return { k: INT, v: BigInt(s.v.length) };
|
|
194
|
+
}
|
|
195
|
+
default:
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Folds `expr` into a BasicLit, or returns null when it needs the runtime.
|
|
201
|
+
// `resolve(name)` yields the BasicLit of an already-folded constant, if any.
|
|
202
|
+
export function foldConstExpr(expr, resolve) {
|
|
203
|
+
const val = foldExpr(expr, resolve);
|
|
204
|
+
return val ? toLit(val) : null;
|
|
205
|
+
}
|
|
@@ -65,18 +65,6 @@ export class BuiltinsEmitter {
|
|
|
65
65
|
return null;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
_getArrayElemType(t) {
|
|
69
|
-
if (!t) return null;
|
|
70
|
-
if (t.elem) return t.elem;
|
|
71
|
-
if (t.underlying?.elem) return t.underlying.elem;
|
|
72
|
-
if (t.kind === "ArrayType") return t.elem;
|
|
73
|
-
if (t.name && this.mod.checker?.types?.has(t.name)) {
|
|
74
|
-
const resolved = this.mod.checker.types.get(t.name);
|
|
75
|
-
return this._getArrayElemType(resolved);
|
|
76
|
-
}
|
|
77
|
-
return null;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
68
|
_emitBuiltinLen(call, targetWasmType) {
|
|
81
69
|
const arg = call.args[0];
|
|
82
70
|
const argType = arg._type ?? this._resolveExprGoType(arg);
|
|
@@ -92,12 +80,7 @@ export class BuiltinsEmitter {
|
|
|
92
80
|
op: "call",
|
|
93
81
|
funcIndex: this.mod.resolveFuncIndex(mapInfo.lenFuncName),
|
|
94
82
|
});
|
|
95
|
-
|
|
96
|
-
this.pushInstruction("i64.extend_i32_u");
|
|
97
|
-
}
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
if (isSliceType(argType, this.mod.checker)) {
|
|
83
|
+
} else if (isSliceType(argType, this.mod.checker)) {
|
|
101
84
|
const elemType = this._getSliceElemType(argType);
|
|
102
85
|
const sliceInfo = this.mod.getSliceType(elemType);
|
|
103
86
|
const sliceWType = {
|
|
@@ -111,12 +94,7 @@ export class BuiltinsEmitter {
|
|
|
111
94
|
typeIndex: sliceInfo.typeIndex,
|
|
112
95
|
fieldIndex: 2,
|
|
113
96
|
});
|
|
114
|
-
|
|
115
|
-
this.pushInstruction("i64.extend_i32_u");
|
|
116
|
-
}
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
if (
|
|
97
|
+
} else if (
|
|
120
98
|
isArrayType(argType, this.mod.checker) ||
|
|
121
99
|
(argType?.kind === "pointer" &&
|
|
122
100
|
isArrayType(argType.base, this.mod.checker))
|
|
@@ -130,19 +108,13 @@ export class BuiltinsEmitter {
|
|
|
130
108
|
};
|
|
131
109
|
this.emitExpr(arg, arrWType);
|
|
132
110
|
this.pushInstruction("array.len");
|
|
133
|
-
|
|
134
|
-
this.pushInstruction("i64.extend_i32_u");
|
|
135
|
-
}
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
if (isStringType(argType, this.mod.checker)) {
|
|
111
|
+
} else if (isStringType(argType, this.mod.checker)) {
|
|
139
112
|
this.emitExpr(arg, "externref");
|
|
140
113
|
const lenIdx = this.mod.getStringLenImportIndex();
|
|
141
114
|
this.pushInstruction({ op: "call", funcIndex: lenIdx });
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
return;
|
|
115
|
+
}
|
|
116
|
+
if ((targetWasmType ?? "i64") === "i64") {
|
|
117
|
+
this.pushInstruction("i64.extend_i32_u");
|
|
146
118
|
}
|
|
147
119
|
}
|
|
148
120
|
|
|
@@ -177,12 +149,7 @@ export class BuiltinsEmitter {
|
|
|
177
149
|
typeIndex: sliceInfo.typeIndex,
|
|
178
150
|
fieldIndex: 3,
|
|
179
151
|
});
|
|
180
|
-
|
|
181
|
-
this.pushInstruction("i64.extend_i32_u");
|
|
182
|
-
}
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
if (
|
|
152
|
+
} else if (
|
|
186
153
|
isArrayType(argType, this.mod.checker) ||
|
|
187
154
|
(argType?.kind === "pointer" &&
|
|
188
155
|
isArrayType(argType.base, this.mod.checker))
|
|
@@ -196,10 +163,9 @@ export class BuiltinsEmitter {
|
|
|
196
163
|
};
|
|
197
164
|
this.emitExpr(arg, arrWType);
|
|
198
165
|
this.pushInstruction("array.len");
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
return;
|
|
166
|
+
}
|
|
167
|
+
if ((targetWasmType ?? "i64") === "i64") {
|
|
168
|
+
this.pushInstruction("i64.extend_i32_u");
|
|
203
169
|
}
|
|
204
170
|
}
|
|
205
171
|
|
|
@@ -894,12 +860,24 @@ export class BuiltinsEmitter {
|
|
|
894
860
|
this.emitSlicesCall(func.field, args, targetWasmType);
|
|
895
861
|
return true;
|
|
896
862
|
case "strings":
|
|
863
|
+
if (func.field === "NewReader") {
|
|
864
|
+
this.emitStringsNewReader(call, targetWasmType);
|
|
865
|
+
return true;
|
|
866
|
+
}
|
|
897
867
|
this.emitStringsCall(func.field, args, targetWasmType);
|
|
898
868
|
return true;
|
|
899
869
|
case "strconv":
|
|
900
870
|
this.emitStrconvCall(func.field, args);
|
|
901
871
|
return true;
|
|
902
872
|
case "fmt":
|
|
873
|
+
if (func.field.startsWith("Fprint")) {
|
|
874
|
+
this.emitFprintCall(func.field, call, targetWasmType);
|
|
875
|
+
return true;
|
|
876
|
+
}
|
|
877
|
+
if (func.field.startsWith("Sscan")) {
|
|
878
|
+
this.emitSscanCall(func.field, call, targetWasmType);
|
|
879
|
+
return true;
|
|
880
|
+
}
|
|
903
881
|
this.emitFmtCall(func.field, args);
|
|
904
882
|
return true;
|
|
905
883
|
case "errors":
|
|
@@ -914,12 +892,53 @@ export class BuiltinsEmitter {
|
|
|
914
892
|
case "shared":
|
|
915
893
|
this.emitSharedNew(func.field, args);
|
|
916
894
|
return true;
|
|
895
|
+
case "unicode":
|
|
896
|
+
this.emitUnicodeCall(func.field, args, targetWasmType);
|
|
897
|
+
return true;
|
|
898
|
+
case "rand":
|
|
899
|
+
this.emitRandCall(func.field, args, targetWasmType);
|
|
900
|
+
return true;
|
|
901
|
+
case "bytes":
|
|
902
|
+
if (func.field === "NewReader") {
|
|
903
|
+
this.emitBytesNewReader(call, targetWasmType);
|
|
904
|
+
return true;
|
|
905
|
+
}
|
|
906
|
+
this.emitBytesCall(func.field, args, targetWasmType);
|
|
907
|
+
return true;
|
|
908
|
+
case "path":
|
|
909
|
+
case "filepath":
|
|
910
|
+
this.emitPathCall(func.field, args);
|
|
911
|
+
return true;
|
|
912
|
+
case "html":
|
|
913
|
+
this.emitHtmlCall(func.field, args);
|
|
914
|
+
return true;
|
|
915
|
+
case "io":
|
|
916
|
+
this.emitIoCall(func.field, call, targetWasmType);
|
|
917
|
+
return true;
|
|
918
|
+
case "time":
|
|
919
|
+
this.emitTimeCall(func.field, call, targetWasmType);
|
|
920
|
+
return true;
|
|
921
|
+
case "binary":
|
|
922
|
+
this.emitBinaryCall(func.field, call, targetWasmType);
|
|
923
|
+
return true;
|
|
924
|
+
case "sha256":
|
|
925
|
+
this.emitSha256Call(func.field, call, targetWasmType);
|
|
926
|
+
return true;
|
|
927
|
+
case "hex":
|
|
928
|
+
this.emitHexCall(func.field, call, targetWasmType);
|
|
929
|
+
return true;
|
|
930
|
+
case "base64":
|
|
931
|
+
this.emitBase64Call(func.field, call, targetWasmType);
|
|
932
|
+
return true;
|
|
933
|
+
case "simd":
|
|
934
|
+
this.emitSimdPackageCall(func.field, args, targetWasmType);
|
|
935
|
+
return true;
|
|
917
936
|
default:
|
|
918
937
|
return false;
|
|
919
938
|
}
|
|
920
939
|
}
|
|
921
940
|
|
|
922
|
-
_emitMethodCall(call) {
|
|
941
|
+
_emitMethodCall(call, targetWasmType = null) {
|
|
923
942
|
const { func, args } = call;
|
|
924
943
|
if (func.kind !== "SelectorExpr") return false;
|
|
925
944
|
|
|
@@ -930,6 +949,39 @@ export class BuiltinsEmitter {
|
|
|
930
949
|
return true;
|
|
931
950
|
}
|
|
932
951
|
const recvTypeName = this._getReceiverTypeName(recvType, func.expr);
|
|
952
|
+
if (
|
|
953
|
+
recvTypeName === "simd.F32x4" ||
|
|
954
|
+
recvTypeName === "gofront/simd.F32x4" ||
|
|
955
|
+
recvType?.kind === "simd" ||
|
|
956
|
+
recvType?.underlying?.kind === "simd" ||
|
|
957
|
+
recvType?.name === "simd.F32x4"
|
|
958
|
+
) {
|
|
959
|
+
return this.emitSimdMethodCall(
|
|
960
|
+
func.field,
|
|
961
|
+
func.expr,
|
|
962
|
+
args,
|
|
963
|
+
targetWasmType,
|
|
964
|
+
);
|
|
965
|
+
}
|
|
966
|
+
if (
|
|
967
|
+
this._emitStdlibReceiverMethod(
|
|
968
|
+
recvTypeName,
|
|
969
|
+
func.field,
|
|
970
|
+
func.expr,
|
|
971
|
+
args,
|
|
972
|
+
targetWasmType,
|
|
973
|
+
)
|
|
974
|
+
) {
|
|
975
|
+
return true;
|
|
976
|
+
}
|
|
977
|
+
if (recvTypeName === "strings.Builder") {
|
|
978
|
+
this.emitBuilderMethodCall(func.field, func.expr, args);
|
|
979
|
+
return true;
|
|
980
|
+
}
|
|
981
|
+
if (recvTypeName === "strings.Replacer") {
|
|
982
|
+
this.emitReplacerMethodCall(func.field, func.expr, args);
|
|
983
|
+
return true;
|
|
984
|
+
}
|
|
933
985
|
if (isNonEmptyInterface(recvType, this.mod.checker)) {
|
|
934
986
|
const ifaceName = recvTypeName ?? "anon";
|
|
935
987
|
const dispatchName = `__dispatch_${ifaceName}_${func.field}`;
|
|
@@ -950,38 +1002,15 @@ export class BuiltinsEmitter {
|
|
|
950
1002
|
}
|
|
951
1003
|
if (recvTypeName) {
|
|
952
1004
|
const methodName = `${recvTypeName}.${func.field}`;
|
|
953
|
-
|
|
954
|
-
|
|
1005
|
+
const targetFuncIdx = this.mod.resolveFuncIndex(methodName);
|
|
1006
|
+
const targetParamTypes = this.mod.getFuncParamTypes(methodName);
|
|
955
1007
|
|
|
956
1008
|
// If not found directly, check embedded structs
|
|
957
|
-
if (
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
const subIdx = this.mod.resolveFuncIndex(embedMethodName);
|
|
963
|
-
if (subIdx !== null) {
|
|
964
|
-
targetFuncIdx = subIdx;
|
|
965
|
-
targetParamTypes = this.mod.getFuncParamTypes(embedMethodName);
|
|
966
|
-
const baseWType = this.toWasmType(recvType);
|
|
967
|
-
this.emitExpr(func.expr, baseWType);
|
|
968
|
-
this.pushInstruction({
|
|
969
|
-
op: "struct.get",
|
|
970
|
-
typeIndex: structInfo.typeIndex,
|
|
971
|
-
fieldIndex: embed.fieldIndex,
|
|
972
|
-
});
|
|
973
|
-
for (let i = 0; i < args.length; i++) {
|
|
974
|
-
const pType = targetParamTypes[i + 1] ?? null;
|
|
975
|
-
this.emitExpr(args[i], pType);
|
|
976
|
-
}
|
|
977
|
-
this.pushInstruction({
|
|
978
|
-
op: "call",
|
|
979
|
-
funcIndex: targetFuncIdx,
|
|
980
|
-
});
|
|
981
|
-
return true;
|
|
982
|
-
}
|
|
983
|
-
}
|
|
984
|
-
}
|
|
1009
|
+
if (
|
|
1010
|
+
targetFuncIdx === null &&
|
|
1011
|
+
this._emitEmbeddedMethodCall(recvTypeName, func, args, recvType)
|
|
1012
|
+
) {
|
|
1013
|
+
return true;
|
|
985
1014
|
}
|
|
986
1015
|
|
|
987
1016
|
if (targetFuncIdx !== null) {
|
|
@@ -1003,6 +1032,113 @@ export class BuiltinsEmitter {
|
|
|
1003
1032
|
return false;
|
|
1004
1033
|
}
|
|
1005
1034
|
|
|
1035
|
+
_emitStdlibReceiverMethod(
|
|
1036
|
+
recvTypeName,
|
|
1037
|
+
field,
|
|
1038
|
+
expr,
|
|
1039
|
+
args,
|
|
1040
|
+
targetWasmType = null,
|
|
1041
|
+
) {
|
|
1042
|
+
if (recvTypeName === "strings.Builder") {
|
|
1043
|
+
this.emitBuilderMethodCall(field, expr, args, targetWasmType);
|
|
1044
|
+
return true;
|
|
1045
|
+
}
|
|
1046
|
+
if (recvTypeName === "strings.Replacer") {
|
|
1047
|
+
this.emitReplacerMethodCall(field, expr, args);
|
|
1048
|
+
return true;
|
|
1049
|
+
}
|
|
1050
|
+
if (recvTypeName === "bytes.Buffer") {
|
|
1051
|
+
this.emitBytesBufferMethodCall(field, expr, args, targetWasmType);
|
|
1052
|
+
return true;
|
|
1053
|
+
}
|
|
1054
|
+
if (recvTypeName === "strings.Reader") {
|
|
1055
|
+
this.emitStringsReaderMethodCall(field, expr, args, targetWasmType);
|
|
1056
|
+
return true;
|
|
1057
|
+
}
|
|
1058
|
+
if (recvTypeName === "bytes.Reader") {
|
|
1059
|
+
this.emitBytesReaderMethodCall(field, expr, args, targetWasmType);
|
|
1060
|
+
return true;
|
|
1061
|
+
}
|
|
1062
|
+
if (recvTypeName === "time.Time") {
|
|
1063
|
+
this.emitTimeMethodCall(field, expr, args, targetWasmType);
|
|
1064
|
+
return true;
|
|
1065
|
+
}
|
|
1066
|
+
if (recvTypeName === "time.Duration") {
|
|
1067
|
+
this.emitDurationMethodCall(field, expr, args, targetWasmType);
|
|
1068
|
+
return true;
|
|
1069
|
+
}
|
|
1070
|
+
if (recvTypeName === "io.LimitedReader") {
|
|
1071
|
+
this.emitIoLimitedReaderMethodCall(field, expr, args, targetWasmType);
|
|
1072
|
+
return true;
|
|
1073
|
+
}
|
|
1074
|
+
if (recvTypeName === "sync.Mutex" || recvTypeName === "sync.RWMutex") {
|
|
1075
|
+
this.emitSyncMutexMethodCall(field, expr, args, targetWasmType);
|
|
1076
|
+
return true;
|
|
1077
|
+
}
|
|
1078
|
+
if (recvTypeName === "sync.Once") {
|
|
1079
|
+
this.emitSyncOnceMethodCall(field, expr, args, targetWasmType);
|
|
1080
|
+
return true;
|
|
1081
|
+
}
|
|
1082
|
+
if (recvTypeName === "sync.WaitGroup") {
|
|
1083
|
+
this.emitSyncWaitGroupMethodCall(field, expr, args, targetWasmType);
|
|
1084
|
+
return true;
|
|
1085
|
+
}
|
|
1086
|
+
if (
|
|
1087
|
+
recvTypeName === "binary.LittleEndian" ||
|
|
1088
|
+
recvTypeName === "binary.BigEndian" ||
|
|
1089
|
+
recvTypeName === "encoding/binary.LittleEndian" ||
|
|
1090
|
+
recvTypeName === "encoding/binary.BigEndian" ||
|
|
1091
|
+
recvTypeName === "binary.ByteOrder" ||
|
|
1092
|
+
recvTypeName === "encoding/binary.ByteOrder"
|
|
1093
|
+
) {
|
|
1094
|
+
this.emitBinaryOrderMethodCall(
|
|
1095
|
+
recvTypeName,
|
|
1096
|
+
field,
|
|
1097
|
+
expr,
|
|
1098
|
+
args,
|
|
1099
|
+
targetWasmType,
|
|
1100
|
+
);
|
|
1101
|
+
return true;
|
|
1102
|
+
}
|
|
1103
|
+
if (
|
|
1104
|
+
recvTypeName === "base64.Encoding" ||
|
|
1105
|
+
recvTypeName === "encoding/base64.Encoding"
|
|
1106
|
+
) {
|
|
1107
|
+
this.emitBase64EncodingMethodCall(field, expr, args, targetWasmType);
|
|
1108
|
+
return true;
|
|
1109
|
+
}
|
|
1110
|
+
return false;
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
_emitEmbeddedMethodCall(recvTypeName, func, args, recvType) {
|
|
1114
|
+
const structInfo = this.mod.getStructType(recvTypeName);
|
|
1115
|
+
if (!structInfo) return false;
|
|
1116
|
+
for (const embed of structInfo.embeds) {
|
|
1117
|
+
const embedMethodName = `${embed.name}.${func.field}`;
|
|
1118
|
+
const subIdx = this.mod.resolveFuncIndex(embedMethodName);
|
|
1119
|
+
if (subIdx !== null) {
|
|
1120
|
+
const targetParamTypes = this.mod.getFuncParamTypes(embedMethodName);
|
|
1121
|
+
const baseWType = this.toWasmType(recvType);
|
|
1122
|
+
this.emitExpr(func.expr, baseWType);
|
|
1123
|
+
this.pushInstruction({
|
|
1124
|
+
op: "struct.get",
|
|
1125
|
+
typeIndex: structInfo.typeIndex,
|
|
1126
|
+
fieldIndex: embed.fieldIndex,
|
|
1127
|
+
});
|
|
1128
|
+
for (let i = 0; i < args.length; i++) {
|
|
1129
|
+
const pType = targetParamTypes[i + 1] ?? null;
|
|
1130
|
+
this.emitExpr(args[i], pType);
|
|
1131
|
+
}
|
|
1132
|
+
this.pushInstruction({
|
|
1133
|
+
op: "call",
|
|
1134
|
+
funcIndex: subIdx,
|
|
1135
|
+
});
|
|
1136
|
+
return true;
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
return false;
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1006
1142
|
_emitStaticCall(call) {
|
|
1007
1143
|
const { func, args } = call;
|
|
1008
1144
|
if (func.kind !== "Ident") return false;
|
|
@@ -1076,6 +1212,14 @@ export class BuiltinsEmitter {
|
|
|
1076
1212
|
}
|
|
1077
1213
|
|
|
1078
1214
|
emitCallExpr(call, targetWasmType = null) {
|
|
1215
|
+
if (call._isTypeConversion) {
|
|
1216
|
+
this.emitTypeConversion({
|
|
1217
|
+
targetType: call._conversionTargetType,
|
|
1218
|
+
expr: call.args[0],
|
|
1219
|
+
});
|
|
1220
|
+
return;
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1079
1223
|
const deqFunc = this._dequalify(call.func);
|
|
1080
1224
|
if (deqFunc) {
|
|
1081
1225
|
this.emitCallExpr({ ...call, func: deqFunc }, targetWasmType);
|
|
@@ -1090,7 +1234,7 @@ export class BuiltinsEmitter {
|
|
|
1090
1234
|
return;
|
|
1091
1235
|
}
|
|
1092
1236
|
|
|
1093
|
-
if (this._emitMethodCall(call)) {
|
|
1237
|
+
if (this._emitMethodCall(call, targetWasmType)) {
|
|
1094
1238
|
return;
|
|
1095
1239
|
}
|
|
1096
1240
|
|