gofront 1.8.0 → 1.9.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 +3 -3
- package/package.json +5 -4
- package/src/backend/js/expressions.js +849 -371
- package/src/backend/js/index.js +241 -147
- package/src/backend/js/natives.js +383 -0
- package/src/backend/js/runtime.js +512 -2547
- package/src/backend/js/statements.js +506 -103
- package/src/backend/js/stdlib/index.js +45 -93
- package/src/backend/js/templ.js +32 -45
- package/src/backend/js/type-info.js +177 -13
- package/src/backend/shared/fixed-float.js +20 -0
- package/src/backend/shared/fmt-types.js +63 -171
- package/src/backend/shared/host-unicode.js +36 -0
- package/src/backend/wasm/boundary.js +490 -965
- package/src/backend/wasm/emit-builtins.js +643 -256
- package/src/backend/wasm/emit-exprs.js +124 -57
- package/src/backend/wasm/emit-maps.js +17 -367
- package/src/backend/wasm/emit-stmts.js +325 -75
- package/src/backend/wasm/emit.js +104 -13
- package/src/backend/wasm/globals.js +47 -8
- package/src/backend/wasm/glue.js +2 -4
- package/src/backend/wasm/index.js +36 -530
- package/src/backend/wasm/method-expr.js +12 -1
- package/src/backend/wasm/module-emitter.js +92 -717
- package/src/backend/wasm/monomorph.js +103 -6
- package/src/backend/wasm/natives.js +596 -0
- package/src/backend/wasm/prescan.js +5 -153
- package/src/backend/wasm/prune-funcs.js +89 -0
- package/src/backend/wasm/stdlib/index.js +2 -32
- package/src/backend/wasm/types.js +68 -20
- package/src/backend/wasm/variadic.js +3 -3
- package/src/cli/cli-core.js +5 -1
- package/src/cli/test-runner.js +115 -342
- package/src/compiler.js +108 -130
- package/src/lexer/lexer.js +105 -11
- package/src/lexer/templ-lexer.js +61 -18
- package/src/lexer/tokens.js +1 -0
- package/src/link/stdlib.js +354 -132
- package/src/link/type-names.js +148 -0
- package/src/link/wasm.js +30 -6
- package/src/lower/bounds.js +153 -0
- package/src/lower/captures.js +92 -33
- package/src/lower/embedding.js +32 -3
- package/src/lower/errors-as.js +196 -0
- package/src/lower/fmt-formatters.js +905 -0
- package/src/lower/functions.js +2 -0
- package/src/lower/gen-types.js +274 -0
- package/src/lower/index.js +7 -1
- package/src/lower/json-codecs.js +228 -247
- package/src/lower/ownership.js +25 -7
- package/src/lower/reachability.js +69 -51
- package/src/lower/walk.js +12 -0
- package/src/parser/declarations.js +28 -5
- package/src/parser/expressions.js +86 -16
- package/src/parser/statements.js +1 -0
- package/src/parser/templ.js +44 -5
- package/src/parser/types.js +1 -0
- package/src/resolver.js +1 -2
- package/src/typechecker/assignability.js +124 -11
- package/src/typechecker/const-fold.js +70 -27
- package/src/typechecker/constants.js +32 -6
- package/src/typechecker/expressions.js +326 -98
- package/src/typechecker/index.js +208 -33
- package/src/typechecker/init-order.js +136 -40
- package/src/typechecker/operand-text.js +40 -0
- package/src/typechecker/resolve.js +192 -36
- package/src/typechecker/statements.js +122 -11
- package/src/typechecker/stdlib/excluded.js +9 -5
- package/src/typechecker/stdlib/index.js +25 -9
- package/src/typechecker/termination.js +10 -1
- package/src/typechecker/types.js +203 -573
- package/stdlib/bytes/buffer.go +479 -0
- package/stdlib/bytes/bytes.go +1010 -0
- package/stdlib/bytes/reader.go +161 -0
- package/stdlib/context/context.go +115 -0
- package/stdlib/crypto/rand/rand.go +34 -0
- package/stdlib/crypto/sha256/sha256.go +249 -0
- package/stdlib/encoding/base64/base64.go +331 -0
- package/stdlib/encoding/binary/binary.go +332 -0
- package/stdlib/encoding/hex/hex.go +140 -0
- package/stdlib/encoding/json/json.go +62 -0
- package/stdlib/errors/errors.go +174 -0
- package/stdlib/fmt/errors.go +110 -0
- package/stdlib/fmt/format.go +732 -0
- package/stdlib/fmt/print.go +1232 -0
- package/stdlib/gom/gom.go +293 -0
- package/stdlib/html/html.go +251 -0
- package/stdlib/internal/fmtrt/fmtrt.go +163 -0
- package/stdlib/internal/jsonrt/dec.go +171 -38
- package/stdlib/internal/jsonrt/enc.go +186 -36
- package/stdlib/internal/jsonrt/scanner.go +17 -8
- package/stdlib/io/io.go +157 -1
- package/stdlib/maps/maps.go +131 -0
- package/stdlib/math/bits/bits.go +507 -0
- package/stdlib/math/extra.go +16 -14
- package/stdlib/math/math.go +439 -0
- package/stdlib/math/rand/rand.go +297 -0
- package/stdlib/os/os.go +93 -0
- package/stdlib/path/filepath/filepath.go +47 -0
- package/stdlib/path/path.go +394 -0
- package/stdlib/regexp/regexp.go +231 -0
- package/stdlib/slices/slices.go +526 -0
- package/stdlib/slices/sort.go +209 -0
- package/stdlib/slices/zsort_cmp.go +480 -0
- package/stdlib/slices/zsort_ordered.go +482 -0
- package/stdlib/sort/sort.go +391 -452
- package/stdlib/sort/zsort_func.go +480 -0
- package/stdlib/sort/zsort_iface.go +480 -0
- package/stdlib/strconv/float.go +1699 -0
- package/stdlib/strconv/isprint.go +752 -0
- package/stdlib/strconv/quote.go +235 -50
- package/stdlib/strconv/strconv.go +616 -0
- package/stdlib/strings/builder.go +95 -0
- package/stdlib/strings/reader.go +163 -0
- package/stdlib/strings/replacer.go +102 -0
- package/stdlib/strings/strings.go +807 -0
- package/stdlib/sync/sync.go +268 -0
- package/stdlib/testing/async_js.go +61 -0
- package/stdlib/testing/benchmark.go +297 -0
- package/stdlib/testing/testing.go +523 -0
- package/stdlib/time/duration.go +233 -0
- package/stdlib/time/format.go +745 -0
- package/stdlib/time/parse.go +790 -0
- package/stdlib/time/sleep_js.go +63 -0
- package/stdlib/time/time.go +834 -0
- package/stdlib/unicode/unicode.go +141 -3
- package/stdlib/unicode/utf8/utf8.go +464 -0
- package/src/backend/js/fmt-runtime.js +0 -573
- package/src/backend/js/stdlib/base64.js +0 -12
- package/src/backend/js/stdlib/binary.js +0 -102
- package/src/backend/js/stdlib/bits.js +0 -119
- package/src/backend/js/stdlib/builder.js +0 -98
- package/src/backend/js/stdlib/bytes.js +0 -368
- package/src/backend/js/stdlib/errors.js +0 -45
- package/src/backend/js/stdlib/fmt.js +0 -517
- package/src/backend/js/stdlib/gom.js +0 -155
- package/src/backend/js/stdlib/hex.js +0 -17
- package/src/backend/js/stdlib/html.js +0 -20
- package/src/backend/js/stdlib/io.js +0 -137
- package/src/backend/js/stdlib/json.js +0 -1167
- package/src/backend/js/stdlib/maps.js +0 -37
- package/src/backend/js/stdlib/math.js +0 -85
- package/src/backend/js/stdlib/os.js +0 -13
- package/src/backend/js/stdlib/path.js +0 -48
- package/src/backend/js/stdlib/rand.js +0 -32
- package/src/backend/js/stdlib/regexp.js +0 -54
- package/src/backend/js/stdlib/sha256.js +0 -12
- package/src/backend/js/stdlib/shared.js +0 -17
- package/src/backend/js/stdlib/simd.js +0 -34
- package/src/backend/js/stdlib/slices.js +0 -107
- package/src/backend/js/stdlib/sort.js +0 -30
- package/src/backend/js/stdlib/strconv.js +0 -79
- package/src/backend/js/stdlib/strings.js +0 -301
- package/src/backend/js/stdlib/sync.js +0 -13
- package/src/backend/js/stdlib/testing.js +0 -41
- package/src/backend/js/stdlib/time.js +0 -152
- package/src/backend/js/stdlib/unicode.js +0 -25
- package/src/backend/js/stdlib/utf8.js +0 -26
- package/src/backend/wasm/stdlib/bytes.js +0 -6716
- package/src/backend/wasm/stdlib/core.js +0 -10278
- package/src/backend/wasm/stdlib/io.js +0 -3578
- package/src/backend/wasm/stdlib/packages.js +0 -2402
- package/src/backend/wasm/stdlib/pure.js +0 -720
- package/src/backend/wasm/stdlib/time.js +0 -2229
- package/src/typechecker/stdlib/core.js +0 -1079
- package/src/typechecker/stdlib/extended.js +0 -1210
- package/stdlib/bytes/iter.go +0 -192
- package/stdlib/bytes/search.go +0 -240
- package/stdlib/bytes/split.go +0 -218
- package/stdlib/errors/overlay.go +0 -12
- package/stdlib/maps/iter.go +0 -65
- package/stdlib/slices/extra.go +0 -73
- package/stdlib/slices/iter.go +0 -113
- package/stdlib/strings/extra.go +0 -35
- package/stdlib/strings/iter.go +0 -122
- package/stdlib/sync/extra.go +0 -105
- package/stdlib/unicode/utf8/bytes.go +0 -239
package/src/lower/json-codecs.js
CHANGED
|
@@ -14,11 +14,22 @@
|
|
|
14
14
|
// which switches over every type the program encodes or decodes anywhere.
|
|
15
15
|
|
|
16
16
|
import { resolveJsonFields } from "../typechecker/resolve.js";
|
|
17
|
-
import { defaultType } from "../typechecker/types.js";
|
|
18
|
-
import {
|
|
17
|
+
import { defaultType, LOCAL_TYPE_SUFFIX } from "../typechecker/types.js";
|
|
18
|
+
import {
|
|
19
|
+
collectProgramTypes,
|
|
20
|
+
hasSharedRepr,
|
|
21
|
+
importsFor,
|
|
22
|
+
isEmptyIface,
|
|
23
|
+
mentionsTypeParam,
|
|
24
|
+
results,
|
|
25
|
+
SHARED_REPR,
|
|
26
|
+
TypeSpeller,
|
|
27
|
+
under,
|
|
28
|
+
walkNodes,
|
|
29
|
+
} from "./gen-types.js";
|
|
19
30
|
|
|
20
31
|
const JSON_PATH = "encoding/json";
|
|
21
|
-
const SITE_FUNCS = new Set(["Marshal", "MarshalIndent", "Unmarshal"
|
|
32
|
+
const SITE_FUNCS = new Set(["Marshal", "MarshalIndent", "Unmarshal"]);
|
|
22
33
|
|
|
23
34
|
const INT_KINDS = new Set(["int", "int8", "int16", "int32", "int64"]);
|
|
24
35
|
const UINT_KINDS = new Set([
|
|
@@ -67,18 +78,13 @@ const DYNAMIC_BASICS = [
|
|
|
67
78
|
const unalias = (n) => ALIAS[n] ?? n;
|
|
68
79
|
const q = (s) => JSON.stringify(s);
|
|
69
80
|
|
|
70
|
-
function under(t) {
|
|
71
|
-
return t?.kind === "named" ? under(t.underlying) : t;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
81
|
function isBasic(t, set) {
|
|
75
82
|
const u = under(t);
|
|
76
83
|
return u?.kind === "basic" && set.has(unalias(u.name));
|
|
77
84
|
}
|
|
78
85
|
|
|
79
86
|
const isRaw = (t) =>
|
|
80
|
-
t?.kind === "named" &&
|
|
81
|
-
(t.name === "json.RawMessage" || t.name === "encoding/json.RawMessage");
|
|
87
|
+
t?.kind === "named" && t.name === "RawMessage$$encoding$json";
|
|
82
88
|
|
|
83
89
|
const isNumber = (t) =>
|
|
84
90
|
t?.kind === "named" &&
|
|
@@ -95,10 +101,6 @@ function isBytes(t) {
|
|
|
95
101
|
);
|
|
96
102
|
}
|
|
97
103
|
|
|
98
|
-
const isEmptyIface = (t) =>
|
|
99
|
-
(t?.kind === "basic" && t.name === "any") ||
|
|
100
|
-
(under(t)?.kind === "interface" && under(t).methods?.size === 0);
|
|
101
|
-
|
|
102
104
|
const isNilType = (t) =>
|
|
103
105
|
t?.kind === "nil" || (t?.kind === "basic" && t.name === "nil");
|
|
104
106
|
|
|
@@ -112,14 +114,6 @@ function methodOf(t, name) {
|
|
|
112
114
|
return t.methods?.get(name) ?? t.underlying?.methods?.get(name) ?? null;
|
|
113
115
|
}
|
|
114
116
|
|
|
115
|
-
// A method's results; a multi-value result is one tuple type.
|
|
116
|
-
function results(m) {
|
|
117
|
-
const r = (m.returns ?? []).filter(
|
|
118
|
-
(t) => !(t?.kind === "basic" && t.name === "void"),
|
|
119
|
-
);
|
|
120
|
-
return r.length === 1 && r[0]?.kind === "tuple" ? r[0].types : r;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
117
|
// MarshalJSON() ([]byte, error)
|
|
124
118
|
function marshalerOf(t) {
|
|
125
119
|
if (isRaw(t)) return null;
|
|
@@ -128,6 +122,14 @@ function marshalerOf(t) {
|
|
|
128
122
|
return m;
|
|
129
123
|
}
|
|
130
124
|
|
|
125
|
+
// MarshalText() ([]byte, error), used when the type has no MarshalJSON.
|
|
126
|
+
function textMarshalerOf(t) {
|
|
127
|
+
if (isRaw(t) || marshalerOf(t)) return null;
|
|
128
|
+
const m = methodOf(t, "MarshalText");
|
|
129
|
+
if (m?.params?.length !== 0 || results(m).length !== 2) return null;
|
|
130
|
+
return m;
|
|
131
|
+
}
|
|
132
|
+
|
|
131
133
|
// UnmarshalJSON([]byte) error
|
|
132
134
|
function unmarshalerOf(t) {
|
|
133
135
|
if (isRaw(t)) return null;
|
|
@@ -140,12 +142,13 @@ function isSimpleKey(s) {
|
|
|
140
142
|
return /^[A-Za-z0-9_ .-]*$/.test(s);
|
|
141
143
|
}
|
|
142
144
|
|
|
143
|
-
|
|
145
|
+
const isNamedOrPointer = (t) =>
|
|
146
|
+
t.kind === "named" || (t.kind === "pointer" && t.base?.kind === "named");
|
|
147
|
+
|
|
148
|
+
export class JsonCodecGen extends TypeSpeller {
|
|
144
149
|
constructor(checker, pkgName, qualifiers, programs = []) {
|
|
145
|
-
|
|
150
|
+
super(pkgName, qualifiers, programs);
|
|
146
151
|
this.checker = checker;
|
|
147
|
-
this.pkgName = pkgName;
|
|
148
|
-
this.qualifiers = qualifiers;
|
|
149
152
|
this.prefix = `gfj_${String(pkgName).replace(/[^A-Za-z0-9_]/g, "_")}_`;
|
|
150
153
|
this.enc = new Map(); // key -> { name, t, addr }
|
|
151
154
|
this.dec = new Map();
|
|
@@ -175,6 +178,7 @@ export class JsonCodecGen {
|
|
|
175
178
|
map: () => `map[${k(t.key)}]${k(t.value)}`,
|
|
176
179
|
struct: () => this.structKey(t),
|
|
177
180
|
interface: () => (isEmptyIface(t) ? "any" : `iface:${t.name ?? ""}`),
|
|
181
|
+
func: () => this.funcString(t, k),
|
|
178
182
|
};
|
|
179
183
|
return (byKind[t.kind] ?? (() => `?${t.kind}`))();
|
|
180
184
|
}
|
|
@@ -187,83 +191,20 @@ export class JsonCodecGen {
|
|
|
187
191
|
return `struct{${fields.join(";")}}`;
|
|
188
192
|
}
|
|
189
193
|
|
|
190
|
-
// Go source for the type.
|
|
191
|
-
spell(t) {
|
|
192
|
-
const sp = (x) => this.spell(x);
|
|
193
|
-
const byKind = {
|
|
194
|
-
basic: () => t.name,
|
|
195
|
-
named: () => this.spellNamed(t),
|
|
196
|
-
pointer: () => `*${sp(t.base)}`,
|
|
197
|
-
slice: () => `[]${sp(t.elem)}`,
|
|
198
|
-
array: () => `[${t.size}]${sp(t.elem)}`,
|
|
199
|
-
map: () => `map[${sp(t.key)}]${sp(t.value)}`,
|
|
200
|
-
struct: () => this.spellStruct(t),
|
|
201
|
-
interface: () => "any",
|
|
202
|
-
func: () => this.funcString(t, sp),
|
|
203
|
-
};
|
|
204
|
-
if (!byKind[t.kind])
|
|
205
|
-
throw new Error(`json: cannot spell type kind ${t.kind}`);
|
|
206
|
-
return byKind[t.kind]();
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
resolveImportAlias(modPath) {
|
|
210
|
-
for (const p of this.programs) {
|
|
211
|
-
for (const imp of p.imports ?? []) {
|
|
212
|
-
for (const spec of imp.imports) {
|
|
213
|
-
if (spec.path === modPath) {
|
|
214
|
-
return spec.alias ?? spec.path.split("/").pop();
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
return modPath.split("/").pop();
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
spellNamed(t) {
|
|
223
|
-
let name = t.name;
|
|
224
|
-
const at = name.indexOf("$$");
|
|
225
|
-
if (at >= 0) {
|
|
226
|
-
const orig = name.slice(0, at);
|
|
227
|
-
const modPath = name.slice(at + 2).replaceAll("$", "/");
|
|
228
|
-
const alias = this.resolveImportAlias(modPath);
|
|
229
|
-
this.qualifiers.add(alias);
|
|
230
|
-
name = `${alias}.${orig}`;
|
|
231
|
-
}
|
|
232
|
-
const base = this.qualified(name);
|
|
233
|
-
return t.typeArgs?.length
|
|
234
|
-
? `${base}[${t.typeArgs.map((a) => this.spell(a)).join(", ")}]`
|
|
235
|
-
: base;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
spellStruct(t) {
|
|
239
|
-
const fields = (t._structFields ?? []).map((f) => {
|
|
240
|
-
const tag = f.tag ? ` \`${f.tag}\`` : "";
|
|
241
|
-
if (f.embedded) return `${this.spell(f.type)}${tag}`;
|
|
242
|
-
return `${f.names.join(", ")} ${this.spell(f.type)}${tag}`;
|
|
243
|
-
});
|
|
244
|
-
return `struct { ${fields.join("; ")} }`;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
qualified(name) {
|
|
248
|
-
const dot = name.indexOf(".");
|
|
249
|
-
if (dot > 0) this.qualifiers.add(name.slice(0, dot));
|
|
250
|
-
return name;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
194
|
// reflect's Type.String() of the type, for error messages.
|
|
254
195
|
refl(t) {
|
|
255
196
|
switch (t.kind) {
|
|
256
197
|
case "basic":
|
|
257
198
|
return REFLECT_NAME[t.name] ?? unalias(t.name);
|
|
258
199
|
case "named": {
|
|
259
|
-
let name = t.name;
|
|
200
|
+
let name = t.name.replace(LOCAL_TYPE_SUFFIX, "");
|
|
260
201
|
const at = name.indexOf("$$");
|
|
261
202
|
if (at >= 0) {
|
|
262
203
|
const orig = name.slice(0, at);
|
|
263
204
|
const modPath = name.slice(at + 2).replaceAll("$", "/");
|
|
264
205
|
name = `${modPath}.${orig}`;
|
|
265
206
|
} else if (!name.includes(".")) {
|
|
266
|
-
name = `${this.
|
|
207
|
+
name = `${this.declaringPackage(t)}.${name}`;
|
|
267
208
|
}
|
|
268
209
|
return t.typeArgs?.length
|
|
269
210
|
? `${name}[${t.typeArgs.map((a) => this.refl(a)).join(",")}]`
|
|
@@ -278,7 +219,7 @@ export class JsonCodecGen {
|
|
|
278
219
|
case "map":
|
|
279
220
|
return `map[${this.refl(t.key)}]${this.refl(t.value)}`;
|
|
280
221
|
case "interface":
|
|
281
|
-
return "interface {}";
|
|
222
|
+
return t.name === "error" ? "error" : "interface {}";
|
|
282
223
|
case "func":
|
|
283
224
|
return this.funcString(t, (x) => this.refl(x));
|
|
284
225
|
default:
|
|
@@ -286,17 +227,6 @@ export class JsonCodecGen {
|
|
|
286
227
|
}
|
|
287
228
|
}
|
|
288
229
|
|
|
289
|
-
funcString(t, name) {
|
|
290
|
-
const rs = results(t);
|
|
291
|
-
const res =
|
|
292
|
-
rs.length === 0
|
|
293
|
-
? ""
|
|
294
|
-
: rs.length === 1
|
|
295
|
-
? ` ${name(rs[0])}`
|
|
296
|
-
: ` (${rs.map(name).join(", ")})`;
|
|
297
|
-
return `func(${t.params.map(name).join(", ")})${res}`;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
230
|
// A struct's own name in decode errors.
|
|
301
231
|
shortName(t) {
|
|
302
232
|
return t?.kind === "named" ? t.name.split(".").pop() : "";
|
|
@@ -309,7 +239,7 @@ export class JsonCodecGen {
|
|
|
309
239
|
needsAddr(t, seen = new Set()) {
|
|
310
240
|
if (!t || seen.has(t)) return false;
|
|
311
241
|
seen.add(t);
|
|
312
|
-
const m = marshalerOf(t);
|
|
242
|
+
const m = marshalerOf(t) ?? textMarshalerOf(t);
|
|
313
243
|
if (m) return Boolean(m._ptrRecv);
|
|
314
244
|
const u = under(t);
|
|
315
245
|
if (u?.kind === "struct")
|
|
@@ -346,7 +276,13 @@ export class JsonCodecGen {
|
|
|
346
276
|
return [`${this.encFn(t, addr)}(e, ${x})`];
|
|
347
277
|
}
|
|
348
278
|
|
|
349
|
-
|
|
279
|
+
// `mt` is the type that declares the method (the pointee for a pointer).
|
|
280
|
+
marshalCall(x, t, mt = t) {
|
|
281
|
+
if (!marshalerOf(mt) && textMarshalerOf(mt))
|
|
282
|
+
return [
|
|
283
|
+
`out, err := ${x}.MarshalText()`,
|
|
284
|
+
`e.TextMarshaled(out, err, ${q(this.refl(t))})`,
|
|
285
|
+
];
|
|
350
286
|
return [
|
|
351
287
|
`out, err := ${x}.MarshalJSON()`,
|
|
352
288
|
`e.Marshaled(out, err, ${q(this.refl(t))})`,
|
|
@@ -357,7 +293,7 @@ export class JsonCodecGen {
|
|
|
357
293
|
encSpecial(t, addr) {
|
|
358
294
|
if (isRaw(t)) return ["e.Raw([]byte(v))"];
|
|
359
295
|
if (isNumber(t)) return ["e.Number(string(v))"];
|
|
360
|
-
const m = marshalerOf(t);
|
|
296
|
+
const m = marshalerOf(t) ?? textMarshalerOf(t);
|
|
361
297
|
if (m && (!m._ptrRecv || addr)) return this.marshalCall("v", t);
|
|
362
298
|
if (isBytes(t)) return ["e.Bytes([]byte(v))"];
|
|
363
299
|
if (isInterface(t)) {
|
|
@@ -385,7 +321,8 @@ export class JsonCodecGen {
|
|
|
385
321
|
|
|
386
322
|
encPointer(t, u) {
|
|
387
323
|
const lines = ["if v == nil {", "\te.Null()", "\treturn", "}"];
|
|
388
|
-
if (marshalerOf(u.base)
|
|
324
|
+
if (marshalerOf(u.base) || textMarshalerOf(u.base))
|
|
325
|
+
lines.push(...this.marshalCall("v", t, u.base));
|
|
389
326
|
else lines.push(...this.encValue(u.base, "*v", true));
|
|
390
327
|
return lines;
|
|
391
328
|
}
|
|
@@ -558,24 +495,50 @@ export class JsonCodecGen {
|
|
|
558
495
|
genEncAny() {
|
|
559
496
|
const cases = [];
|
|
560
497
|
const seen = new Set();
|
|
498
|
+
const f32 = this.enc.get("float32")?.name;
|
|
561
499
|
for (const [key, e] of this.enc) {
|
|
562
500
|
if (e.addr || seen.has(key)) continue;
|
|
563
501
|
seen.add(key);
|
|
564
502
|
if (isInterface(e.t)) continue;
|
|
565
|
-
|
|
503
|
+
// A package that only lends its encoder knows its named types.
|
|
504
|
+
if (this.registerOnly && !isNamedOrPointer(e.t)) continue;
|
|
505
|
+
// Where one run-time shape serves both float sizes, a float64 that a
|
|
506
|
+
// float32 holds exactly encodes as the float32 it most likely is.
|
|
507
|
+
const body =
|
|
508
|
+
f32 && e.t.kind === "basic" && e.t.name === "float64"
|
|
509
|
+
? `\t\tif jsonrt.LooksFloat32(x) {\n\t\t\t${f32}(e, float32(x))\n\t\t} else {\n\t\t\t${e.name}(e, x)\n\t\t}`
|
|
510
|
+
: `\t\t${e.name}(e, x)`;
|
|
511
|
+
cases.push({
|
|
512
|
+
t: e.t,
|
|
513
|
+
src: `\tcase ${this.spell(e.t)}:\n${body}\n\t\treturn true`,
|
|
514
|
+
});
|
|
566
515
|
}
|
|
516
|
+
// JS numbers do not tell an int from a float64 or float32: whole numbers
|
|
517
|
+
// take the int cases, then float64, then float32.
|
|
518
|
+
const rank = ({ t }) =>
|
|
519
|
+
t.name === "float64" ? 1 : t.name === "float32" ? 2 : 0;
|
|
520
|
+
cases.sort((a, b) => rank(a) - rank(b));
|
|
521
|
+
const known = `${this.prefix}encKnown`;
|
|
567
522
|
this.out.push(
|
|
568
523
|
[
|
|
569
|
-
`func ${
|
|
524
|
+
`func ${known}(e *jsonrt.Enc, v any) bool {`,
|
|
570
525
|
"\tif v == nil {",
|
|
571
526
|
"\t\te.Null()",
|
|
572
|
-
"\t\treturn",
|
|
527
|
+
"\t\treturn true",
|
|
573
528
|
"\t}",
|
|
574
529
|
"\tswitch x := v.(type) {",
|
|
575
|
-
...cases,
|
|
530
|
+
...cases.map((c) => c.src),
|
|
576
531
|
"\tdefault:",
|
|
577
532
|
"\t\t_ = x",
|
|
578
|
-
|
|
533
|
+
"\t\treturn false",
|
|
534
|
+
"\t}",
|
|
535
|
+
"}",
|
|
536
|
+
// Every package with codecs lends its dynamic encoder to the others,
|
|
537
|
+
// so a value of a type another package declares still encodes.
|
|
538
|
+
`var _ = jsonrt.RegisterEncoder(${known})`,
|
|
539
|
+
`func ${this.prefix}encAny(e *jsonrt.Enc, v any) {`,
|
|
540
|
+
`\tif !${known}(e, v) {`,
|
|
541
|
+
"\t\te.EncodeDynamic(v)",
|
|
579
542
|
"\t}",
|
|
580
543
|
"}",
|
|
581
544
|
].join("\n"),
|
|
@@ -632,7 +595,7 @@ export class JsonCodecGen {
|
|
|
632
595
|
if (u.kind === "basic" && !isEmptyIface(t))
|
|
633
596
|
return this.decScalar(t, u, typ);
|
|
634
597
|
const byKind = {
|
|
635
|
-
pointer: () => this.decPointer(u
|
|
598
|
+
pointer: () => this.decPointer(u),
|
|
636
599
|
slice: () => this.decSlice(u, t, typ),
|
|
637
600
|
array: () => this.decArray(u, typ),
|
|
638
601
|
map: () => this.decMap(u, t, typ),
|
|
@@ -707,7 +670,7 @@ export class JsonCodecGen {
|
|
|
707
670
|
];
|
|
708
671
|
}
|
|
709
672
|
|
|
710
|
-
decPointer(u
|
|
673
|
+
decPointer(u) {
|
|
711
674
|
const base = u.base;
|
|
712
675
|
const lines = [
|
|
713
676
|
"if d.Null() {",
|
|
@@ -717,9 +680,7 @@ export class JsonCodecGen {
|
|
|
717
680
|
`\tcur = new(${this.spell(base)})`,
|
|
718
681
|
"}",
|
|
719
682
|
];
|
|
720
|
-
if (
|
|
721
|
-
lines.push(`d.Mismatch(${typ})`);
|
|
722
|
-
} else if (this.decInPlace(base)) {
|
|
683
|
+
if (this.decInPlace(base)) {
|
|
723
684
|
lines.push(`${this.decFn(base)}(d, cur)`);
|
|
724
685
|
} else {
|
|
725
686
|
lines.push(`*cur = ${this.decFn(base)}(d, *cur)`);
|
|
@@ -1015,19 +976,11 @@ export class JsonCodecGen {
|
|
|
1015
976
|
return name;
|
|
1016
977
|
}
|
|
1017
978
|
|
|
1018
|
-
entryValid() {
|
|
1019
|
-
const name = `${this.prefix}valid${this.entries.length}`;
|
|
1020
|
-
this.entries.push(
|
|
1021
|
-
`func ${name}(data []byte) bool {\n\treturn jsonrt.Valid(data)\n}`,
|
|
1022
|
-
);
|
|
1023
|
-
return name;
|
|
1024
|
-
}
|
|
1025
|
-
|
|
1026
979
|
entryEncode(argType) {
|
|
1027
|
-
this.
|
|
980
|
+
const json = this.qualifierFor(JSON_PATH);
|
|
1028
981
|
const name = `${this.prefix}encode${this.entries.length}`;
|
|
1029
982
|
const lines = [
|
|
1030
|
-
`func ${name}(enc
|
|
983
|
+
`func ${name}(enc *${json}.Encoder, v any) error {`,
|
|
1031
984
|
"\tif enc.Err() != nil {",
|
|
1032
985
|
"\t\treturn enc.Err()",
|
|
1033
986
|
"\t}",
|
|
@@ -1056,10 +1009,10 @@ export class JsonCodecGen {
|
|
|
1056
1009
|
}
|
|
1057
1010
|
|
|
1058
1011
|
entryDecode(argType) {
|
|
1059
|
-
this.
|
|
1012
|
+
const json = this.qualifierFor(JSON_PATH);
|
|
1060
1013
|
const name = `${this.prefix}decode${this.entries.length}`;
|
|
1061
1014
|
const lines = [
|
|
1062
|
-
`func ${name}(dec
|
|
1015
|
+
`func ${name}(dec *${json}.Decoder, v any) error {`,
|
|
1063
1016
|
"\tdata, err := dec.ScanNext()",
|
|
1064
1017
|
"\tif err != nil {",
|
|
1065
1018
|
"\t\treturn err",
|
|
@@ -1103,7 +1056,13 @@ export class JsonCodecGen {
|
|
|
1103
1056
|
(t) =>
|
|
1104
1057
|
t.kind === "pointer" && (isEmptyIface(t.base) || !isInterface(t.base)),
|
|
1105
1058
|
);
|
|
1106
|
-
|
|
1059
|
+
// A pointer to a type parameter's variable names no type in JavaScript:
|
|
1060
|
+
// it decodes by its pointee's shape (see __isty), so these always exist.
|
|
1061
|
+
const shapes = ["any", "float64", "string", "bool", "int64"].map((n) => ({
|
|
1062
|
+
kind: "pointer",
|
|
1063
|
+
base: { kind: "basic", name: n },
|
|
1064
|
+
}));
|
|
1065
|
+
for (const pt of [...this.decTargets, ...pointers, ...shapes]) {
|
|
1107
1066
|
const k = this.key(pt);
|
|
1108
1067
|
if (seen.has(k)) continue;
|
|
1109
1068
|
seen.add(k);
|
|
@@ -1133,21 +1092,21 @@ export class JsonCodecGen {
|
|
|
1133
1092
|
|
|
1134
1093
|
// Registers what the dynamic encoder handles, until nothing new appears.
|
|
1135
1094
|
addDynamicTypes() {
|
|
1136
|
-
for (const n of DYNAMIC_BASICS) this.encFn({ kind: "basic", name: n });
|
|
1137
1095
|
const anyT = { kind: "basic", name: "any" };
|
|
1138
|
-
this.
|
|
1139
|
-
|
|
1140
|
-
kind: "
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1096
|
+
if (!this.registerOnly) {
|
|
1097
|
+
for (const n of DYNAMIC_BASICS) this.encFn({ kind: "basic", name: n });
|
|
1098
|
+
this.encFn({ kind: "slice", elem: anyT });
|
|
1099
|
+
this.encFn({
|
|
1100
|
+
kind: "map",
|
|
1101
|
+
key: { kind: "basic", name: "string" },
|
|
1102
|
+
value: anyT,
|
|
1103
|
+
});
|
|
1104
|
+
}
|
|
1144
1105
|
for (const t of this.programTypes()) {
|
|
1145
1106
|
this.encFn(t, false);
|
|
1146
1107
|
}
|
|
1147
|
-
for (const
|
|
1148
|
-
if (
|
|
1149
|
-
continue;
|
|
1150
|
-
if (t.name !== name || t.typeArgs?.length || t._genericOf) continue;
|
|
1108
|
+
for (const { type: t, via } of this.collectedTypes()) {
|
|
1109
|
+
if (via !== "declared" || mentionsTypeParam(t)) continue;
|
|
1151
1110
|
const u = under(t);
|
|
1152
1111
|
if (!u || u.kind === "interface" || u.kind === "func") continue;
|
|
1153
1112
|
this.encFn(t, false);
|
|
@@ -1155,41 +1114,53 @@ export class JsonCodecGen {
|
|
|
1155
1114
|
}
|
|
1156
1115
|
}
|
|
1157
1116
|
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
const byKind = {
|
|
1163
|
-
basic: () => t.name !== "void" && t.name !== "nil",
|
|
1164
|
-
named: () =>
|
|
1165
|
-
!t.name.includes("$$") &&
|
|
1117
|
+
spellableNamed(t, ok) {
|
|
1118
|
+
return (
|
|
1119
|
+
isJsonValue(t) ||
|
|
1120
|
+
(!t.name.includes("$$") &&
|
|
1166
1121
|
!t.name.includes("[") &&
|
|
1167
|
-
(t.typeArgs ?? []).every(ok)
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1122
|
+
(t.typeArgs ?? []).every(ok))
|
|
1123
|
+
);
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
addTypeArg(found, a) {
|
|
1127
|
+
const t = defaultType(a);
|
|
1128
|
+
if (!t || mentionsTypeParam(t) || !this.spellable(t) || isInterface(t))
|
|
1129
|
+
return;
|
|
1130
|
+
// An array or map of small integers would shadow its look-alikes in the
|
|
1131
|
+
// type switch (see SHARED_REPR).
|
|
1132
|
+
const ambiguous = !isCompositeType(t) && hasSharedRepr(t);
|
|
1133
|
+
if (holdsFunc(t) || ambiguous) return;
|
|
1134
|
+
found.set(this.key(t), t);
|
|
1135
|
+
const p = { kind: "pointer", base: t };
|
|
1136
|
+
found.set(this.key(p), p);
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
collectedTypes() {
|
|
1140
|
+
this.collected ??= collectProgramTypes(this.programs, this.checker);
|
|
1141
|
+
return this.collected;
|
|
1177
1142
|
}
|
|
1178
1143
|
|
|
1179
1144
|
// Types the program builds with composite literals, `&T{}`, make and new:
|
|
1180
1145
|
// candidates for a value stored in an interface (an `any` map value, say).
|
|
1181
1146
|
programTypes() {
|
|
1182
1147
|
const found = new Map();
|
|
1183
|
-
for (const
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
}
|
|
1148
|
+
for (const { type: t, node, via } of this.collectedTypes()) {
|
|
1149
|
+
// A generic call's type arguments are what its body encodes and
|
|
1150
|
+
// decodes through `T` and `&v`.
|
|
1151
|
+
if (via === "typeArg") this.addTypeArg(found, t);
|
|
1152
|
+
if (via !== "expr" || !t) continue;
|
|
1153
|
+
// A func literal in an interface reports its own type.
|
|
1154
|
+
if (node.kind === "FuncLit" && this.spellable(t)) {
|
|
1155
|
+
found.set(this.key(t), t);
|
|
1156
|
+
continue;
|
|
1157
|
+
}
|
|
1158
|
+
// Slices, arrays and maps reach an interface from any expression
|
|
1159
|
+
// (a function result, a variable), not only from a literal
|
|
1160
|
+
if (!buildsValue(node) && !isCompositeType(t) && !isJsonValue(t))
|
|
1161
|
+
continue;
|
|
1162
|
+
if (this.spellable(t) && under(t)?.kind !== "interface")
|
|
1163
|
+
found.set(this.key(t), t);
|
|
1193
1164
|
}
|
|
1194
1165
|
return [...found.values()];
|
|
1195
1166
|
}
|
|
@@ -1212,68 +1183,32 @@ export class JsonCodecGen {
|
|
|
1212
1183
|
}
|
|
1213
1184
|
}
|
|
1214
1185
|
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
return [
|
|
1218
|
-
`package ${pkgName}`,
|
|
1219
|
-
"",
|
|
1220
|
-
`import (\n\t"internal/jsonrt"\n${importLines}\n)`,
|
|
1221
|
-
"",
|
|
1222
|
-
...this.entries,
|
|
1223
|
-
...this.out,
|
|
1224
|
-
"",
|
|
1225
|
-
].join("\n\n");
|
|
1186
|
+
// The generated declarations (the spine adds the header and imports).
|
|
1187
|
+
body() {
|
|
1188
|
+
return [...this.entries, ...this.out].join("\n\n");
|
|
1226
1189
|
}
|
|
1227
1190
|
}
|
|
1228
1191
|
|
|
1229
|
-
//
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
for (const n of root) walkNodes(n, fn);
|
|
1234
|
-
return;
|
|
1235
|
-
}
|
|
1236
|
-
fn(root);
|
|
1237
|
-
forEachChild(root, (c) => walkNodes(c, fn));
|
|
1238
|
-
}
|
|
1192
|
+
// A slice of booleans or small integers carries a tag of its Go type inside an
|
|
1193
|
+
// interface, so it never shadows another slice type in the type switch.
|
|
1194
|
+
const isTaggedSlice = (t) =>
|
|
1195
|
+
under(t)?.kind === "slice" && isBasic(under(t).elem, SHARED_REPR);
|
|
1239
1196
|
|
|
1240
|
-
//
|
|
1241
|
-
//
|
|
1242
|
-
|
|
1243
|
-
const SHARED_REPR = new Set([
|
|
1244
|
-
"bool",
|
|
1245
|
-
"int8",
|
|
1246
|
-
"int16",
|
|
1247
|
-
"int32",
|
|
1248
|
-
"uint8",
|
|
1249
|
-
"uint16",
|
|
1250
|
-
"uint32",
|
|
1251
|
-
"byte",
|
|
1252
|
-
"rune",
|
|
1253
|
-
]);
|
|
1197
|
+
// json.RawMessage and json.Number values can reach an interface from any
|
|
1198
|
+
// expression (a conversion, a field read).
|
|
1199
|
+
const isJsonValue = (t) => isRaw(t) || isNumber(t);
|
|
1254
1200
|
|
|
1255
|
-
const isCompositeType = (t) => {
|
|
1201
|
+
export const isCompositeType = (t) => {
|
|
1256
1202
|
const k = under(t)?.kind;
|
|
1257
1203
|
return (
|
|
1258
1204
|
(k === "slice" || k === "array" || k === "map") &&
|
|
1259
1205
|
!holdsFunc(t) &&
|
|
1260
|
-
!hasSharedRepr(t)
|
|
1206
|
+
(isTaggedSlice(t) || !hasSharedRepr(t))
|
|
1261
1207
|
);
|
|
1262
1208
|
};
|
|
1263
1209
|
|
|
1264
|
-
function hasSharedRepr(t, depth = 0) {
|
|
1265
|
-
if (!t || depth > 8) return false;
|
|
1266
|
-
const u = under(t);
|
|
1267
|
-
if (u?.kind === "basic") return SHARED_REPR.has(u.name);
|
|
1268
|
-
if (u?.kind === "slice" || u?.kind === "array")
|
|
1269
|
-
return hasSharedRepr(u.elem, depth + 1);
|
|
1270
|
-
if (u?.kind === "map")
|
|
1271
|
-
return hasSharedRepr(u.key, depth + 1) || hasSharedRepr(u.value, depth + 1);
|
|
1272
|
-
return false;
|
|
1273
|
-
}
|
|
1274
|
-
|
|
1275
1210
|
// Func values are not JSON-encodable, and `case []func():` does not parse.
|
|
1276
|
-
function holdsFunc(t, depth = 0) {
|
|
1211
|
+
export function holdsFunc(t, depth = 0) {
|
|
1277
1212
|
if (!t || depth > 8) return false;
|
|
1278
1213
|
const u = under(t);
|
|
1279
1214
|
if (u?.kind === "func") return true;
|
|
@@ -1305,12 +1240,17 @@ function jsonAliases(program) {
|
|
|
1305
1240
|
return aliases;
|
|
1306
1241
|
}
|
|
1307
1242
|
|
|
1243
|
+
// `json.Marshal`-style selector on the imported package (not a local that
|
|
1244
|
+
// shadows its name).
|
|
1245
|
+
const isJsonRef = (node, aliases) =>
|
|
1246
|
+
node.kind === "SelectorExpr" &&
|
|
1247
|
+
node.expr?.kind === "Ident" &&
|
|
1248
|
+
node.expr._type?.kind === "namespace" &&
|
|
1249
|
+
aliases.has(node.expr.name) &&
|
|
1250
|
+
SITE_FUNCS.has(node.field);
|
|
1251
|
+
|
|
1308
1252
|
const isJsonCall = (node, aliases) =>
|
|
1309
|
-
node.kind === "CallExpr" &&
|
|
1310
|
-
node.func?.kind === "SelectorExpr" &&
|
|
1311
|
-
node.func.expr?.kind === "Ident" &&
|
|
1312
|
-
aliases.has(node.func.expr.name) &&
|
|
1313
|
-
SITE_FUNCS.has(node.func.field);
|
|
1253
|
+
node.kind === "CallExpr" && isJsonRef(node.func, aliases);
|
|
1314
1254
|
|
|
1315
1255
|
function isStreamCall(node) {
|
|
1316
1256
|
if (node.kind !== "CallExpr" || node.func?.kind !== "SelectorExpr")
|
|
@@ -1330,21 +1270,36 @@ function isStreamCall(node) {
|
|
|
1330
1270
|
return null;
|
|
1331
1271
|
}
|
|
1332
1272
|
|
|
1333
|
-
// Finds `json.Marshal/Unmarshal
|
|
1273
|
+
// Finds `json.Marshal/MarshalIndent/Unmarshal` call sites in the checked
|
|
1274
|
+
// programs, and a diagnostic for each use of them that is not a call.
|
|
1334
1275
|
function findSites(programs) {
|
|
1335
1276
|
const sites = [];
|
|
1277
|
+
const diagnostics = [];
|
|
1336
1278
|
for (const p of programs) {
|
|
1337
1279
|
const aliases = jsonAliases(p);
|
|
1280
|
+
const callees = new Set();
|
|
1281
|
+
const refs = [];
|
|
1338
1282
|
walkNodes(p.decls, (node) => {
|
|
1339
1283
|
if (isJsonCall(node, aliases)) {
|
|
1340
1284
|
sites.push({ call: node, fn: node.func.field, program: p });
|
|
1285
|
+
callees.add(node.func);
|
|
1286
|
+
} else if (isJsonRef(node, aliases)) {
|
|
1287
|
+
refs.push(node);
|
|
1341
1288
|
} else {
|
|
1342
1289
|
const streamFn = isStreamCall(node);
|
|
1343
1290
|
if (streamFn) sites.push({ call: node, fn: streamFn, program: p });
|
|
1344
1291
|
}
|
|
1345
1292
|
});
|
|
1346
|
-
|
|
1347
|
-
|
|
1293
|
+
for (const node of refs)
|
|
1294
|
+
if (!callees.has(node))
|
|
1295
|
+
diagnostics.push({
|
|
1296
|
+
program: p,
|
|
1297
|
+
node: node.expr,
|
|
1298
|
+
message: `json.${node.field} cannot be used as a value`,
|
|
1299
|
+
hint: `call json.${node.field} directly`,
|
|
1300
|
+
});
|
|
1301
|
+
}
|
|
1302
|
+
return { sites, diagnostics };
|
|
1348
1303
|
}
|
|
1349
1304
|
|
|
1350
1305
|
function selectCodecEntry(gen, fn, argType) {
|
|
@@ -1353,22 +1308,52 @@ function selectCodecEntry(gen, fn, argType) {
|
|
|
1353
1308
|
if (fn === "Unmarshal") return gen.entryUnmarshal(argType(1));
|
|
1354
1309
|
if (fn === "Encode") return gen.entryEncode(argType(0));
|
|
1355
1310
|
if (fn === "Decode") return gen.entryDecode(argType(0));
|
|
1356
|
-
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
// Records that the build uses `json` when this package calls it; returns
|
|
1314
|
+
// whether the package has call sites (or misuses) of its own. One without
|
|
1315
|
+
// them still lends its types to the dynamic encoder once `json` is in use.
|
|
1316
|
+
// The flag is tracked per target so WASM-only use does not pull `json` into
|
|
1317
|
+
// JS output (and vice versa).
|
|
1318
|
+
function jsonTargets(checker) {
|
|
1319
|
+
return checker.target === "both" || !checker.target
|
|
1320
|
+
? ["js", "wasm"]
|
|
1321
|
+
: [checker.target];
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
function noteJsonUse(options, sites, diagnostics, checker) {
|
|
1325
|
+
if (sites.length > 0 && options.jsonBuild)
|
|
1326
|
+
for (const t of jsonTargets(checker)) options.jsonBuild[t] = true;
|
|
1327
|
+
return sites.length + diagnostics.length > 0;
|
|
1357
1328
|
}
|
|
1358
1329
|
|
|
1359
1330
|
// Generates the codec file for a package's `json` call sites. Returns null
|
|
1360
|
-
// when it has none; otherwise {
|
|
1361
|
-
// SelectorExpr with `
|
|
1362
|
-
export function generateJsonCodecs(programs, checker, pkgName) {
|
|
1363
|
-
const sites = findSites(programs);
|
|
1364
|
-
|
|
1365
|
-
|
|
1331
|
+
// when it has none; otherwise { body, imports, diagnostics } and marks each call's
|
|
1332
|
+
// SelectorExpr with `_genEntry`, the entry point the WASM link calls instead.
|
|
1333
|
+
export function generateJsonCodecs(programs, checker, pkgName, options = {}) {
|
|
1334
|
+
const { sites, diagnostics } = findSites(programs);
|
|
1335
|
+
const registerOnly = !noteJsonUse(options, sites, diagnostics, checker);
|
|
1336
|
+
if (registerOnly && !jsonTargets(checker).some((t) => options.jsonBuild?.[t]))
|
|
1337
|
+
return null;
|
|
1338
|
+
const qualifiers = new Map();
|
|
1366
1339
|
const gen = new JsonCodecGen(checker, pkgName, qualifiers, programs);
|
|
1340
|
+
if (registerOnly) {
|
|
1341
|
+
gen.registerOnly = true;
|
|
1342
|
+
gen.flagDynamic();
|
|
1343
|
+
}
|
|
1367
1344
|
const pending = [];
|
|
1368
|
-
const diagnostics = [];
|
|
1369
1345
|
for (const { call, fn, program } of sites) {
|
|
1370
|
-
|
|
1371
|
-
|
|
1346
|
+
// A site inside a generic function is shared by every instance, so a type
|
|
1347
|
+
// that mentions a type parameter takes the dynamic entry.
|
|
1348
|
+
const argType = (i) => {
|
|
1349
|
+
const t = defaultType(call.args[i]?._type);
|
|
1350
|
+
return mentionsTypeParam(t) ? null : t;
|
|
1351
|
+
};
|
|
1352
|
+
if (
|
|
1353
|
+
fn === "Unmarshal" &&
|
|
1354
|
+
checker.target !== "js" &&
|
|
1355
|
+
argType(1)?.base?.kind === "pointer"
|
|
1356
|
+
)
|
|
1372
1357
|
diagnostics.push({
|
|
1373
1358
|
program,
|
|
1374
1359
|
node: call.func.expr,
|
|
@@ -1380,19 +1365,15 @@ export function generateJsonCodecs(programs, checker, pkgName) {
|
|
|
1380
1365
|
pending.push([call.func, entry, fn === "Encode" || fn === "Decode"]);
|
|
1381
1366
|
}
|
|
1382
1367
|
gen.finish();
|
|
1368
|
+
if (registerOnly && !gen.out.some((o) => o.includes("RegisterEncoder")))
|
|
1369
|
+
return null;
|
|
1383
1370
|
for (const [sel, entry, isStream] of pending) {
|
|
1384
|
-
sel.
|
|
1371
|
+
sel._genEntry = entry;
|
|
1385
1372
|
if (isStream) sel._jsonStream = true;
|
|
1386
1373
|
}
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
importPaths.set(spec.alias ?? last, spec.path);
|
|
1393
|
-
}
|
|
1394
|
-
const imports = [...qualifiers]
|
|
1395
|
-
.map((qn) => importPaths.get(qn))
|
|
1396
|
-
.filter(Boolean);
|
|
1397
|
-
return { source: gen.source(pkgName, imports), diagnostics };
|
|
1374
|
+
return {
|
|
1375
|
+
body: gen.body(),
|
|
1376
|
+
imports: ["internal/jsonrt", ...importsFor(qualifiers)],
|
|
1377
|
+
diagnostics,
|
|
1378
|
+
};
|
|
1398
1379
|
}
|