gofront 1.7.0 → 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 +13 -13
- package/package.json +8 -37
- package/src/backend/js/expressions.js +1341 -148
- package/src/backend/js/fmt-runtime.js +573 -0
- package/src/backend/js/index.js +375 -57
- package/src/backend/js/runtime.js +1460 -133
- package/src/backend/js/statements.js +900 -86
- 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 +2 -1
- package/src/backend/js/stdlib/fmt.js +349 -10
- package/src/backend/js/stdlib/index.js +8 -3
- package/src/backend/js/stdlib/json.js +247 -52
- 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 +10 -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 +436 -80
- package/src/backend/wasm/emit-builtins.js +303 -13
- package/src/backend/wasm/emit-exprs.js +1397 -203
- package/src/backend/wasm/emit-maps.js +61 -13
- package/src/backend/wasm/emit-stmts.js +1359 -164
- package/src/backend/wasm/emit.js +512 -77
- package/src/backend/wasm/encode.js +43 -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/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 +0 -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 +293 -28
- package/src/backend/wasm/stdlib/time.js +1041 -12
- package/src/backend/wasm/types.js +200 -15
- package/src/backend/wasm/variadic.js +93 -0
- package/src/backend/wasm/wat.js +47 -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} +3 -4
- package/src/compiler.js +107 -269
- 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 -1
- package/src/lower/captures.js +176 -32
- 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/{templ-parser.js → parser/templ.js} +138 -15
- package/src/parser/types.js +69 -9
- package/src/resolver.js +22 -1
- package/src/typechecker/assignability.js +176 -17
- package/src/typechecker/const-fold.js +356 -0
- package/src/typechecker/constants.js +256 -0
- package/src/typechecker/expressions.js +931 -122
- package/src/typechecker/index.js +459 -67
- package/src/typechecker/init-order.js +330 -0
- package/src/typechecker/resolve.js +451 -56
- package/src/typechecker/statements.js +90 -9
- package/src/typechecker/stdlib/core.js +333 -55
- package/src/typechecker/stdlib/excluded.js +898 -0
- package/src/typechecker/stdlib/extended.js +322 -26
- package/src/typechecker/{stdlib.js → stdlib/index.js} +5 -5
- package/src/typechecker/stdlib/simd.js +65 -15
- package/src/typechecker/termination.js +1 -1
- package/src/typechecker/types.js +149 -25
- 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,17 +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
|
-
SimdEmitter,
|
|
18
|
-
StdlibCoreEmitter,
|
|
19
|
-
TimeEmitter,
|
|
20
|
-
} from "./stdlib/index.js";
|
|
12
|
+
import { stdlibEmitters } from "./stdlib/index.js";
|
|
21
13
|
import {
|
|
22
14
|
isArrayType,
|
|
15
|
+
isComplex,
|
|
16
|
+
isComplex64,
|
|
17
|
+
isFuncType,
|
|
23
18
|
isPointerToStruct,
|
|
24
19
|
isSliceType,
|
|
25
20
|
isStringType,
|
|
@@ -30,6 +25,36 @@ import {
|
|
|
30
25
|
// The emitter is one class split over several files by concern. Methods from
|
|
31
26
|
// the emit-*.js modules are assigned to FunctionEmitter.prototype below; `this`
|
|
32
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
|
+
|
|
33
58
|
export class FunctionEmitter {
|
|
34
59
|
constructor(moduleEmitter, funcDecl, funcIndex) {
|
|
35
60
|
this.mod = moduleEmitter;
|
|
@@ -118,7 +143,75 @@ export class FunctionEmitter {
|
|
|
118
143
|
this.recoverOkLocalIndex = local;
|
|
119
144
|
}
|
|
120
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
|
+
|
|
121
211
|
emitFunctionBody(body) {
|
|
212
|
+
if (!this.funcDecl._isClosure && !this.funcDecl._labelTargets) {
|
|
213
|
+
this.funcDecl._labelTargets = this._collectLabelTargets(body);
|
|
214
|
+
}
|
|
122
215
|
if (
|
|
123
216
|
this.funcDecl._isClosure &&
|
|
124
217
|
!this.funcDecl._isTrampoline &&
|
|
@@ -132,6 +225,7 @@ export class FunctionEmitter {
|
|
|
132
225
|
this._cacheScratchGlobals();
|
|
133
226
|
|
|
134
227
|
this._emitRecoverArmPrologue(body);
|
|
228
|
+
this._checkYieldClosureReentry();
|
|
135
229
|
|
|
136
230
|
const hasDef = hasDefer(body);
|
|
137
231
|
this.hasDefer = hasDef;
|
|
@@ -143,31 +237,139 @@ export class FunctionEmitter {
|
|
|
143
237
|
|
|
144
238
|
this._initNamedReturns();
|
|
145
239
|
|
|
146
|
-
const
|
|
147
|
-
if (needsOobBlock) {
|
|
148
|
-
this.pushInstruction({ op: "block", blockType: "void" });
|
|
149
|
-
this.pushControl("oob");
|
|
150
|
-
}
|
|
240
|
+
const closeChecks = this._openCheckBlocks(body);
|
|
151
241
|
|
|
152
242
|
this.emitBlock(body);
|
|
243
|
+
if (this._finishYieldClosureBody()) {
|
|
244
|
+
closeChecks?.(() => {});
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
closeChecks?.(() =>
|
|
249
|
+
this.pushInstruction({
|
|
250
|
+
op: this.returnTypes.length === 0 ? "return" : "unreachable",
|
|
251
|
+
}),
|
|
252
|
+
);
|
|
253
|
+
}
|
|
153
254
|
|
|
154
|
-
|
|
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) => {
|
|
155
265
|
const lastOp = this.body[this.body.length - 1]?.op;
|
|
156
|
-
if (lastOp !== "return" && lastOp !== "unreachable")
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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
|
+
}
|
|
164
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
|
+
}
|
|
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 });
|
|
165
350
|
this.pushInstruction({
|
|
166
|
-
op: "
|
|
167
|
-
|
|
351
|
+
op: "br_table",
|
|
352
|
+
targets: target,
|
|
353
|
+
defaultTarget: sites.length - 1,
|
|
168
354
|
});
|
|
169
|
-
|
|
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");
|
|
170
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");
|
|
171
373
|
}
|
|
172
374
|
|
|
173
375
|
_initNamedReturns() {
|
|
@@ -180,13 +382,6 @@ export class FunctionEmitter {
|
|
|
180
382
|
const varName = isBlank ? `__ret_blank$${i}` : r.name;
|
|
181
383
|
const wType = this.toWasmType(r.type);
|
|
182
384
|
const localIdx = this.allocLocal(varName, wType, r.type, isBlank);
|
|
183
|
-
this.namedReturnVars.push({
|
|
184
|
-
name: varName,
|
|
185
|
-
localIdx,
|
|
186
|
-
type: wType,
|
|
187
|
-
goType: r.type,
|
|
188
|
-
isBlank,
|
|
189
|
-
});
|
|
190
385
|
this.emitZeroValue(r.type, wType);
|
|
191
386
|
const localInfo = this.locals.get(varName);
|
|
192
387
|
if (localInfo?.isBoxed) {
|
|
@@ -196,6 +391,13 @@ export class FunctionEmitter {
|
|
|
196
391
|
});
|
|
197
392
|
}
|
|
198
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
|
+
});
|
|
199
401
|
}
|
|
200
402
|
return true;
|
|
201
403
|
}
|
|
@@ -256,30 +458,16 @@ export class FunctionEmitter {
|
|
|
256
458
|
});
|
|
257
459
|
this.pushControl("try_table");
|
|
258
460
|
|
|
259
|
-
const
|
|
260
|
-
if (needsOobBlock) {
|
|
261
|
-
this.pushInstruction({ op: "block", blockType: "void" });
|
|
262
|
-
this.pushControl("oob");
|
|
263
|
-
}
|
|
461
|
+
const closeChecks = this._openCheckBlocks(body);
|
|
264
462
|
|
|
265
463
|
this.emitBlock(body);
|
|
266
464
|
|
|
267
|
-
|
|
268
|
-
const lastOp = this.body[this.body.length - 1]?.op;
|
|
269
|
-
if (lastOp !== "return" && lastOp !== "unreachable") {
|
|
270
|
-
this.pushInstruction({
|
|
271
|
-
op: "br",
|
|
272
|
-
depth: this.resolveBranchDepthToRole("runDefers"),
|
|
273
|
-
});
|
|
274
|
-
}
|
|
275
|
-
this.pushInstruction("end");
|
|
276
|
-
|
|
465
|
+
closeChecks?.(() =>
|
|
277
466
|
this.pushInstruction({
|
|
278
|
-
op: "
|
|
279
|
-
|
|
280
|
-
})
|
|
281
|
-
|
|
282
|
-
}
|
|
467
|
+
op: "br",
|
|
468
|
+
depth: this.resolveBranchDepthToRole("runDefers"),
|
|
469
|
+
}),
|
|
470
|
+
);
|
|
283
471
|
|
|
284
472
|
// Close try_table
|
|
285
473
|
this.pushInstruction("end");
|
|
@@ -575,6 +763,52 @@ export class FunctionEmitter {
|
|
|
575
763
|
return false;
|
|
576
764
|
}
|
|
577
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
|
+
|
|
578
812
|
_needsOobBlock(node) {
|
|
579
813
|
if (!node || typeof node !== "object") return false;
|
|
580
814
|
if (Array.isArray(node)) {
|
|
@@ -640,6 +874,10 @@ export class FunctionEmitter {
|
|
|
640
874
|
for (let i = 0; i < lhs.length; i++) {
|
|
641
875
|
if (lhs[i].name === varName) {
|
|
642
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;
|
|
643
881
|
if (node.rhs?.[i]?._type) return node.rhs[i]._type;
|
|
644
882
|
}
|
|
645
883
|
}
|
|
@@ -868,7 +1106,7 @@ export class FunctionEmitter {
|
|
|
868
1106
|
for (const n of names) rawFields.push({ name: n, goType: f.type });
|
|
869
1107
|
}
|
|
870
1108
|
}
|
|
871
|
-
if (
|
|
1109
|
+
if (this.mod.getAnonStructType) {
|
|
872
1110
|
return this.mod.getAnonStructType(rawFields);
|
|
873
1111
|
}
|
|
874
1112
|
}
|
|
@@ -882,7 +1120,13 @@ export class FunctionEmitter {
|
|
|
882
1120
|
return [{ structInfo, fieldIndex: idx, field: structInfo.fields[idx] }];
|
|
883
1121
|
}
|
|
884
1122
|
for (const embed of structInfo.embeds) {
|
|
885
|
-
|
|
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);
|
|
886
1130
|
if (embedInfo) {
|
|
887
1131
|
const sub = this._resolveFieldPath(embedInfo, fieldName);
|
|
888
1132
|
if (sub) {
|
|
@@ -900,7 +1144,7 @@ export class FunctionEmitter {
|
|
|
900
1144
|
return null;
|
|
901
1145
|
}
|
|
902
1146
|
|
|
903
|
-
emitCloneStruct(structInfo, baseWType) {
|
|
1147
|
+
emitCloneStruct(structInfo, baseWType, dstInfo = structInfo) {
|
|
904
1148
|
const tmp = this.acquireTemp(baseWType);
|
|
905
1149
|
this.pushInstruction({ op: "local.set", index: tmp });
|
|
906
1150
|
for (let i = 0; i < structInfo.fields.length; i++) {
|
|
@@ -910,18 +1154,117 @@ export class FunctionEmitter {
|
|
|
910
1154
|
typeIndex: structInfo.typeIndex,
|
|
911
1155
|
fieldIndex: i,
|
|
912
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
|
+
}
|
|
913
1169
|
}
|
|
914
1170
|
this.pushInstruction({
|
|
915
1171
|
op: "struct.new",
|
|
916
|
-
typeIndex:
|
|
1172
|
+
typeIndex: dstInfo.typeIndex,
|
|
917
1173
|
});
|
|
918
1174
|
this.releaseTemp(tmp, baseWType);
|
|
919
1175
|
}
|
|
920
1176
|
|
|
921
|
-
//
|
|
922
|
-
//
|
|
923
|
-
// (
|
|
924
|
-
|
|
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
|
+
}
|
|
925
1268
|
if (
|
|
926
1269
|
!isStructType(goType, this.mod.checker, this.mod) ||
|
|
927
1270
|
isPointerToStruct(goType, this.mod.checker, this.mod)
|
|
@@ -963,7 +1306,8 @@ export class FunctionEmitter {
|
|
|
963
1306
|
const pkg = t.expr?.name;
|
|
964
1307
|
if (pkg) return `${pkg}.${t.field}`;
|
|
965
1308
|
}
|
|
966
|
-
if (t.kind === "simd")
|
|
1309
|
+
if (t.kind === "simd")
|
|
1310
|
+
return t.simd === "Mask32x4" ? "simd.Mask32x4" : "simd.F32x4";
|
|
967
1311
|
if (t.name) return t.name;
|
|
968
1312
|
if (t.kind === "named") return t.name;
|
|
969
1313
|
if (t.kind === "TypeName" || t.kind === "Ident") return t.name;
|
|
@@ -981,11 +1325,23 @@ export class FunctionEmitter {
|
|
|
981
1325
|
return null;
|
|
982
1326
|
}
|
|
983
1327
|
|
|
984
|
-
|
|
985
|
-
|
|
1328
|
+
// Concrete methods the dispatcher `fn` can call: same name, same signature.
|
|
1329
|
+
_dispatchCandidates(fn) {
|
|
1330
|
+
const candidates = this.mod.findInterfaceCandidates(
|
|
986
1331
|
fn._ifaceType,
|
|
987
1332
|
fn._methodName,
|
|
988
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);
|
|
989
1345
|
const recvLocal = 0; // __recv is local 0
|
|
990
1346
|
// Soft dispatchers (errors.Unwrap) return nil instead of panicking when
|
|
991
1347
|
// the receiver is nil or has no such method.
|
|
@@ -1031,6 +1387,8 @@ export class FunctionEmitter {
|
|
|
1031
1387
|
this.pushInstruction("i32.eqz");
|
|
1032
1388
|
this.pushInstruction({ op: "br_if", depth: 0 });
|
|
1033
1389
|
|
|
1390
|
+
this._emitNilPtrBoxPanic(cand, recvLocal);
|
|
1391
|
+
|
|
1034
1392
|
// Matched! Cast receiver
|
|
1035
1393
|
this.pushInstruction({ op: "local.get", index: recvLocal });
|
|
1036
1394
|
this.pushInstruction({
|
|
@@ -1046,6 +1404,12 @@ export class FunctionEmitter {
|
|
|
1046
1404
|
});
|
|
1047
1405
|
if (cand.embedPath) {
|
|
1048
1406
|
for (const step of cand.embedPath) {
|
|
1407
|
+
if (step.castTypeIndex) {
|
|
1408
|
+
this.pushInstruction({
|
|
1409
|
+
op: "ref.cast",
|
|
1410
|
+
typeIndex: step.castTypeIndex,
|
|
1411
|
+
});
|
|
1412
|
+
}
|
|
1049
1413
|
this.pushInstruction({
|
|
1050
1414
|
op: "struct.get",
|
|
1051
1415
|
typeIndex: step.parentTypeIndex,
|
|
@@ -1081,6 +1445,28 @@ export class FunctionEmitter {
|
|
|
1081
1445
|
emitMiss("interface conversion: nil or unmatched type for method call");
|
|
1082
1446
|
}
|
|
1083
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
|
+
|
|
1084
1470
|
_getArraySize(t, defaultSize = 0) {
|
|
1085
1471
|
if (!t) return defaultSize;
|
|
1086
1472
|
if (typeof t.size === "number") return t.size;
|
|
@@ -1088,6 +1474,10 @@ export class FunctionEmitter {
|
|
|
1088
1474
|
if (typeof t.len === "number") return t.len;
|
|
1089
1475
|
if (t.len?.value !== undefined) return Number(t.len.value);
|
|
1090
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
|
+
}
|
|
1091
1481
|
return defaultSize;
|
|
1092
1482
|
}
|
|
1093
1483
|
|
|
@@ -1119,6 +1509,22 @@ export class FunctionEmitter {
|
|
|
1119
1509
|
wType !== null &&
|
|
1120
1510
|
wType.kind === "ref"
|
|
1121
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
|
+
}
|
|
1122
1528
|
if (
|
|
1123
1529
|
goType &&
|
|
1124
1530
|
goType.kind !== "pointer" &&
|
|
@@ -1126,7 +1532,10 @@ export class FunctionEmitter {
|
|
|
1126
1532
|
) {
|
|
1127
1533
|
const sName =
|
|
1128
1534
|
goType.name ?? (goType.kind === "named" ? goType.name : null);
|
|
1129
|
-
|
|
1535
|
+
// An anonymous struct type has no name to look up.
|
|
1536
|
+
const structInfo =
|
|
1537
|
+
this.mod.getStructType(sName) ??
|
|
1538
|
+
this._resolveStructInfo({ _type: goType });
|
|
1130
1539
|
if (structInfo) {
|
|
1131
1540
|
for (const f of structInfo.fields) {
|
|
1132
1541
|
this.emitZeroValue(f.goType, f.wType);
|
|
@@ -1143,10 +1552,15 @@ export class FunctionEmitter {
|
|
|
1143
1552
|
goType.kind !== "pointer" &&
|
|
1144
1553
|
isArrayType(goType, this.mod.checker)
|
|
1145
1554
|
) {
|
|
1146
|
-
const elemType = goType
|
|
1555
|
+
const elemType = this._getArrayElemType(goType);
|
|
1147
1556
|
const arrInfo = this.mod.getArrayType(elemType);
|
|
1148
1557
|
const size = this._getArraySize(goType);
|
|
1149
|
-
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
|
+
) {
|
|
1150
1564
|
for (let i = 0; i < size; i++) {
|
|
1151
1565
|
this.emitZeroValue(elemType, arrInfo.elemWType);
|
|
1152
1566
|
}
|
|
@@ -1286,8 +1700,9 @@ export class FunctionEmitter {
|
|
|
1286
1700
|
return 0;
|
|
1287
1701
|
}
|
|
1288
1702
|
|
|
1289
|
-
|
|
1290
|
-
|
|
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");
|
|
1291
1706
|
}
|
|
1292
1707
|
|
|
1293
1708
|
resolveBranchDepthToRole(targetRole) {
|
|
@@ -1308,17 +1723,43 @@ export class FunctionEmitter {
|
|
|
1308
1723
|
}
|
|
1309
1724
|
}
|
|
1310
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.
|
|
1311
1729
|
emitStmt(stmt) {
|
|
1312
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) {
|
|
1313
1744
|
switch (stmt.kind) {
|
|
1314
1745
|
case "Block":
|
|
1315
1746
|
this.emitBlock(stmt);
|
|
1316
1747
|
break;
|
|
1317
1748
|
|
|
1318
1749
|
case "VarDecl":
|
|
1750
|
+
case "ConstDecl":
|
|
1319
1751
|
this.emitVarDecl(stmt);
|
|
1320
1752
|
break;
|
|
1321
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
|
+
|
|
1322
1763
|
case "DefineStmt":
|
|
1323
1764
|
this.emitDefineStmt(stmt);
|
|
1324
1765
|
break;
|
|
@@ -1386,7 +1827,7 @@ export class FunctionEmitter {
|
|
|
1386
1827
|
}
|
|
1387
1828
|
}
|
|
1388
1829
|
|
|
1389
|
-
function copyMethods(target, source) {
|
|
1830
|
+
export function copyMethods(target, source) {
|
|
1390
1831
|
const descriptors = Object.getOwnPropertyDescriptors(source.prototype);
|
|
1391
1832
|
delete descriptors.constructor;
|
|
1392
1833
|
for (const key of Object.keys(descriptors)) {
|
|
@@ -1403,11 +1844,5 @@ copyMethods(FunctionEmitter, MapsEmitter);
|
|
|
1403
1844
|
copyMethods(FunctionEmitter, StmtsEmitter);
|
|
1404
1845
|
copyMethods(FunctionEmitter, ExprsEmitter);
|
|
1405
1846
|
copyMethods(FunctionEmitter, BuiltinsEmitter);
|
|
1406
|
-
copyMethods(FunctionEmitter,
|
|
1407
|
-
copyMethods(FunctionEmitter, BytesEmitter);
|
|
1408
|
-
copyMethods(FunctionEmitter, PurePackagesEmitter);
|
|
1409
|
-
copyMethods(FunctionEmitter, IoEmitter);
|
|
1410
|
-
copyMethods(FunctionEmitter, TimeEmitter);
|
|
1411
|
-
copyMethods(FunctionEmitter, ExtendedPackagesEmitter);
|
|
1847
|
+
for (const emitter of stdlibEmitters) copyMethods(FunctionEmitter, emitter);
|
|
1412
1848
|
copyMethods(FunctionEmitter, SharedEmitter);
|
|
1413
|
-
copyMethods(FunctionEmitter, SimdEmitter);
|