gofront 1.6.1 → 1.8.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 +45 -49
- package/package.json +12 -8
- package/src/backend/js/expressions.js +1627 -148
- package/src/backend/js/fmt-runtime.js +573 -0
- package/src/backend/js/index.js +508 -102
- package/src/backend/js/runtime.js +1997 -106
- package/src/backend/js/statements.js +931 -87
- package/src/backend/js/stdlib/bits.js +87 -14
- package/src/backend/js/stdlib/builder.js +41 -6
- package/src/backend/js/stdlib/bytes.js +253 -7
- package/src/backend/js/stdlib/errors.js +7 -1
- package/src/backend/js/stdlib/fmt.js +349 -10
- package/src/backend/js/stdlib/index.js +19 -2
- package/src/backend/js/stdlib/json.js +1167 -0
- package/src/backend/js/stdlib/maps.js +0 -8
- package/src/backend/js/stdlib/math.js +17 -1
- package/src/backend/js/stdlib/simd.js +34 -0
- package/src/backend/js/stdlib/slices.js +36 -6
- package/src/backend/js/stdlib/strconv.js +10 -5
- package/src/backend/js/stdlib/strings.js +83 -4
- package/src/backend/js/stdlib/testing.js +7 -0
- package/src/backend/js/stdlib/time.js +79 -55
- package/src/backend/js/stdlib/unicode.js +1 -1
- package/src/backend/js/stdlib/utf8.js +1 -1
- package/src/backend/js/templ.js +38 -76
- package/src/backend/js/type-info.js +272 -0
- package/src/backend/shared/fmt-types.js +188 -0
- package/src/backend/wasm/boundary.js +446 -72
- package/src/backend/wasm/emit-builtins.js +320 -13
- package/src/backend/wasm/emit-exprs.js +1404 -199
- package/src/backend/wasm/emit-maps.js +61 -13
- package/src/backend/wasm/emit-stmts.js +1359 -164
- package/src/backend/wasm/emit.js +518 -74
- package/src/backend/wasm/encode.js +128 -13
- package/src/backend/wasm/globals.js +452 -0
- package/src/backend/wasm/index.js +194 -2489
- package/src/backend/wasm/method-expr.js +381 -0
- package/src/backend/wasm/module-emitter.js +2240 -0
- package/src/backend/wasm/monomorph.js +62 -4
- package/src/backend/wasm/optimize.js +4 -2
- package/src/backend/wasm/prescan.js +397 -0
- package/src/backend/wasm/stdlib/bytes.js +1804 -127
- package/src/backend/wasm/stdlib/core.js +1574 -199
- package/src/backend/wasm/stdlib/index.js +3 -5
- package/src/backend/wasm/stdlib/io.js +1567 -150
- package/src/backend/wasm/stdlib/packages.js +2 -0
- package/src/backend/wasm/stdlib/simd.js +499 -0
- package/src/backend/wasm/stdlib/time.js +1041 -12
- package/src/backend/wasm/types.js +221 -15
- package/src/backend/wasm/variadic.js +93 -0
- package/src/backend/wasm/wat.js +84 -20
- package/src/{cli-core.js → cli/cli-core.js} +236 -10
- package/src/{test-runner.js → cli/test-runner.js} +332 -39
- package/src/{vendor.js → cli/vendor.js} +89 -15
- package/src/compiler.js +107 -269
- package/src/dts-parser.js +71 -14
- package/src/index.js +15 -169
- package/src/{lexer.js → lexer/lexer.js} +102 -55
- package/src/{tokens.js → lexer/tokens.js} +1 -0
- package/src/link/sourcemap.js +97 -0
- package/src/link/stdlib.js +515 -0
- package/src/link/wasm.js +294 -0
- package/src/lower/bounds.js +394 -0
- package/src/lower/boxing.js +1 -6
- package/src/lower/captures.js +176 -32
- package/src/lower/embedding.js +2 -1
- package/src/lower/index.js +26 -18
- package/src/lower/json-codecs.js +1398 -0
- package/src/lower/ownership.js +22 -1
- package/src/lower/range.js +86 -1
- package/src/lower/reachability.js +132 -0
- package/src/parser/declarations.js +58 -13
- package/src/parser/expressions.js +29 -7
- package/src/parser/index.js +2 -1
- package/src/parser/statements.js +37 -22
- package/src/parser/struct-tag.js +144 -0
- package/src/{templ-parser.js → parser/templ.js} +138 -15
- package/src/parser/types.js +130 -13
- package/src/resolver.js +22 -1
- package/src/typechecker/assignability.js +215 -11
- package/src/typechecker/const-fold.js +356 -0
- package/src/typechecker/constants.js +256 -0
- package/src/typechecker/expressions.js +960 -99
- package/src/typechecker/index.js +491 -72
- package/src/typechecker/init-order.js +330 -0
- package/src/typechecker/resolve.js +596 -52
- package/src/typechecker/statements.js +157 -17
- package/src/typechecker/stdlib/core.js +333 -55
- package/src/typechecker/stdlib/excluded.js +898 -0
- package/src/typechecker/stdlib/extended.js +359 -26
- package/src/typechecker/{stdlib.js → stdlib/index.js} +6 -4
- package/src/typechecker/stdlib/simd.js +123 -0
- package/src/typechecker/termination.js +1 -1
- package/src/typechecker/types.js +165 -26
- package/stdlib/bytes/iter.go +192 -0
- package/stdlib/bytes/search.go +240 -0
- package/stdlib/bytes/split.go +218 -0
- package/stdlib/cmp/cmp.go +65 -0
- package/stdlib/encoding/json/errors.go +31 -0
- package/stdlib/encoding/json/indent.go +71 -0
- package/stdlib/encoding/json/number.go +12 -0
- package/stdlib/encoding/json/stream.go +173 -0
- package/stdlib/errors/overlay.go +12 -0
- package/stdlib/hash/hash.go +53 -0
- package/stdlib/internal/jsonrt/dec.go +738 -0
- package/stdlib/internal/jsonrt/enc.go +366 -0
- package/stdlib/internal/jsonrt/indent.go +135 -0
- package/stdlib/internal/jsonrt/number.go +27 -0
- package/stdlib/internal/jsonrt/reader.go +73 -0
- package/stdlib/internal/jsonrt/scanner.go +649 -0
- package/stdlib/io/io.go +346 -0
- package/stdlib/iter/iter.go +18 -0
- package/stdlib/maps/iter.go +65 -0
- package/stdlib/math/extra.go +270 -0
- package/stdlib/slices/extra.go +73 -0
- package/stdlib/slices/iter.go +113 -0
- package/stdlib/sort/sort.go +561 -0
- package/stdlib/strconv/quote.go +341 -0
- package/stdlib/strings/extra.go +35 -0
- package/stdlib/strings/iter.go +122 -0
- package/stdlib/sync/extra.go +105 -0
- package/stdlib/unicode/unicode.go +112 -0
- package/stdlib/unicode/utf8/bytes.go +239 -0
- package/src/backend/wasm/const-fold.js +0 -205
- package/src/backend/wasm/emit-stdlib.js +0 -13
- /package/src/{asset-manager.js → cli/asset-manager.js} +0 -0
- /package/src/{colors.js → cli/colors.js} +0 -0
- /package/src/{dev-server.js → cli/dev-server.js} +0 -0
- /package/src/{minifier.js → cli/minifier.js} +0 -0
- /package/src/{project-config.js → cli/project-config.js} +0 -0
- /package/src/{pwa.js → cli/pwa.js} +0 -0
- /package/src/{templ-lexer.js → lexer/templ-lexer.js} +0 -0
package/src/backend/wasm/emit.js
CHANGED
|
@@ -9,16 +9,12 @@ import { ExprsEmitter } from "./emit-exprs.js";
|
|
|
9
9
|
import { MapsEmitter } from "./emit-maps.js";
|
|
10
10
|
import { SharedEmitter } from "./emit-shared.js";
|
|
11
11
|
import { StmtsEmitter } from "./emit-stmts.js";
|
|
12
|
-
import {
|
|
13
|
-
BytesEmitter,
|
|
14
|
-
ExtendedPackagesEmitter,
|
|
15
|
-
IoEmitter,
|
|
16
|
-
PurePackagesEmitter,
|
|
17
|
-
StdlibCoreEmitter,
|
|
18
|
-
TimeEmitter,
|
|
19
|
-
} from "./stdlib/index.js";
|
|
12
|
+
import { stdlibEmitters } from "./stdlib/index.js";
|
|
20
13
|
import {
|
|
21
14
|
isArrayType,
|
|
15
|
+
isComplex,
|
|
16
|
+
isComplex64,
|
|
17
|
+
isFuncType,
|
|
22
18
|
isPointerToStruct,
|
|
23
19
|
isSliceType,
|
|
24
20
|
isStringType,
|
|
@@ -29,6 +25,36 @@ import {
|
|
|
29
25
|
// The emitter is one class split over several files by concern. Methods from
|
|
30
26
|
// the emit-*.js modules are assigned to FunctionEmitter.prototype below; `this`
|
|
31
27
|
// is shared, so cross-file calls are plain method calls.
|
|
28
|
+
const SCOPED_STMTS = new Set([
|
|
29
|
+
"Block",
|
|
30
|
+
"IfStmt",
|
|
31
|
+
"ForStmt",
|
|
32
|
+
"SwitchStmt",
|
|
33
|
+
"TypeSwitchStmt",
|
|
34
|
+
]);
|
|
35
|
+
|
|
36
|
+
// A same-named method with another arity (strings.Reader.Reset(s) vs
|
|
37
|
+
// hash.Hash.Reset()) cannot satisfy this interface method. Neither can one
|
|
38
|
+
// whose parameters or results have other types (csv.Writer.Write(record
|
|
39
|
+
// []string) error vs io.Writer.Write(p []byte) (int, error)).
|
|
40
|
+
function sameTypes(a, b) {
|
|
41
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function sameParams(c, params, paramTypes) {
|
|
45
|
+
if (c.targetParamTypes.length !== params.length) return false;
|
|
46
|
+
if (paramTypes?.length !== params.length) return true;
|
|
47
|
+
return c.targetParamTypes.every(
|
|
48
|
+
(t, i) => i === 0 || sameTypes(t, paramTypes[i]),
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function sameSignature(c, params, paramTypes, want, got) {
|
|
53
|
+
if (!Array.isArray(c.targetParamTypes)) return true;
|
|
54
|
+
if (!sameParams(c, params, paramTypes)) return false;
|
|
55
|
+
return want === undefined || got === undefined || sameTypes(got, want);
|
|
56
|
+
}
|
|
57
|
+
|
|
32
58
|
export class FunctionEmitter {
|
|
33
59
|
constructor(moduleEmitter, funcDecl, funcIndex) {
|
|
34
60
|
this.mod = moduleEmitter;
|
|
@@ -117,7 +143,75 @@ export class FunctionEmitter {
|
|
|
117
143
|
this.recoverOkLocalIndex = local;
|
|
118
144
|
}
|
|
119
145
|
|
|
146
|
+
_checkYieldClosureReentry() {
|
|
147
|
+
if (!this.funcDecl._isYieldClosure || !this.funcDecl._iterStateName) return;
|
|
148
|
+
const stateLoc = this.resolveLocal(this.funcDecl._iterStateName);
|
|
149
|
+
if (!stateLoc?.isBoxed) return;
|
|
150
|
+
this.pushInstruction({ op: "local.get", index: stateLoc.index });
|
|
151
|
+
this.pushInstruction({
|
|
152
|
+
op: "struct.get",
|
|
153
|
+
typeIndex: stateLoc.boxInfo.typeIndex,
|
|
154
|
+
fieldIndex: 0,
|
|
155
|
+
});
|
|
156
|
+
this.pushInstruction({ op: "i64.const", value: 0n });
|
|
157
|
+
this.pushInstruction("i64.ne");
|
|
158
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
159
|
+
this.emitPanic(
|
|
160
|
+
"range function continued iteration after function for loop body returned false",
|
|
161
|
+
);
|
|
162
|
+
this.pushInstruction("end");
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
_finishYieldClosureBody() {
|
|
166
|
+
if (!this.funcDecl._isYieldClosure) return false;
|
|
167
|
+
const lastOp = this.body[this.body.length - 1]?.op;
|
|
168
|
+
if (lastOp !== "return" && lastOp !== "unreachable") {
|
|
169
|
+
this.pushInstruction({ op: "i32.const", value: 1 });
|
|
170
|
+
this.pushInstruction({ op: "return" });
|
|
171
|
+
}
|
|
172
|
+
return true;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
_collectLabelTargets(body) {
|
|
176
|
+
const targets = new Map();
|
|
177
|
+
let nextTag = 3;
|
|
178
|
+
const scan = (node) => {
|
|
179
|
+
if (!node || typeof node !== "object") return;
|
|
180
|
+
if (Array.isArray(node)) {
|
|
181
|
+
for (const item of node) scan(item);
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
if (node.kind === "LabeledStmt" && node.label) {
|
|
185
|
+
const bTag = nextTag++;
|
|
186
|
+
const cTag = nextTag++;
|
|
187
|
+
targets.set(bTag, { tag: bTag, label: node.label, role: "break" });
|
|
188
|
+
targets.set(cTag, { tag: cTag, label: node.label, role: "continue" });
|
|
189
|
+
} else if (node.kind === "ForStmt" && node.label) {
|
|
190
|
+
const bTag = nextTag++;
|
|
191
|
+
const cTag = nextTag++;
|
|
192
|
+
targets.set(bTag, { tag: bTag, label: node.label, role: "break" });
|
|
193
|
+
targets.set(cTag, { tag: cTag, label: node.label, role: "continue" });
|
|
194
|
+
}
|
|
195
|
+
for (const k of Object.keys(node)) {
|
|
196
|
+
if (k.startsWith("_")) continue;
|
|
197
|
+
scan(node[k]);
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
scan(body);
|
|
201
|
+
return targets;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
_findLabelTarget(label, role) {
|
|
205
|
+
for (const target of this.rootFuncDecl?._labelTargets?.values() ?? []) {
|
|
206
|
+
if (target.label === label && target.role === role) return target;
|
|
207
|
+
}
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
|
|
120
211
|
emitFunctionBody(body) {
|
|
212
|
+
if (!this.funcDecl._isClosure && !this.funcDecl._labelTargets) {
|
|
213
|
+
this.funcDecl._labelTargets = this._collectLabelTargets(body);
|
|
214
|
+
}
|
|
121
215
|
if (
|
|
122
216
|
this.funcDecl._isClosure &&
|
|
123
217
|
!this.funcDecl._isTrampoline &&
|
|
@@ -131,6 +225,7 @@ export class FunctionEmitter {
|
|
|
131
225
|
this._cacheScratchGlobals();
|
|
132
226
|
|
|
133
227
|
this._emitRecoverArmPrologue(body);
|
|
228
|
+
this._checkYieldClosureReentry();
|
|
134
229
|
|
|
135
230
|
const hasDef = hasDefer(body);
|
|
136
231
|
this.hasDefer = hasDef;
|
|
@@ -142,31 +237,139 @@ export class FunctionEmitter {
|
|
|
142
237
|
|
|
143
238
|
this._initNamedReturns();
|
|
144
239
|
|
|
145
|
-
const
|
|
146
|
-
if (needsOobBlock) {
|
|
147
|
-
this.pushInstruction({ op: "block", blockType: "void" });
|
|
148
|
-
this.pushControl("oob");
|
|
149
|
-
}
|
|
240
|
+
const closeChecks = this._openCheckBlocks(body);
|
|
150
241
|
|
|
151
242
|
this.emitBlock(body);
|
|
243
|
+
if (this._finishYieldClosureBody()) {
|
|
244
|
+
closeChecks?.(() => {});
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
152
247
|
|
|
153
|
-
|
|
248
|
+
closeChecks?.(() =>
|
|
249
|
+
this.pushInstruction({
|
|
250
|
+
op: this.returnTypes.length === 0 ? "return" : "unreachable",
|
|
251
|
+
}),
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Opens the nil and index-check blocks `body` needs and returns the
|
|
256
|
+
// function that closes them (null when none are needed). The closer runs
|
|
257
|
+
// `terminate` first unless the body already ends in a return or trap.
|
|
258
|
+
_openCheckBlocks(body) {
|
|
259
|
+
const nil = this._needsNilBlock(body);
|
|
260
|
+
const oob = this._needsOobBlock(body);
|
|
261
|
+
if (!nil && !oob) return null;
|
|
262
|
+
if (nil) this._openNilBlock();
|
|
263
|
+
if (oob) this._openOobBlocks();
|
|
264
|
+
return (terminate) => {
|
|
154
265
|
const lastOp = this.body[this.body.length - 1]?.op;
|
|
155
|
-
if (lastOp !== "return" && lastOp !== "unreachable")
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
266
|
+
if (lastOp !== "return" && lastOp !== "unreachable") terminate();
|
|
267
|
+
if (oob) this._closeOobBlocks();
|
|
268
|
+
if (nil) this._closeNilBlock();
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Field access through a nil struct pointer branches (`br_on_null`) out
|
|
273
|
+
// of the body to a block wrapped around it, whose handler raises Go's
|
|
274
|
+
// panic, so `recover()` sees it where a trap would skip the defers.
|
|
275
|
+
_openNilBlock() {
|
|
276
|
+
this.pushInstruction({ op: "block", blockType: "void" });
|
|
277
|
+
this.pushControl("nil");
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Closes the block opened by _openNilBlock; the body must already end in
|
|
281
|
+
// a branch, return or unreachable.
|
|
282
|
+
_closeNilBlock() {
|
|
283
|
+
this.pushInstruction("end");
|
|
284
|
+
this.pushInstruction({
|
|
285
|
+
op: "call",
|
|
286
|
+
funcIndex: this.mod.getNilPanicFuncIndex(),
|
|
287
|
+
});
|
|
288
|
+
this.pushInstruction("unreachable");
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Index checks branch out of the body to one of two blocks wrapped around
|
|
292
|
+
// it, `oob32` for i32 indexes and `oob64` for i64 indexes, carrying the
|
|
293
|
+
// index and the check's site id as block results (`_pushOobSite`). The
|
|
294
|
+
// handlers after the blocks recompute that site's length and raise Go's
|
|
295
|
+
// `index out of range [i] with length n`. Nothing extra stays live in
|
|
296
|
+
// the hot path: a site id is an immediate.
|
|
297
|
+
_openOobBlocks() {
|
|
298
|
+
this.pushInstruction({
|
|
299
|
+
op: "block",
|
|
300
|
+
blockType: this.mod.getTypeIndex([], ["i64", "i32"]),
|
|
301
|
+
});
|
|
302
|
+
this.pushControl("oob64");
|
|
303
|
+
this.pushInstruction({
|
|
304
|
+
op: "block",
|
|
305
|
+
blockType: this.mod.getTypeIndex([], ["i32", "i32"]),
|
|
306
|
+
});
|
|
307
|
+
this.pushControl("oob32");
|
|
308
|
+
}
|
|
163
309
|
|
|
310
|
+
// Closes the blocks opened by _openOobBlocks; the body must already end
|
|
311
|
+
// in a branch, return or unreachable.
|
|
312
|
+
_closeOobBlocks() {
|
|
313
|
+
const site = this.acquireTemp("i32");
|
|
314
|
+
const idx = this.acquireTemp("i64");
|
|
315
|
+
const len = this.acquireTemp("i32");
|
|
316
|
+
// oob32 lands here with [i32 index, site]: widen the index and fall
|
|
317
|
+
// through to the oob64 handler.
|
|
318
|
+
this.pushInstruction("end");
|
|
319
|
+
this.pushInstruction({ op: "local.set", index: site });
|
|
320
|
+
this.pushInstruction("i64.extend_i32_s");
|
|
321
|
+
this.pushInstruction({ op: "local.get", index: site });
|
|
322
|
+
this.pushInstruction("end");
|
|
323
|
+
this.pushInstruction({ op: "local.set", index: site });
|
|
324
|
+
this.pushInstruction({ op: "local.set", index: idx });
|
|
325
|
+
// Sites whose length code is identical (same temp, same field) share
|
|
326
|
+
// one br_table target.
|
|
327
|
+
const codes = [];
|
|
328
|
+
const target = (this.oobSites ?? []).map((emitLen) => {
|
|
329
|
+
const saved = this.body;
|
|
330
|
+
this.body = [];
|
|
331
|
+
emitLen();
|
|
332
|
+
const code = this.body;
|
|
333
|
+
this.body = saved;
|
|
334
|
+
const key = JSON.stringify(code);
|
|
335
|
+
let k = codes.findIndex((c) => c.key === key);
|
|
336
|
+
if (k < 0) k = codes.push({ key, code }) - 1;
|
|
337
|
+
return k;
|
|
338
|
+
});
|
|
339
|
+
const sites = codes.map(({ code }) => () => {
|
|
340
|
+
for (const inst of code) this.pushInstruction(inst);
|
|
341
|
+
});
|
|
342
|
+
if (sites.length === 0) {
|
|
343
|
+
this.pushInstruction({ op: "i32.const", value: 0 });
|
|
344
|
+
} else {
|
|
345
|
+
// block $len (result i32) { block … block { br_table } emitLen_0; br $len … }
|
|
346
|
+
this.pushInstruction({ op: "block", blockType: "i32" });
|
|
347
|
+
for (let k = 0; k < sites.length; k++)
|
|
348
|
+
this.pushInstruction({ op: "block", blockType: "void" });
|
|
349
|
+
this.pushInstruction({ op: "local.get", index: site });
|
|
164
350
|
this.pushInstruction({
|
|
165
|
-
op: "
|
|
166
|
-
|
|
351
|
+
op: "br_table",
|
|
352
|
+
targets: target,
|
|
353
|
+
defaultTarget: sites.length - 1,
|
|
167
354
|
});
|
|
168
|
-
|
|
355
|
+
for (let k = 0; k < sites.length; k++) {
|
|
356
|
+
this.pushInstruction("end");
|
|
357
|
+
sites[k]();
|
|
358
|
+
this.pushInstruction({ op: "br", depth: sites.length - 1 - k });
|
|
359
|
+
}
|
|
360
|
+
this.pushInstruction("end");
|
|
169
361
|
}
|
|
362
|
+
this.pushInstruction({ op: "local.set", index: len });
|
|
363
|
+
this.pushInstruction({ op: "local.get", index: idx });
|
|
364
|
+
this.pushInstruction({ op: "local.get", index: len });
|
|
365
|
+
this.pushInstruction({
|
|
366
|
+
op: "call",
|
|
367
|
+
funcIndex: this.mod.getIndexPanicFuncIndex(),
|
|
368
|
+
});
|
|
369
|
+
this.pushInstruction("unreachable");
|
|
370
|
+
this.releaseTemp(site, "i32");
|
|
371
|
+
this.releaseTemp(idx, "i64");
|
|
372
|
+
this.releaseTemp(len, "i32");
|
|
170
373
|
}
|
|
171
374
|
|
|
172
375
|
_initNamedReturns() {
|
|
@@ -179,13 +382,6 @@ export class FunctionEmitter {
|
|
|
179
382
|
const varName = isBlank ? `__ret_blank$${i}` : r.name;
|
|
180
383
|
const wType = this.toWasmType(r.type);
|
|
181
384
|
const localIdx = this.allocLocal(varName, wType, r.type, isBlank);
|
|
182
|
-
this.namedReturnVars.push({
|
|
183
|
-
name: varName,
|
|
184
|
-
localIdx,
|
|
185
|
-
type: wType,
|
|
186
|
-
goType: r.type,
|
|
187
|
-
isBlank,
|
|
188
|
-
});
|
|
189
385
|
this.emitZeroValue(r.type, wType);
|
|
190
386
|
const localInfo = this.locals.get(varName);
|
|
191
387
|
if (localInfo?.isBoxed) {
|
|
@@ -195,6 +391,13 @@ export class FunctionEmitter {
|
|
|
195
391
|
});
|
|
196
392
|
}
|
|
197
393
|
this.pushInstruction({ op: "local.set", index: localIdx });
|
|
394
|
+
this.namedReturnVars.push({
|
|
395
|
+
name: varName,
|
|
396
|
+
localIdx,
|
|
397
|
+
type: wType,
|
|
398
|
+
goType: r.type,
|
|
399
|
+
isBlank,
|
|
400
|
+
});
|
|
198
401
|
}
|
|
199
402
|
return true;
|
|
200
403
|
}
|
|
@@ -255,30 +458,16 @@ export class FunctionEmitter {
|
|
|
255
458
|
});
|
|
256
459
|
this.pushControl("try_table");
|
|
257
460
|
|
|
258
|
-
const
|
|
259
|
-
if (needsOobBlock) {
|
|
260
|
-
this.pushInstruction({ op: "block", blockType: "void" });
|
|
261
|
-
this.pushControl("oob");
|
|
262
|
-
}
|
|
461
|
+
const closeChecks = this._openCheckBlocks(body);
|
|
263
462
|
|
|
264
463
|
this.emitBlock(body);
|
|
265
464
|
|
|
266
|
-
|
|
267
|
-
const lastOp = this.body[this.body.length - 1]?.op;
|
|
268
|
-
if (lastOp !== "return" && lastOp !== "unreachable") {
|
|
269
|
-
this.pushInstruction({
|
|
270
|
-
op: "br",
|
|
271
|
-
depth: this.resolveBranchDepthToRole("runDefers"),
|
|
272
|
-
});
|
|
273
|
-
}
|
|
274
|
-
this.pushInstruction("end");
|
|
275
|
-
|
|
465
|
+
closeChecks?.(() =>
|
|
276
466
|
this.pushInstruction({
|
|
277
|
-
op: "
|
|
278
|
-
|
|
279
|
-
})
|
|
280
|
-
|
|
281
|
-
}
|
|
467
|
+
op: "br",
|
|
468
|
+
depth: this.resolveBranchDepthToRole("runDefers"),
|
|
469
|
+
}),
|
|
470
|
+
);
|
|
282
471
|
|
|
283
472
|
// Close try_table
|
|
284
473
|
this.pushInstruction("end");
|
|
@@ -574,6 +763,52 @@ export class FunctionEmitter {
|
|
|
574
763
|
return false;
|
|
575
764
|
}
|
|
576
765
|
|
|
766
|
+
// True when the body reads or writes a field or the target of a pointer.
|
|
767
|
+
_needsNilBlock(node) {
|
|
768
|
+
if (!node || typeof node !== "object") return false;
|
|
769
|
+
if (Array.isArray(node)) return node.some((n) => this._needsNilBlock(n));
|
|
770
|
+
if (node.kind === "FuncLit") return false;
|
|
771
|
+
if (node.kind === "SelectorExpr" && node.expr?._type?.kind === "pointer")
|
|
772
|
+
return true;
|
|
773
|
+
if (this._isNilTrapSite(node)) return true;
|
|
774
|
+
for (const key of Object.keys(node)) {
|
|
775
|
+
if (key.startsWith("_")) continue;
|
|
776
|
+
if (this._needsNilBlock(node[key])) return true;
|
|
777
|
+
}
|
|
778
|
+
return false;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
// A pointer dereference or a call through a func value: sites whose nil
|
|
782
|
+
// operand raises Go's panic through the `nil` block.
|
|
783
|
+
_isNilTrapSite(node) {
|
|
784
|
+
if (node.kind === "CallExpr") return this._isFuncValueCall(node);
|
|
785
|
+
const star =
|
|
786
|
+
(node.kind === "UnaryExpr" && node.op === "*") ||
|
|
787
|
+
node.kind === "StarExpr";
|
|
788
|
+
return star && (node.operand ?? node.expr)?._type?.kind === "pointer";
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
// A call through a func variable, parameter or expression (not a declared
|
|
792
|
+
// function or a method), which panics when the value is nil.
|
|
793
|
+
_isFuncValueCall(call) {
|
|
794
|
+
const f = call.func;
|
|
795
|
+
if (!f || call._isTypeConversion) return false;
|
|
796
|
+
if (f.kind === "Ident")
|
|
797
|
+
return (
|
|
798
|
+
isFuncType(f._type, this.mod.checker) &&
|
|
799
|
+
this.mod.resolveFuncIndex(f.name) === null
|
|
800
|
+
);
|
|
801
|
+
if (f.kind === "SelectorExpr") {
|
|
802
|
+
const t = f.expr?._type;
|
|
803
|
+
const base = t?.kind === "pointer" ? t.base : t;
|
|
804
|
+
const fields = (base?.underlying ?? base)?.fields;
|
|
805
|
+
return fields instanceof Map && fields.has(f.field);
|
|
806
|
+
}
|
|
807
|
+
return (
|
|
808
|
+
f.kind === "IndexExpr" || f.kind === "CallExpr" || f.kind === "ParenExpr"
|
|
809
|
+
);
|
|
810
|
+
}
|
|
811
|
+
|
|
577
812
|
_needsOobBlock(node) {
|
|
578
813
|
if (!node || typeof node !== "object") return false;
|
|
579
814
|
if (Array.isArray(node)) {
|
|
@@ -639,6 +874,10 @@ export class FunctionEmitter {
|
|
|
639
874
|
for (let i = 0; i < lhs.length; i++) {
|
|
640
875
|
if (lhs[i].name === varName) {
|
|
641
876
|
if (lhs[i]._type) return lhs[i]._type;
|
|
877
|
+
// `for k, v := range x`: the range expression is a tuple.
|
|
878
|
+
const rt = node.rhs?.[0]?._type;
|
|
879
|
+
if (node.rhs?.[0]?.kind === "RangeExpr" && rt?.kind === "tuple")
|
|
880
|
+
return rt.types?.[i] ?? null;
|
|
642
881
|
if (node.rhs?.[i]?._type) return node.rhs[i]._type;
|
|
643
882
|
}
|
|
644
883
|
}
|
|
@@ -867,7 +1106,7 @@ export class FunctionEmitter {
|
|
|
867
1106
|
for (const n of names) rawFields.push({ name: n, goType: f.type });
|
|
868
1107
|
}
|
|
869
1108
|
}
|
|
870
|
-
if (
|
|
1109
|
+
if (this.mod.getAnonStructType) {
|
|
871
1110
|
return this.mod.getAnonStructType(rawFields);
|
|
872
1111
|
}
|
|
873
1112
|
}
|
|
@@ -881,7 +1120,13 @@ export class FunctionEmitter {
|
|
|
881
1120
|
return [{ structInfo, fieldIndex: idx, field: structInfo.fields[idx] }];
|
|
882
1121
|
}
|
|
883
1122
|
for (const embed of structInfo.embeds) {
|
|
884
|
-
|
|
1123
|
+
// A generic instance is registered under its specialised name
|
|
1124
|
+
// (`Inner$int`), which the field's type carries.
|
|
1125
|
+
const ft = structInfo.fields[embed.fieldIndex]?.goType;
|
|
1126
|
+
const instName = (ft?.kind === "PointerType" ? ft.base : ft)?.name;
|
|
1127
|
+
const embedInfo =
|
|
1128
|
+
(instName && this.mod.structTypes.get(instName)) ??
|
|
1129
|
+
this.mod.structTypes.get(embed.name);
|
|
885
1130
|
if (embedInfo) {
|
|
886
1131
|
const sub = this._resolveFieldPath(embedInfo, fieldName);
|
|
887
1132
|
if (sub) {
|
|
@@ -899,7 +1144,7 @@ export class FunctionEmitter {
|
|
|
899
1144
|
return null;
|
|
900
1145
|
}
|
|
901
1146
|
|
|
902
|
-
emitCloneStruct(structInfo, baseWType) {
|
|
1147
|
+
emitCloneStruct(structInfo, baseWType, dstInfo = structInfo) {
|
|
903
1148
|
const tmp = this.acquireTemp(baseWType);
|
|
904
1149
|
this.pushInstruction({ op: "local.set", index: tmp });
|
|
905
1150
|
for (let i = 0; i < structInfo.fields.length; i++) {
|
|
@@ -909,18 +1154,117 @@ export class FunctionEmitter {
|
|
|
909
1154
|
typeIndex: structInfo.typeIndex,
|
|
910
1155
|
fieldIndex: i,
|
|
911
1156
|
});
|
|
1157
|
+
// Array and value-struct fields are values: copy them with the struct.
|
|
1158
|
+
const f = structInfo.fields[i];
|
|
1159
|
+
if (!f.goType || f.goType.kind === "pointer") continue;
|
|
1160
|
+
if (isArrayType(f.goType, this.mod.checker)) {
|
|
1161
|
+
this.emitCopyArray(f.goType);
|
|
1162
|
+
} else if (
|
|
1163
|
+
isStructType(f.goType, this.mod.checker, this.mod) &&
|
|
1164
|
+
!isPointerToStruct(f.goType, this.mod.checker, this.mod)
|
|
1165
|
+
) {
|
|
1166
|
+
const inner = this._resolveStructInfo({ _type: f.goType });
|
|
1167
|
+
if (inner) this.emitCloneStruct(inner, f.wType);
|
|
1168
|
+
}
|
|
912
1169
|
}
|
|
913
1170
|
this.pushInstruction({
|
|
914
1171
|
op: "struct.new",
|
|
915
|
-
typeIndex:
|
|
1172
|
+
typeIndex: dstInfo.typeIndex,
|
|
916
1173
|
});
|
|
917
1174
|
this.releaseTemp(tmp, baseWType);
|
|
918
1175
|
}
|
|
919
1176
|
|
|
920
|
-
//
|
|
921
|
-
//
|
|
922
|
-
// (
|
|
923
|
-
|
|
1177
|
+
// Copies the fixed-size array on top of the stack into a fresh array.
|
|
1178
|
+
// Elements that are themselves arrays or value structs are copied deeply.
|
|
1179
|
+
// A null source (an array slot that was never written, e.g. an element of
|
|
1180
|
+
// `make([][2]int, n)`) yields the zero array, so every copy is non-null.
|
|
1181
|
+
emitCopyArray(goType) {
|
|
1182
|
+
const elemType = this._getArrayElemType(goType);
|
|
1183
|
+
const arrInfo = this.mod.getArrayType(elemType);
|
|
1184
|
+
const arrWType = {
|
|
1185
|
+
kind: "ref",
|
|
1186
|
+
nullable: true,
|
|
1187
|
+
typeIndex: arrInfo.typeIndex,
|
|
1188
|
+
};
|
|
1189
|
+
const src = this.acquireTemp(arrWType);
|
|
1190
|
+
const dst = this.acquireTemp(arrWType);
|
|
1191
|
+
this.pushInstruction({ op: "local.tee", index: src });
|
|
1192
|
+
this.pushInstruction("ref.is_null");
|
|
1193
|
+
this.pushInstruction({ op: "if", blockType: arrWType });
|
|
1194
|
+
this.emitZeroValue(goType, arrWType);
|
|
1195
|
+
this.pushInstruction("else");
|
|
1196
|
+
this.pushInstruction({ op: "local.get", index: src });
|
|
1197
|
+
this.pushInstruction("array.len");
|
|
1198
|
+
this.pushInstruction({
|
|
1199
|
+
op: "array.new_default",
|
|
1200
|
+
typeIndex: arrInfo.typeIndex,
|
|
1201
|
+
});
|
|
1202
|
+
this.pushInstruction({ op: "local.set", index: dst });
|
|
1203
|
+
const elemIsArray =
|
|
1204
|
+
elemType?.kind !== "pointer" && isArrayType(elemType, this.mod.checker);
|
|
1205
|
+
const elemStruct =
|
|
1206
|
+
!elemIsArray &&
|
|
1207
|
+
isStructType(elemType, this.mod.checker, this.mod) &&
|
|
1208
|
+
!isPointerToStruct(elemType, this.mod.checker, this.mod)
|
|
1209
|
+
? (this._resolveStructInfo({ _type: elemType }) ??
|
|
1210
|
+
this.mod.getStructType(elemType?.name))
|
|
1211
|
+
: null;
|
|
1212
|
+
if (elemIsArray || elemStruct) {
|
|
1213
|
+
const i = this.acquireTemp("i32");
|
|
1214
|
+
this.pushInstruction({ op: "i32.const", value: 0 });
|
|
1215
|
+
this.pushInstruction({ op: "local.set", index: i });
|
|
1216
|
+
this.pushInstruction({ op: "block", blockType: "void" });
|
|
1217
|
+
this.pushInstruction({ op: "loop", blockType: "void" });
|
|
1218
|
+
this.pushInstruction({ op: "local.get", index: i });
|
|
1219
|
+
this.pushInstruction({ op: "local.get", index: dst });
|
|
1220
|
+
this.pushInstruction("array.len");
|
|
1221
|
+
this.pushInstruction("i32.ge_u");
|
|
1222
|
+
this.pushInstruction({ op: "br_if", depth: 1 });
|
|
1223
|
+
this.pushInstruction({ op: "local.get", index: dst });
|
|
1224
|
+
this.pushInstruction({ op: "local.get", index: i });
|
|
1225
|
+
this.pushInstruction({ op: "local.get", index: src });
|
|
1226
|
+
this.pushInstruction({ op: "local.get", index: i });
|
|
1227
|
+
this.pushInstruction({ op: "array.get", typeIndex: arrInfo.typeIndex });
|
|
1228
|
+
if (elemIsArray) this.emitCopyArray(elemType);
|
|
1229
|
+
else this.emitCloneStruct(elemStruct, arrInfo.elemWType);
|
|
1230
|
+
this.pushInstruction({ op: "array.set", typeIndex: arrInfo.typeIndex });
|
|
1231
|
+
this.pushInstruction({ op: "local.get", index: i });
|
|
1232
|
+
this.pushInstruction({ op: "i32.const", value: 1 });
|
|
1233
|
+
this.pushInstruction("i32.add");
|
|
1234
|
+
this.pushInstruction({ op: "local.set", index: i });
|
|
1235
|
+
this.pushInstruction({ op: "br", depth: 0 });
|
|
1236
|
+
this.pushInstruction("end");
|
|
1237
|
+
this.pushInstruction("end");
|
|
1238
|
+
this.releaseTemp(i, "i32");
|
|
1239
|
+
} else {
|
|
1240
|
+
this.pushInstruction({ op: "local.get", index: dst });
|
|
1241
|
+
this.pushInstruction({ op: "i32.const", value: 0 });
|
|
1242
|
+
this.pushInstruction({ op: "local.get", index: src });
|
|
1243
|
+
this.pushInstruction({ op: "i32.const", value: 0 });
|
|
1244
|
+
this.pushInstruction({ op: "local.get", index: src });
|
|
1245
|
+
this.pushInstruction("array.len");
|
|
1246
|
+
this.pushInstruction({
|
|
1247
|
+
op: "array.copy",
|
|
1248
|
+
typeIndex: arrInfo.typeIndex,
|
|
1249
|
+
});
|
|
1250
|
+
}
|
|
1251
|
+
this.pushInstruction({ op: "local.get", index: dst });
|
|
1252
|
+
this.pushInstruction("end");
|
|
1253
|
+
this.releaseTemp(dst, arrWType);
|
|
1254
|
+
this.releaseTemp(src, arrWType);
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
// Go copies structs and arrays on assignment. After `node` has been
|
|
1258
|
+
// emitted (value on the stack), copy it unless it is already a fresh
|
|
1259
|
+
// temporary (composite literal, call result, or a struct `*p` deref) or not
|
|
1260
|
+
// a value type at all.
|
|
1261
|
+
_emitCopyIfValue(node, goType, wType) {
|
|
1262
|
+
if (goType?.kind !== "pointer" && isArrayType(goType, this.mod.checker)) {
|
|
1263
|
+
if (node.kind === "CompositeLit" || node._freshArray) return;
|
|
1264
|
+
if (node.kind === "CallExpr" && !node._isTypeConversion) return;
|
|
1265
|
+
this.emitCopyArray(goType);
|
|
1266
|
+
return;
|
|
1267
|
+
}
|
|
924
1268
|
if (
|
|
925
1269
|
!isStructType(goType, this.mod.checker, this.mod) ||
|
|
926
1270
|
isPointerToStruct(goType, this.mod.checker, this.mod)
|
|
@@ -958,6 +1302,12 @@ export class FunctionEmitter {
|
|
|
958
1302
|
if (recvType) {
|
|
959
1303
|
let t = recvType;
|
|
960
1304
|
while (t.kind === "pointer") t = t.base;
|
|
1305
|
+
if (t.kind === "SelectorExpr") {
|
|
1306
|
+
const pkg = t.expr?.name;
|
|
1307
|
+
if (pkg) return `${pkg}.${t.field}`;
|
|
1308
|
+
}
|
|
1309
|
+
if (t.kind === "simd")
|
|
1310
|
+
return t.simd === "Mask32x4" ? "simd.Mask32x4" : "simd.F32x4";
|
|
961
1311
|
if (t.name) return t.name;
|
|
962
1312
|
if (t.kind === "named") return t.name;
|
|
963
1313
|
if (t.kind === "TypeName" || t.kind === "Ident") return t.name;
|
|
@@ -975,11 +1325,23 @@ export class FunctionEmitter {
|
|
|
975
1325
|
return null;
|
|
976
1326
|
}
|
|
977
1327
|
|
|
978
|
-
|
|
979
|
-
|
|
1328
|
+
// Concrete methods the dispatcher `fn` can call: same name, same signature.
|
|
1329
|
+
_dispatchCandidates(fn) {
|
|
1330
|
+
const candidates = this.mod.findInterfaceCandidates(
|
|
980
1331
|
fn._ifaceType,
|
|
981
1332
|
fn._methodName,
|
|
982
1333
|
);
|
|
1334
|
+
if (!Array.isArray(fn.params)) return candidates;
|
|
1335
|
+
const paramTypes = this.mod.getFuncParamTypes(fn.name);
|
|
1336
|
+
const results = this.mod.funcReturnTypesByIdx;
|
|
1337
|
+
const want = results.get(fn._globalFuncIndex);
|
|
1338
|
+
return candidates.filter((c) =>
|
|
1339
|
+
sameSignature(c, fn.params, paramTypes, want, results.get(c.funcIndex)),
|
|
1340
|
+
);
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
emitInterfaceDispatcher(fn) {
|
|
1344
|
+
let candidates = this._dispatchCandidates(fn);
|
|
983
1345
|
const recvLocal = 0; // __recv is local 0
|
|
984
1346
|
// Soft dispatchers (errors.Unwrap) return nil instead of panicking when
|
|
985
1347
|
// the receiver is nil or has no such method.
|
|
@@ -1025,6 +1387,8 @@ export class FunctionEmitter {
|
|
|
1025
1387
|
this.pushInstruction("i32.eqz");
|
|
1026
1388
|
this.pushInstruction({ op: "br_if", depth: 0 });
|
|
1027
1389
|
|
|
1390
|
+
this._emitNilPtrBoxPanic(cand, recvLocal);
|
|
1391
|
+
|
|
1028
1392
|
// Matched! Cast receiver
|
|
1029
1393
|
this.pushInstruction({ op: "local.get", index: recvLocal });
|
|
1030
1394
|
this.pushInstruction({
|
|
@@ -1040,6 +1404,12 @@ export class FunctionEmitter {
|
|
|
1040
1404
|
});
|
|
1041
1405
|
if (cand.embedPath) {
|
|
1042
1406
|
for (const step of cand.embedPath) {
|
|
1407
|
+
if (step.castTypeIndex) {
|
|
1408
|
+
this.pushInstruction({
|
|
1409
|
+
op: "ref.cast",
|
|
1410
|
+
typeIndex: step.castTypeIndex,
|
|
1411
|
+
});
|
|
1412
|
+
}
|
|
1043
1413
|
this.pushInstruction({
|
|
1044
1414
|
op: "struct.get",
|
|
1045
1415
|
typeIndex: step.parentTypeIndex,
|
|
@@ -1075,6 +1445,28 @@ export class FunctionEmitter {
|
|
|
1075
1445
|
emitMiss("interface conversion: nil or unmatched type for method call");
|
|
1076
1446
|
}
|
|
1077
1447
|
|
|
1448
|
+
// A value-receiver method reached through a pointer box reads the pointee
|
|
1449
|
+
// first, so a nil pointer inside the interface is a catchable panic.
|
|
1450
|
+
_emitNilPtrBoxPanic(cand, recvLocal) {
|
|
1451
|
+
if (!cand.nilPtrPanic) return;
|
|
1452
|
+
this.pushInstruction({ op: "local.get", index: recvLocal });
|
|
1453
|
+
this.pushInstruction({
|
|
1454
|
+
op: "ref.cast_null",
|
|
1455
|
+
typeIndex: cand.testTypeIndex,
|
|
1456
|
+
});
|
|
1457
|
+
this.pushInstruction({
|
|
1458
|
+
op: "struct.get",
|
|
1459
|
+
typeIndex: cand.testTypeIndex,
|
|
1460
|
+
fieldIndex: 0,
|
|
1461
|
+
});
|
|
1462
|
+
this.pushInstruction("ref.is_null");
|
|
1463
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
1464
|
+
this.emitPanic(
|
|
1465
|
+
"runtime error: invalid memory address or nil pointer dereference",
|
|
1466
|
+
);
|
|
1467
|
+
this.pushInstruction("end");
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1078
1470
|
_getArraySize(t, defaultSize = 0) {
|
|
1079
1471
|
if (!t) return defaultSize;
|
|
1080
1472
|
if (typeof t.size === "number") return t.size;
|
|
@@ -1082,6 +1474,10 @@ export class FunctionEmitter {
|
|
|
1082
1474
|
if (typeof t.len === "number") return t.len;
|
|
1083
1475
|
if (t.len?.value !== undefined) return Number(t.len.value);
|
|
1084
1476
|
if (t.underlying) return this._getArraySize(t.underlying, defaultSize);
|
|
1477
|
+
if (t.name && this.mod.checker?.types?.has(t.name)) {
|
|
1478
|
+
const resolved = this.mod.checker.types.get(t.name);
|
|
1479
|
+
if (resolved !== t) return this._getArraySize(resolved, defaultSize);
|
|
1480
|
+
}
|
|
1085
1481
|
return defaultSize;
|
|
1086
1482
|
}
|
|
1087
1483
|
|
|
@@ -1106,11 +1502,29 @@ export class FunctionEmitter {
|
|
|
1106
1502
|
this.pushInstruction({ op: "f32.const", value: 0.0 });
|
|
1107
1503
|
} else if (wType === "f64") {
|
|
1108
1504
|
this.pushInstruction({ op: "f64.const", value: 0.0 });
|
|
1505
|
+
} else if (wType === "v128") {
|
|
1506
|
+
this.pushInstruction({ op: "v128.const", bytes: new Array(16).fill(0) });
|
|
1109
1507
|
} else if (
|
|
1110
1508
|
typeof wType === "object" &&
|
|
1111
1509
|
wType !== null &&
|
|
1112
1510
|
wType.kind === "ref"
|
|
1113
1511
|
) {
|
|
1512
|
+
if (
|
|
1513
|
+
(goType && isComplex(goType)) ||
|
|
1514
|
+
wType.typeName === "$complex64" ||
|
|
1515
|
+
wType.typeName === "$complex128"
|
|
1516
|
+
) {
|
|
1517
|
+
const is64 = isComplex64(goType) || wType.typeName === "$complex64";
|
|
1518
|
+
const info = this.mod.getComplexType(is64 ? "complex64" : "complex128");
|
|
1519
|
+
this.pushInstruction({ op: `${info.elemWType}.const`, value: 0.0 });
|
|
1520
|
+
this.pushInstruction({ op: `${info.elemWType}.const`, value: 0.0 });
|
|
1521
|
+
this.pushInstruction({
|
|
1522
|
+
op: "struct.new",
|
|
1523
|
+
typeIndex: info.typeIndex,
|
|
1524
|
+
typeName: info.name,
|
|
1525
|
+
});
|
|
1526
|
+
return;
|
|
1527
|
+
}
|
|
1114
1528
|
if (
|
|
1115
1529
|
goType &&
|
|
1116
1530
|
goType.kind !== "pointer" &&
|
|
@@ -1118,7 +1532,10 @@ export class FunctionEmitter {
|
|
|
1118
1532
|
) {
|
|
1119
1533
|
const sName =
|
|
1120
1534
|
goType.name ?? (goType.kind === "named" ? goType.name : null);
|
|
1121
|
-
|
|
1535
|
+
// An anonymous struct type has no name to look up.
|
|
1536
|
+
const structInfo =
|
|
1537
|
+
this.mod.getStructType(sName) ??
|
|
1538
|
+
this._resolveStructInfo({ _type: goType });
|
|
1122
1539
|
if (structInfo) {
|
|
1123
1540
|
for (const f of structInfo.fields) {
|
|
1124
1541
|
this.emitZeroValue(f.goType, f.wType);
|
|
@@ -1135,10 +1552,15 @@ export class FunctionEmitter {
|
|
|
1135
1552
|
goType.kind !== "pointer" &&
|
|
1136
1553
|
isArrayType(goType, this.mod.checker)
|
|
1137
1554
|
) {
|
|
1138
|
-
const elemType = goType
|
|
1555
|
+
const elemType = this._getArrayElemType(goType);
|
|
1139
1556
|
const arrInfo = this.mod.getArrayType(elemType);
|
|
1140
1557
|
const size = this._getArraySize(goType);
|
|
1141
|
-
if (
|
|
1558
|
+
if (
|
|
1559
|
+
isStructType(elemType, this.mod.checker, this.mod) ||
|
|
1560
|
+
isComplex(elemType) ||
|
|
1561
|
+
(elemType?.kind !== "pointer" &&
|
|
1562
|
+
isArrayType(elemType, this.mod.checker))
|
|
1563
|
+
) {
|
|
1142
1564
|
for (let i = 0; i < size; i++) {
|
|
1143
1565
|
this.emitZeroValue(elemType, arrInfo.elemWType);
|
|
1144
1566
|
}
|
|
@@ -1278,8 +1700,9 @@ export class FunctionEmitter {
|
|
|
1278
1700
|
return 0;
|
|
1279
1701
|
}
|
|
1280
1702
|
|
|
1281
|
-
|
|
1282
|
-
|
|
1703
|
+
// Depth of the index-check block for i32 or i64 indexes, or null.
|
|
1704
|
+
resolveBranchDepthToOob(width = 64) {
|
|
1705
|
+
return this.resolveBranchDepthToRole(width === 32 ? "oob32" : "oob64");
|
|
1283
1706
|
}
|
|
1284
1707
|
|
|
1285
1708
|
resolveBranchDepthToRole(targetRole) {
|
|
@@ -1300,17 +1723,43 @@ export class FunctionEmitter {
|
|
|
1300
1723
|
}
|
|
1301
1724
|
}
|
|
1302
1725
|
|
|
1726
|
+
// Go scopes: a block, `if`, `for` and `switch` (with their init
|
|
1727
|
+
// statements) end the names they declare, so an inner `x := 5` does not
|
|
1728
|
+
// rebind the outer `x` for the code after it.
|
|
1303
1729
|
emitStmt(stmt) {
|
|
1304
1730
|
if (!stmt) return;
|
|
1731
|
+
if (!SCOPED_STMTS.has(stmt.kind)) {
|
|
1732
|
+
this._emitStmtKind(stmt);
|
|
1733
|
+
return;
|
|
1734
|
+
}
|
|
1735
|
+
const saved = new Map(this.locals);
|
|
1736
|
+
try {
|
|
1737
|
+
this._emitStmtKind(stmt);
|
|
1738
|
+
} finally {
|
|
1739
|
+
this.locals = saved;
|
|
1740
|
+
}
|
|
1741
|
+
}
|
|
1742
|
+
|
|
1743
|
+
_emitStmtKind(stmt) {
|
|
1305
1744
|
switch (stmt.kind) {
|
|
1306
1745
|
case "Block":
|
|
1307
1746
|
this.emitBlock(stmt);
|
|
1308
1747
|
break;
|
|
1309
1748
|
|
|
1310
1749
|
case "VarDecl":
|
|
1750
|
+
case "ConstDecl":
|
|
1311
1751
|
this.emitVarDecl(stmt);
|
|
1312
1752
|
break;
|
|
1313
1753
|
|
|
1754
|
+
// A package-level array or struct too large to build as a constant
|
|
1755
|
+
// expression gets its zero value when the module starts.
|
|
1756
|
+
case "GlobalZeroInit": {
|
|
1757
|
+
const g = this.mod.resolveGlobal(stmt.name);
|
|
1758
|
+
this.emitZeroValue(stmt.goType, g.type);
|
|
1759
|
+
this.pushInstruction({ op: "global.set", index: g.index });
|
|
1760
|
+
break;
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1314
1763
|
case "DefineStmt":
|
|
1315
1764
|
this.emitDefineStmt(stmt);
|
|
1316
1765
|
break;
|
|
@@ -1378,7 +1827,7 @@ export class FunctionEmitter {
|
|
|
1378
1827
|
}
|
|
1379
1828
|
}
|
|
1380
1829
|
|
|
1381
|
-
function copyMethods(target, source) {
|
|
1830
|
+
export function copyMethods(target, source) {
|
|
1382
1831
|
const descriptors = Object.getOwnPropertyDescriptors(source.prototype);
|
|
1383
1832
|
delete descriptors.constructor;
|
|
1384
1833
|
for (const key of Object.keys(descriptors)) {
|
|
@@ -1395,10 +1844,5 @@ copyMethods(FunctionEmitter, MapsEmitter);
|
|
|
1395
1844
|
copyMethods(FunctionEmitter, StmtsEmitter);
|
|
1396
1845
|
copyMethods(FunctionEmitter, ExprsEmitter);
|
|
1397
1846
|
copyMethods(FunctionEmitter, BuiltinsEmitter);
|
|
1398
|
-
copyMethods(FunctionEmitter,
|
|
1399
|
-
copyMethods(FunctionEmitter, BytesEmitter);
|
|
1400
|
-
copyMethods(FunctionEmitter, PurePackagesEmitter);
|
|
1401
|
-
copyMethods(FunctionEmitter, IoEmitter);
|
|
1402
|
-
copyMethods(FunctionEmitter, TimeEmitter);
|
|
1403
|
-
copyMethods(FunctionEmitter, ExtendedPackagesEmitter);
|
|
1847
|
+
for (const emitter of stdlibEmitters) copyMethods(FunctionEmitter, emitter);
|
|
1404
1848
|
copyMethods(FunctionEmitter, SharedEmitter);
|