gofront 1.8.0 → 1.9.1

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.
Files changed (178) hide show
  1. package/README.md +3 -3
  2. package/package.json +6 -5
  3. package/src/backend/js/expressions.js +1157 -408
  4. package/src/backend/js/index.js +327 -153
  5. package/src/backend/js/natives.js +383 -0
  6. package/src/backend/js/runtime.js +559 -2552
  7. package/src/backend/js/statements.js +670 -158
  8. package/src/backend/js/stdlib/index.js +45 -93
  9. package/src/backend/js/templ.js +32 -45
  10. package/src/backend/js/type-info.js +298 -19
  11. package/src/backend/shared/fixed-float.js +20 -0
  12. package/src/backend/shared/fmt-types.js +91 -170
  13. package/src/backend/shared/host-unicode.js +36 -0
  14. package/src/backend/wasm/boundary.js +490 -965
  15. package/src/backend/wasm/emit-builtins.js +884 -256
  16. package/src/backend/wasm/emit-exprs.js +185 -77
  17. package/src/backend/wasm/emit-maps.js +17 -367
  18. package/src/backend/wasm/emit-shared.js +4 -1
  19. package/src/backend/wasm/emit-stmts.js +524 -240
  20. package/src/backend/wasm/emit.js +176 -38
  21. package/src/backend/wasm/globals.js +48 -8
  22. package/src/backend/wasm/glue.js +2 -4
  23. package/src/backend/wasm/index.js +44 -530
  24. package/src/backend/wasm/method-expr.js +12 -1
  25. package/src/backend/wasm/module-emitter.js +299 -770
  26. package/src/backend/wasm/monomorph.js +139 -8
  27. package/src/backend/wasm/natives.js +600 -0
  28. package/src/backend/wasm/prescan.js +5 -153
  29. package/src/backend/wasm/prune-funcs.js +89 -0
  30. package/src/backend/wasm/stdlib/index.js +2 -32
  31. package/src/backend/wasm/types.js +97 -20
  32. package/src/backend/wasm/variadic.js +9 -4
  33. package/src/cli/cli-core.js +5 -1
  34. package/src/cli/test-runner.js +115 -342
  35. package/src/compiler.js +113 -130
  36. package/src/lexer/lexer.js +106 -11
  37. package/src/lexer/templ-lexer.js +61 -18
  38. package/src/lexer/tokens.js +1 -0
  39. package/src/link/stdlib.js +355 -132
  40. package/src/link/type-names.js +204 -0
  41. package/src/link/wasm.js +30 -6
  42. package/src/lower/bounds.js +153 -0
  43. package/src/lower/captures.js +92 -33
  44. package/src/lower/embedding.js +62 -5
  45. package/src/lower/errors-as.js +196 -0
  46. package/src/lower/fmt-formatters.js +929 -0
  47. package/src/lower/functions.js +225 -7
  48. package/src/lower/gen-types.js +293 -0
  49. package/src/lower/index.js +14 -2
  50. package/src/lower/json-codecs.js +334 -245
  51. package/src/lower/ownership.js +25 -7
  52. package/src/lower/reachability.js +69 -51
  53. package/src/lower/walk.js +12 -0
  54. package/src/parser/declarations.js +30 -5
  55. package/src/parser/expressions.js +111 -57
  56. package/src/parser/statements.js +108 -0
  57. package/src/parser/templ.js +44 -5
  58. package/src/parser/types.js +12 -0
  59. package/src/resolver.js +1 -2
  60. package/src/typechecker/assignability.js +214 -19
  61. package/src/typechecker/const-fold.js +70 -27
  62. package/src/typechecker/constants.js +156 -8
  63. package/src/typechecker/expressions.js +490 -108
  64. package/src/typechecker/index.js +346 -42
  65. package/src/typechecker/init-order.js +136 -40
  66. package/src/typechecker/operand-text.js +41 -0
  67. package/src/typechecker/resolve.js +490 -65
  68. package/src/typechecker/statements.js +288 -27
  69. package/src/typechecker/stdlib/excluded.js +9 -5
  70. package/src/typechecker/stdlib/index.js +25 -9
  71. package/src/typechecker/termination.js +10 -1
  72. package/src/typechecker/types.js +287 -575
  73. package/stdlib/bytes/buffer.go +479 -0
  74. package/stdlib/bytes/bytes.go +1010 -0
  75. package/stdlib/bytes/reader.go +161 -0
  76. package/stdlib/context/context.go +115 -0
  77. package/stdlib/crypto/rand/rand.go +34 -0
  78. package/stdlib/crypto/sha256/sha256.go +249 -0
  79. package/stdlib/encoding/base64/base64.go +331 -0
  80. package/stdlib/encoding/binary/binary.go +332 -0
  81. package/stdlib/encoding/hex/hex.go +140 -0
  82. package/stdlib/encoding/json/json.go +62 -0
  83. package/stdlib/errors/errors.go +174 -0
  84. package/stdlib/fmt/errors.go +110 -0
  85. package/stdlib/fmt/format.go +732 -0
  86. package/stdlib/fmt/print.go +1232 -0
  87. package/stdlib/gom/gom.go +293 -0
  88. package/stdlib/html/html.go +251 -0
  89. package/stdlib/internal/fmtrt/fmtrt.go +163 -0
  90. package/stdlib/internal/jsonrt/dec.go +171 -38
  91. package/stdlib/internal/jsonrt/enc.go +186 -36
  92. package/stdlib/internal/jsonrt/scanner.go +17 -8
  93. package/stdlib/io/io.go +157 -1
  94. package/stdlib/maps/maps.go +131 -0
  95. package/stdlib/math/bits/bits.go +507 -0
  96. package/stdlib/math/extra.go +16 -14
  97. package/stdlib/math/math.go +439 -0
  98. package/stdlib/math/rand/rand.go +297 -0
  99. package/stdlib/os/os.go +93 -0
  100. package/stdlib/path/filepath/filepath.go +47 -0
  101. package/stdlib/path/path.go +394 -0
  102. package/stdlib/regexp/regexp.go +231 -0
  103. package/stdlib/slices/slices.go +526 -0
  104. package/stdlib/slices/sort.go +209 -0
  105. package/stdlib/slices/zsort_cmp.go +480 -0
  106. package/stdlib/slices/zsort_ordered.go +482 -0
  107. package/stdlib/sort/sort.go +391 -452
  108. package/stdlib/sort/zsort_func.go +480 -0
  109. package/stdlib/sort/zsort_iface.go +480 -0
  110. package/stdlib/strconv/float.go +1699 -0
  111. package/stdlib/strconv/isprint.go +752 -0
  112. package/stdlib/strconv/quote.go +235 -50
  113. package/stdlib/strconv/strconv.go +616 -0
  114. package/stdlib/strings/builder.go +95 -0
  115. package/stdlib/strings/reader.go +163 -0
  116. package/stdlib/strings/replacer.go +102 -0
  117. package/stdlib/strings/strings.go +807 -0
  118. package/stdlib/sync/sync.go +268 -0
  119. package/stdlib/testing/async_js.go +61 -0
  120. package/stdlib/testing/benchmark.go +297 -0
  121. package/stdlib/testing/testing.go +523 -0
  122. package/stdlib/time/duration.go +233 -0
  123. package/stdlib/time/format.go +745 -0
  124. package/stdlib/time/parse.go +790 -0
  125. package/stdlib/time/sleep_js.go +63 -0
  126. package/stdlib/time/time.go +834 -0
  127. package/stdlib/unicode/unicode.go +141 -3
  128. package/stdlib/unicode/utf8/utf8.go +464 -0
  129. package/src/backend/js/fmt-runtime.js +0 -573
  130. package/src/backend/js/stdlib/base64.js +0 -12
  131. package/src/backend/js/stdlib/binary.js +0 -102
  132. package/src/backend/js/stdlib/bits.js +0 -119
  133. package/src/backend/js/stdlib/builder.js +0 -98
  134. package/src/backend/js/stdlib/bytes.js +0 -368
  135. package/src/backend/js/stdlib/errors.js +0 -45
  136. package/src/backend/js/stdlib/fmt.js +0 -517
  137. package/src/backend/js/stdlib/gom.js +0 -155
  138. package/src/backend/js/stdlib/hex.js +0 -17
  139. package/src/backend/js/stdlib/html.js +0 -20
  140. package/src/backend/js/stdlib/io.js +0 -137
  141. package/src/backend/js/stdlib/json.js +0 -1167
  142. package/src/backend/js/stdlib/maps.js +0 -37
  143. package/src/backend/js/stdlib/math.js +0 -85
  144. package/src/backend/js/stdlib/os.js +0 -13
  145. package/src/backend/js/stdlib/path.js +0 -48
  146. package/src/backend/js/stdlib/rand.js +0 -32
  147. package/src/backend/js/stdlib/regexp.js +0 -54
  148. package/src/backend/js/stdlib/sha256.js +0 -12
  149. package/src/backend/js/stdlib/shared.js +0 -17
  150. package/src/backend/js/stdlib/simd.js +0 -34
  151. package/src/backend/js/stdlib/slices.js +0 -107
  152. package/src/backend/js/stdlib/sort.js +0 -30
  153. package/src/backend/js/stdlib/strconv.js +0 -79
  154. package/src/backend/js/stdlib/strings.js +0 -301
  155. package/src/backend/js/stdlib/sync.js +0 -13
  156. package/src/backend/js/stdlib/testing.js +0 -41
  157. package/src/backend/js/stdlib/time.js +0 -152
  158. package/src/backend/js/stdlib/unicode.js +0 -25
  159. package/src/backend/js/stdlib/utf8.js +0 -26
  160. package/src/backend/wasm/stdlib/bytes.js +0 -6716
  161. package/src/backend/wasm/stdlib/core.js +0 -10278
  162. package/src/backend/wasm/stdlib/io.js +0 -3578
  163. package/src/backend/wasm/stdlib/packages.js +0 -2402
  164. package/src/backend/wasm/stdlib/pure.js +0 -720
  165. package/src/backend/wasm/stdlib/time.js +0 -2229
  166. package/src/typechecker/stdlib/core.js +0 -1079
  167. package/src/typechecker/stdlib/extended.js +0 -1210
  168. package/stdlib/bytes/iter.go +0 -192
  169. package/stdlib/bytes/search.go +0 -240
  170. package/stdlib/bytes/split.go +0 -218
  171. package/stdlib/errors/overlay.go +0 -12
  172. package/stdlib/maps/iter.go +0 -65
  173. package/stdlib/slices/extra.go +0 -73
  174. package/stdlib/slices/iter.go +0 -113
  175. package/stdlib/strings/extra.go +0 -35
  176. package/stdlib/strings/iter.go +0 -122
  177. package/stdlib/sync/extra.go +0 -105
  178. package/stdlib/unicode/utf8/bytes.go +0 -239
@@ -1,1167 +0,0 @@
1
- // src/backend/js/stdlib/json.js
2
- // CodeGen for Go encoding/json package with compile-time static encoders and decoders.
3
-
4
- import { someChild } from "../../../lower/walk.js";
5
- import { typedArrayConstructorForElem } from "../../../typechecker/types.js";
6
-
7
- function safeIdent(name) {
8
- return String(name || "").replace(/[^a-zA-Z0-9_$]/g, "_");
9
- }
10
-
11
- function declareCodec(fnName, params, body) {
12
- return `var ${fnName} = typeof ${fnName} !== "undefined" ? ${fnName} : function ${fnName}(${params}) {\n${body}\n};`;
13
- }
14
-
15
- function typeCodecName(t, fallback = "val") {
16
- if (!t) return fallback;
17
- if (t.kind === "pointer" && t.base) {
18
- return `${typeCodecName(t.base, fallback)}_ptr`;
19
- }
20
- if (t.name) return t.name;
21
- if (t.kind === "slice") return `slice_${typeCodecName(t.elem, "elem")}`;
22
- if (t.kind === "map")
23
- return `map_${typeCodecName(t.value ?? t.elem, "elem")}`;
24
- return fallback;
25
- }
26
-
27
- function unwrapNamed(t) {
28
- return t?.kind === "named" ? t.underlying : t;
29
- }
30
-
31
- function isRawMessageType(t) {
32
- return (
33
- t?.name === "json.RawMessage" || t?.name === "encoding/json.RawMessage"
34
- );
35
- }
36
-
37
- const NUMBER_NAMES = new Set(["json.Number", "encoding/json.Number"]);
38
-
39
- function isNumberType(t) {
40
- if (!t) return false;
41
- const name = t.name ?? "";
42
- if (NUMBER_NAMES.has(name)) return true;
43
- if (name.startsWith("Number$$") || name.endsWith(".Number")) return true;
44
- return (
45
- t.kind === "named" && name === "Number" && unwrapNamed(t)?.name === "string"
46
- );
47
- }
48
-
49
- function isByteSliceType(t) {
50
- const base = unwrapNamed(t);
51
- return (
52
- base?.kind === "slice" &&
53
- (base.elem?.name === "byte" || base.elem?.name === "uint8") &&
54
- !isRawMessageType(t)
55
- );
56
- }
57
-
58
- function isJsonNullAssignable(type) {
59
- const base = unwrapNamed(type);
60
- return (
61
- base?.kind === "pointer" ||
62
- base?.kind === "slice" ||
63
- base?.kind === "map" ||
64
- base?.kind === "interface" ||
65
- (base?.kind === "basic" && base.name === "any")
66
- );
67
- }
68
-
69
- // Field kinds the `,string` tag option applies to (Go: bool, ints, uints, floats, string).
70
- const QUOTABLE_KINDS = new Set([
71
- "string",
72
- "bool",
73
- "int",
74
- "int8",
75
- "int16",
76
- "int32",
77
- "int64",
78
- "uint",
79
- "uint8",
80
- "uint16",
81
- "uint32",
82
- "uint64",
83
- "uintptr",
84
- "byte",
85
- "rune",
86
- "float32",
87
- "float64",
88
- ]);
89
-
90
- // reflect's names for GoFront's basic aliases, used in decode error messages.
91
- const BASIC_DISPLAY = { any: "interface {}", byte: "uint8", rune: "int32" };
92
-
93
- // Unmarshal targets whose argument errors depend on the static type alone:
94
- // pointers can be nil, interfaces are judged by their dynamic value.
95
- // Only statically concrete non-pointer kinds are rejected up front; anything
96
- // else (type parameters, unknown kinds) is judged by its dynamic value.
97
- const TARGET_KIND = { pointer: "ptr" };
98
- const VALUE_TARGET_KINDS = new Set([
99
- "slice",
100
- "array",
101
- "map",
102
- "struct",
103
- "func",
104
- "chan",
105
- ]);
106
-
107
- const COMPOSITE_KINDS = new Set(["slice", "array", "map"]);
108
- const COMPOSITE_DECODERS = {
109
- slice: "_getSliceDecoder",
110
- array: "_getArrayDecoder",
111
- map: "_getMapDecoder",
112
- };
113
-
114
- // Basic kinds decoded by the typed scalar decoders.
115
- const SCALAR_KINDS = QUOTABLE_KINDS;
116
-
117
- // Methods live on the named type, or on its underlying struct for struct types.
118
- function lookupMethod(type, name) {
119
- if (type?.kind !== "named") return null;
120
- return type.methods?.get(name) ?? type.underlying?.methods?.get(name) ?? null;
121
- }
122
-
123
- export const jsonMethods = {
124
- _genJson(fn, a, expr) {
125
- this.useHelper("json");
126
- this.useHelper("base64");
127
- this.useHelper("error");
128
- this._markJsonPtrRecvMarshalers();
129
- const args = a();
130
- if (fn === "Marshal") {
131
- this._markJsonAnyEncoders();
132
- const argType = expr.args[0]?._type;
133
- const enc = this._getJsonEncoder(argType);
134
- return `__json_marshal(${enc}, ${args[0]})`;
135
- }
136
- if (fn === "MarshalIndent") {
137
- this._markJsonAnyEncoders();
138
- const argType = expr.args[0]?._type;
139
- const enc = this._getJsonEncoder(argType);
140
- return `__json_marshal_indent(${enc}, ${args[0]}, ${args[1]}, ${args[2]})`;
141
- }
142
- if (fn === "Unmarshal") {
143
- const targetType = expr.args[1]?._type;
144
- const dec = this._getJsonDecoder(targetType);
145
- return `__json_unmarshal(${dec}, ${args[0]}, ${args[1]}, ${this._jsonTargetArgs(targetType)})`;
146
- }
147
- if (fn === "Valid") {
148
- return `__json_valid(${args[0]})`;
149
- }
150
- return undefined;
151
- },
152
-
153
- // Name and static kind of an Unmarshal/Decode target, for the argument
154
- // errors Go reports from the type alone ("ptr", "val" or none for interfaces).
155
- _jsonTargetArgs(targetType) {
156
- const base = unwrapNamed(targetType);
157
- const key = base?.kind === "basic" ? base.name : base?.kind;
158
- if (!targetType || key === "nil") return '"", ""';
159
- const isValue =
160
- base?.kind === "basic" ? key !== "any" : VALUE_TARGET_KINDS.has(key);
161
- const kind = TARGET_KIND[key] ?? (isValue ? "val" : "");
162
- return `${JSON.stringify(this._jsonDisplayType(targetType))}, ${JSON.stringify(kind)}`;
163
- },
164
-
165
- _getJsonEncoder(type, addr = false) {
166
- if (!type) return "__json_enc_any";
167
- if (isNumberType(type)) return "__json_enc_number";
168
- if (isRawMessageType(type)) {
169
- const fnName = "__json_enc_RawMessage";
170
- if (!this._jsonCodecs.has(fnName)) {
171
- this._jsonCodecs.set(
172
- fnName,
173
- declareCodec(fnName, "v", ` return __json_enc_raw(v);`),
174
- );
175
- }
176
- return fnName;
177
- }
178
-
179
- // Go calls a pointer-receiver MarshalJSON only on addressable values.
180
- const marshaler = this._jsonMarshalerInfo(type);
181
- if (marshaler && (!marshaler.ptrRecv || addr)) {
182
- return this._getMarshalerEncoder(type, marshaler);
183
- }
184
-
185
- const base = unwrapNamed(type);
186
- if (isByteSliceType(type)) return "__json_b64";
187
-
188
- if (base?.kind === "struct") {
189
- const needsAddr = addr && this._jsonNeedsAddr(type);
190
- const typeName = safeIdent((type.name ?? "AnonStruct").split(".").pop());
191
- const fnName = `__json_enc_${typeName}${needsAddr ? "_addr" : ""}`;
192
- if (!this._jsonCodecs.has(fnName)) {
193
- this._jsonCodecs.set(fnName, "");
194
- const code = this._buildStructEncoder(type, fnName, needsAddr);
195
- this._jsonCodecs.set(fnName, code);
196
- }
197
- return fnName;
198
- }
199
-
200
- if (type.kind === "pointer" && type.base) {
201
- const innerEnc = this._getJsonEncoder(type.base, true);
202
- // Pointers to structs are the struct object; other pointers are { value } boxes.
203
- const deref = unwrapNamed(type.base)?.kind === "struct" ? "v" : "v.value";
204
- const fnName = `__json_enc_ptr_${safeIdent(typeCodecName(type.base, "val").split(".").pop())}`;
205
- if (!this._jsonCodecs.has(fnName)) {
206
- this._jsonCodecs.set(
207
- fnName,
208
- declareCodec(
209
- fnName,
210
- "v",
211
- ` return (v === null || v === undefined) ? "null" : ${innerEnc}(${deref});`,
212
- ),
213
- );
214
- }
215
- return fnName;
216
- }
217
-
218
- if (base?.kind === "slice" || base?.kind === "array") {
219
- const elemEnc = this._getJsonEncoder(base.elem, true);
220
- const fnName = `__json_enc_slice_${safeIdent(typeCodecName(base.elem, "elem").split(".").pop())}`;
221
- if (!this._jsonCodecs.has(fnName)) {
222
- this._jsonCodecs.set(
223
- fnName,
224
- declareCodec(
225
- fnName,
226
- "v",
227
- ` return __json_enc_slice(v, ${elemEnc});`,
228
- ),
229
- );
230
- }
231
- return fnName;
232
- }
233
-
234
- if (base?.kind === "map") {
235
- const valType = base.value ?? base.elem;
236
- const valEnc = this._getJsonEncoder(valType, false);
237
- const fnName = `__json_enc_map_${safeIdent(typeCodecName(valType, "val").split(".").pop())}`;
238
- if (!this._jsonCodecs.has(fnName)) {
239
- this._jsonCodecs.set(
240
- fnName,
241
- declareCodec(fnName, "v", ` return __json_enc_map(v, ${valEnc});`),
242
- );
243
- }
244
- return fnName;
245
- }
246
-
247
- if (base?.kind === "basic") {
248
- return this._getBasicEncoder(base.name);
249
- }
250
-
251
- return "__json_enc_any";
252
- },
253
-
254
- // Tags pointer-receiver MarshalJSON methods so the dynamic `any` encoder can skip
255
- // them for non-addressable values, as Go does.
256
- _markJsonPtrRecvMarshalers() {
257
- if (this._jsonPtrRecvMarked) return;
258
- this._jsonPtrRecvMarked = true;
259
- for (const [name, type] of this.checker?.types ?? []) {
260
- if (name.includes(".") || type?.kind !== "named") continue;
261
- if (!lookupMethod(type, "MarshalJSON")?._ptrRecv) continue;
262
- const cls = safeIdent(name);
263
- this._jsonCodecs.set(
264
- `__json_ptrrecv_${cls}`,
265
- `if (typeof ${cls} === "function" && ${cls}.prototype.MarshalJSON) ${cls}.prototype.MarshalJSON.__ptrRecv = true;`,
266
- );
267
- }
268
- },
269
-
270
- // Structs reach the dynamic `any` encoder as bare instances, so each package
271
- // struct without a MarshalJSON method gets a hidden hook that runs its static
272
- // encoder (tags, omitempty, promoted fields) instead of listing its properties.
273
- // A non-generic struct declared in this package that has no MarshalJSON.
274
- _isPlainPackageStruct(name, type) {
275
- const isStruct =
276
- type?.kind === "named" && type.underlying?.kind === "struct";
277
- const generic = type?.typeArgs?.length || type?._genericOf;
278
- if (!isStruct || generic || /[.$[]/.test(name) || type.name !== name)
279
- return false;
280
- return !this._jsonMarshalerInfo(type) && this.structNames?.has(name);
281
- },
282
-
283
- _markJsonAnyEncoders() {
284
- this._jsonAnyMarked = true;
285
- },
286
-
287
- _isJsonEncoderName(recvName) {
288
- return (
289
- recvName === "json.Encoder" ||
290
- recvName === "encoding/json.Encoder" ||
291
- recvName?.startsWith("Encoder$$")
292
- );
293
- },
294
-
295
- // True when `node` contains a call that marks the any-encoders when
296
- // generated (`json.Marshal`, `json.MarshalIndent`, `Encoder.Encode`).
297
- // Decided before any body is generated, so a function-local struct can get
298
- // its hook right after its class even when the call comes later.
299
- _usesJsonAnyMarshal(node) {
300
- if (Array.isArray(node))
301
- return node.some((n) => this._usesJsonAnyMarshal(n));
302
- if (!node || typeof node !== "object") return false;
303
- if (node.kind === "CallExpr" && node.func?.kind === "SelectorExpr") {
304
- const { expr, field } = node.func;
305
- if (
306
- expr?.kind === "Ident" &&
307
- expr.name === "json" &&
308
- (field === "Marshal" || field === "MarshalIndent")
309
- )
310
- return true;
311
- if (
312
- field === "Encode" &&
313
- this._isJsonEncoderName(this._resolveRecvName(expr?._type))
314
- )
315
- return true;
316
- }
317
- return someChild(node, (n) => this._usesJsonAnyMarshal(n));
318
- },
319
-
320
- _jsonAnyHook(cls, enc) {
321
- return `Object.defineProperty(${cls}.prototype, "__json_enc", { value: function() { return ${enc}(this); }, configurable: true, writable: true });`;
322
- },
323
-
324
- // Runs once all function bodies are generated; package-level structs get
325
- // their hook at top level (function-local ones get it in _noteLocalTypeDecl).
326
- _emitJsonAnyHooks() {
327
- if (!this._jsonAnyMarked || this._jsonAnyHooked) return;
328
- this._jsonAnyHooked = true;
329
- const local = this._localTypeNames;
330
- for (const [name, type] of this.checker?.types ?? []) {
331
- if (local.has(name) || !this._isPlainPackageStruct(name, type)) continue;
332
- const cls = safeIdent(name);
333
- this._jsonCodecs.set(
334
- `__json_anyenc_${cls}`,
335
- this._jsonAnyHook(cls, this._getJsonEncoder(type, false)),
336
- );
337
- }
338
- },
339
-
340
- // A function-local struct class is only in scope inside its function, so
341
- // its hook goes right after the class, and only when the program marshals.
342
- _noteLocalTypeDecl(stmt) {
343
- this._localTypeNames.add(stmt.name);
344
- if (!this._jsonAnyUsed || stmt.type.kind !== "StructType") return;
345
- const type = this.checker?.types.get(stmt.name);
346
- if (!this._isPlainPackageStruct(stmt.name, type)) return;
347
- const enc = this._getJsonEncoder(type, false);
348
- this.line(this._jsonAnyHook(safeIdent(stmt.name), enc));
349
- },
350
-
351
- _jsonTypeName(type) {
352
- const name = type?.name ?? "value";
353
- const at = name.indexOf("$$");
354
- if (at >= 0) {
355
- const orig = name.slice(0, at);
356
- const modPath = name.slice(at + 2).replaceAll("$", "/");
357
- return `${modPath}.${orig}`;
358
- }
359
- if (name.includes(".")) return name;
360
- const pkg = this.checker?.pkgName;
361
- return pkg ? `${pkg}.${name}` : name;
362
- },
363
-
364
- _jsonClassName(type) {
365
- return String(type?.name ?? "Object")
366
- .split(".")
367
- .pop();
368
- },
369
-
370
- // How to call `name` (MarshalJSON / UnmarshalJSON) on values of `type`, or null.
371
- // Struct types dispatch on the instance; named non-struct types (raw values)
372
- // call the static `T.$name(recv, ...)` with a { value } box for pointer receivers.
373
- _jsonMethodInfo(type, name) {
374
- const method = lookupMethod(type, name);
375
- if (!method || isRawMessageType(type)) return null;
376
- const isStruct = type.underlying?.kind === "struct";
377
- if (!isStruct && !this._namedBoxInfo(type)) return null;
378
- return {
379
- ptrRecv: Boolean(method._ptrRecv),
380
- isStruct,
381
- cls: this._jsonClassName(type),
382
- };
383
- },
384
-
385
- _jsonMarshalerInfo(type) {
386
- return this._jsonMethodInfo(type, "MarshalJSON");
387
- },
388
-
389
- _jsonUnmarshalerInfo(type) {
390
- return this._jsonMethodInfo(type, "UnmarshalJSON");
391
- },
392
-
393
- // UnmarshalJSON call for a target reachable through `addrExpr`: the struct
394
- // instance for struct types, a { value } box otherwise.
395
- _jsonUnmarshalCall(info, addrExpr, bytesExpr) {
396
- if (info.isStruct) return `${addrExpr}.UnmarshalJSON(${bytesExpr})`;
397
- const recv = info.ptrRecv ? addrExpr : `${addrExpr}.value`;
398
- return `${info.cls}.$UnmarshalJSON(${recv}, ${bytesExpr})`;
399
- },
400
-
401
- // True when encoding an addressable `type` differs from a non-addressable one,
402
- // i.e. it (or a value field inside it) has a pointer-receiver MarshalJSON.
403
- _jsonNeedsAddr(type, seen = new Set()) {
404
- if (!type || seen.has(type)) return false;
405
- seen.add(type);
406
- const marshaler = this._jsonMarshalerInfo(type);
407
- if (marshaler) return marshaler.ptrRecv;
408
- const base = unwrapNamed(type);
409
- if (base?.kind !== "struct") return false;
410
- const fields = this.checker?.resolveJsonFields(base) ?? [];
411
- return fields.some((f) => this._jsonNeedsAddr(f.type, seen));
412
- },
413
-
414
- _getMarshalerEncoder(type, info) {
415
- const fnName = `__json_enc_${safeIdent(info.cls)}_marshal`;
416
- if (!this._jsonCodecs.has(fnName)) {
417
- let call;
418
- if (info.isStruct) {
419
- call = ` if (v === null || v === undefined) return "null";\n var res = v.MarshalJSON();`;
420
- } else {
421
- const recv = info.ptrRecv ? "{ value: v }" : "v";
422
- call = ` var res = ${info.cls}.$MarshalJSON(${recv});`;
423
- }
424
- this._jsonCodecs.set(
425
- fnName,
426
- declareCodec(
427
- fnName,
428
- "v",
429
- `${call}\n return __json_marshaler_out(res, ${JSON.stringify(this._jsonTypeName(type))});`,
430
- ),
431
- );
432
- }
433
- return fnName;
434
- },
435
-
436
- _getUnmarshalerDecoder(type) {
437
- const info = this._jsonUnmarshalerInfo(type);
438
- const fnName = `__json_dec_${safeIdent(info.cls)}_unmarshal`;
439
- if (!this._jsonCodecs.has(fnName)) {
440
- const bytes =
441
- "__json_raw_bytes(rawStr !== undefined ? rawStr : JSON.stringify(parsed))";
442
- const body = info.isStruct
443
- ? ` var inst = target && typeof target === "object" ? target : new ${info.cls}();\n var err = ${this._jsonUnmarshalCall(info, "inst", bytes)};\n if (err) return __json_user_err(err);\n if (box) box.value = inst;\n return null;`
444
- : ` var addr = box || { value: target };\n var err = ${this._jsonUnmarshalCall(info, "addr", bytes)};\n if (err) return __json_user_err(err);\n return null;`;
445
- this._jsonCodecs.set(
446
- fnName,
447
- declareCodec(fnName, "parsed, target, box, rawData, rawStr", body),
448
- );
449
- }
450
- return fnName;
451
- },
452
-
453
- _unmarshalerElemDecoder(elemType) {
454
- const isPtr = elemType?.kind === "pointer";
455
- const valueType = isPtr ? elemType.base : elemType;
456
- const info = this._jsonUnmarshalerInfo(valueType);
457
- if (!info) return null;
458
- let make;
459
- if (info.isStruct) {
460
- make = (rawExpr) =>
461
- `(() => { var inst = new ${info.cls}(); var uerr = ${this._jsonUnmarshalCall(info, "inst", `__json_raw_bytes(${rawExpr})`)}; return uerr ? __json_user_err(uerr) : inst; })()`;
462
- } else {
463
- // Pointer elements are the { value } box itself; value elements its value.
464
- const zero = this.zeroValueForType(valueType);
465
- const result = isPtr ? "__b" : "__b.value";
466
- make = (rawExpr) =>
467
- `(() => { var __b = { value: ${zero} }; var uerr = ${this._jsonUnmarshalCall(info, "__b", `__json_raw_bytes(${rawExpr})`)}; return uerr ? __json_user_err(uerr) : ${result}; })()`;
468
- }
469
- if (isPtr)
470
- return (v, rawExpr) => `(${v} === null ? null : ${make(rawExpr)})`;
471
- return (_v, rawExpr) => make(rawExpr);
472
- },
473
-
474
- // Returns the basic kind a `,string` field is quoted as, or null if the option does not apply.
475
- _jsonQuotedKind(f) {
476
- if (!f.quoted) return null;
477
- const t = f.type?.kind === "pointer" ? f.type.base : f.type;
478
- if (this._jsonMarshalerInfo(t)) return null;
479
- const base = unwrapNamed(t);
480
- return base?.kind === "basic" && QUOTABLE_KINDS.has(base.name)
481
- ? base.name
482
- : null;
483
- },
484
-
485
- _genQuotedFieldEncoderExpr(f, kind) {
486
- const isPtr = f.type?.kind === "pointer";
487
- const basic = this._genBasicFieldExpr(kind);
488
- const quote = (x) => {
489
- const inner = basic(x);
490
- return kind === "string"
491
- ? `__json_esc_noh(${inner})`
492
- : `('"' + ${inner} + '"')`;
493
- };
494
- if (!isPtr) return quote;
495
- return (v) =>
496
- `(${v} === null || ${v} === undefined ? "null" : ${quote(`${v}.value`)})`;
497
- },
498
-
499
- _getBasicEncoder(basicName) {
500
- switch (basicName) {
501
- case "string":
502
- return "__json_esc";
503
- case "bool":
504
- return "(v => v ? 'true' : 'false')";
505
- case "int":
506
- case "int8":
507
- case "int16":
508
- case "int32":
509
- case "uint":
510
- case "uint8":
511
- case "uint16":
512
- case "uint32":
513
- case "byte":
514
- case "rune":
515
- return "(v => String(Math.trunc(v)))";
516
- case "int64":
517
- case "uint64":
518
- return "(v => typeof v === 'bigint' ? v.toString() : String(v))";
519
- case "float32":
520
- return "__json_float32";
521
- case "float64":
522
- return "__json_float64";
523
- default:
524
- return "__json_enc_any";
525
- }
526
- },
527
-
528
- _buildStructEncoder(structType, fnName, addr = false) {
529
- const underlying = unwrapNamed(structType);
530
- const fields = this.checker?.resolveJsonFields(underlying) ?? [];
531
- const lines = [];
532
- lines.push(
533
- `var ${fnName} = typeof ${fnName} !== "undefined" ? ${fnName} : function ${fnName}(v) {`,
534
- );
535
- lines.push(` if (v === null || v === undefined) return "null";`);
536
- lines.push(` var parts = [];`);
537
-
538
- for (const f of fields) {
539
- const keyLiteral = JSON.stringify(`${JSON.stringify(f.jsonKey)}:`);
540
- const encodeVal = this._genFieldEncoderExpr(f, addr);
541
- const isStruct = unwrapNamed(f.type)?.kind === "struct";
542
- const isMap = unwrapNamed(f.type)?.kind === "map";
543
-
544
- this._emitFieldEncoderAccess(f, lines, (valName) => {
545
- const encoded = encodeVal(valName);
546
- if (f.omitempty) {
547
- return `if (!__json_is_zero(${valName}, ${isStruct}, ${isMap})) parts.push(${keyLiteral} + ${encoded});`;
548
- }
549
- return `parts.push(${keyLiteral} + ${encoded});`;
550
- });
551
- }
552
-
553
- lines.push(` return "{" + parts.join(",") + "}";`);
554
- lines.push(`};`);
555
- return lines.join("\n");
556
- },
557
-
558
- _emitFieldEncoderAccess(f, lines, onVal) {
559
- const path = f.path ?? [{ name: f.goName, isPtr: false }];
560
- if (f.isEmbedded && f.tagged) {
561
- lines.push(
562
- ` { var val = (v.${f.goName} !== undefined ? v.${f.goName} : v); ${onVal("val")} }`,
563
- );
564
- return;
565
- }
566
- if (path.length === 1) {
567
- lines.push(` { var val = v.${path[0].name}; ${onVal("val")} }`);
568
- return;
569
- }
570
-
571
- // Promoted fields from embedded struct
572
- const ptrSegs = path.slice(0, -1).filter((s) => s.isPtr);
573
- const nilChecks = ptrSegs.map((s) => `v.${s.name} === null`);
574
-
575
- let cur = "v";
576
- const conds = [];
577
- for (let i = 0; i < path.length - 1; i++) {
578
- const seg = path[i];
579
- conds.push(`${cur}.${seg.name} != null`);
580
- cur = `${cur}.${seg.name}`;
581
- }
582
- const leaf = `${cur}.${path[path.length - 1].name}`;
583
-
584
- if (nilChecks.length > 0) {
585
- lines.push(` if (!(${nilChecks.join(" || ")})) {`);
586
- lines.push(
587
- ` var val = (${conds.join(" && ")} ? ${leaf} : v.${f.goName});`,
588
- );
589
- lines.push(` if (val !== undefined) { ${onVal("val")} }`);
590
- lines.push(` }`);
591
- } else {
592
- lines.push(
593
- ` { var val = (${conds.join(" && ")} ? ${leaf} : v.${f.goName}); if (val !== undefined) { ${onVal("val")} } }`,
594
- );
595
- }
596
- },
597
-
598
- _genFieldEncoderExpr(f, addr = false) {
599
- const quotedKind = this._jsonQuotedKind(f);
600
- if (quotedKind) return this._genQuotedFieldEncoderExpr(f, quotedKind);
601
- if (isNumberType(f.type)) return (v) => `__json_enc_number(${v})`;
602
- if (isRawMessageType(f.type)) {
603
- return (v) => `__json_enc_raw(${v})`;
604
- }
605
- // Marshalers and pointers go through the shared encoders.
606
- const special =
607
- f.type?.kind === "pointer" ||
608
- (f.type?.kind === "named" && this._jsonMarshalerInfo(f.type));
609
- if (special) {
610
- const enc = this._getJsonEncoder(f.type, addr);
611
- return (v) => `${enc}(${v})`;
612
- }
613
- if (isByteSliceType(f.type)) {
614
- return (v) => `__json_b64(${v})`;
615
- }
616
- const base = unwrapNamed(f.type);
617
- if (base?.kind === "basic") {
618
- return this._genBasicFieldExpr(base.name);
619
- }
620
- if (base?.kind === "struct") {
621
- const enc = this._getJsonEncoder(f.type, addr);
622
- return (v) => `${enc}(${v})`;
623
- }
624
- if (base?.kind === "slice" || base?.kind === "array") {
625
- const enc = this._getJsonEncoder(base.elem, true);
626
- return (v) => `__json_enc_slice(${v}, ${enc})`;
627
- }
628
- if (base?.kind === "map") {
629
- const enc = this._getJsonEncoder(base.value ?? base.elem, false);
630
- return (v) => `__json_enc_map(${v}, ${enc})`;
631
- }
632
- return (v) => `__json_enc_any(${v})`;
633
- },
634
-
635
- _genBasicFieldExpr(name) {
636
- switch (name) {
637
- case "string":
638
- return (v) => `__json_esc(${v})`;
639
- case "bool":
640
- return (v) => `(${v} ? "true" : "false")`;
641
- case "int":
642
- case "int8":
643
- case "int16":
644
- case "int32":
645
- case "uint":
646
- case "uint8":
647
- case "uint16":
648
- case "uint32":
649
- case "byte":
650
- case "rune":
651
- return (v) => `String(Math.trunc(${v}))`;
652
- case "int64":
653
- case "uint64":
654
- return (v) =>
655
- `(typeof ${v} === "bigint" ? ${v}.toString() : String(${v}))`;
656
- case "float32":
657
- return (v) => `__json_float32(${v})`;
658
- case "float64":
659
- return (v) => `__json_float64(${v})`;
660
- default:
661
- return (v) => `__json_enc_any(${v})`;
662
- }
663
- },
664
-
665
- _getJsonDecoder(targetType) {
666
- if (!targetType) return "__json_dec_any_target";
667
- const baseType =
668
- targetType.kind === "pointer" ? targetType.base : targetType;
669
- if (!baseType) return "__json_dec_any_target";
670
-
671
- if (isNumberType(baseType)) {
672
- return "__json_dec_number";
673
- }
674
-
675
- if (this._jsonUnmarshalerInfo(baseType)) {
676
- return this._getUnmarshalerDecoder(baseType);
677
- }
678
-
679
- if (isRawMessageType(baseType)) {
680
- const fnName = "__json_dec_RawMessage";
681
- if (!this._jsonCodecs.has(fnName)) {
682
- this._jsonCodecs.set(
683
- fnName,
684
- declareCodec(
685
- fnName,
686
- "parsed, target, box, rawData",
687
- ` var u8 = rawData instanceof Uint8Array ? rawData : new Uint8Array(rawData);\n u8.__raw = true;\n if (box) box.value = u8;\n return null;`,
688
- ),
689
- );
690
- }
691
- return fnName;
692
- }
693
-
694
- if (isByteSliceType(baseType)) {
695
- const fnName = "__json_dec_byte_slice";
696
- if (!this._jsonCodecs.has(fnName)) {
697
- this._jsonCodecs.set(
698
- fnName,
699
- declareCodec(
700
- fnName,
701
- "parsed, target, box",
702
- ` if (parsed === null) { if (box) box.value = null; return null; }\n var u8 = __json_bytes_decode(parsed);\n if (u8 && u8._msg) return u8;\n if (box) box.value = u8;\n return null;`,
703
- ),
704
- );
705
- }
706
- return fnName;
707
- }
708
-
709
- const base = unwrapNamed(baseType);
710
- if (base?.kind === "struct") {
711
- const typeName = safeIdent(
712
- (baseType.name ?? "AnonStruct").split(".").pop(),
713
- );
714
- const fnName = `__json_dec_${typeName}`;
715
- if (!this._jsonCodecs.has(fnName)) {
716
- this._jsonCodecs.set(fnName, "");
717
- const code = this._buildStructDecoder(baseType, fnName);
718
- this._jsonCodecs.set(fnName, code);
719
- }
720
- return fnName;
721
- }
722
-
723
- const composite = COMPOSITE_DECODERS[base?.kind];
724
- if (composite) return this[composite](base);
725
- // Top-level scalar targets (`json.Unmarshal(b, &n)`) are type-checked too.
726
- if (base?.kind === "basic" && SCALAR_KINDS.has(base.name))
727
- return this._getScalarDecoder(baseType, base);
728
-
729
- return "__json_dec_any_target";
730
- },
731
-
732
- _getSliceDecoder(base) {
733
- const elemTypeName = typeCodecName(base.elem, "elem");
734
- const fnName = `__json_dec_slice_${safeIdent(elemTypeName.split(".").pop())}`;
735
- if (!this._jsonCodecs.has(fnName)) {
736
- const elemDec = this._genElemDecoder(base.elem);
737
- const typedCtor = typedArrayConstructorForElem(base.elem);
738
- const initOut = typedCtor ? `new ${typedCtor}(parsed.length)` : `[]`;
739
- const setElem = typedCtor ? `out[i] = item;` : `out.push(item);`;
740
- const display = JSON.stringify(`[]${this._jsonDisplayType(base.elem)}`);
741
- this._jsonCodecs.set(
742
- fnName,
743
- declareCodec(
744
- fnName,
745
- "parsed, target, box",
746
- ` if (parsed === null) { if (box) box.value = null; return null; }\n if (!Array.isArray(parsed)) return __json_err("cannot unmarshal " + __json_type(parsed) + " into " + __json_ctx(${display}));\n var out = ${initOut};\n for (var i = 0; i < parsed.length; i++) {\n var item = ${elemDec("parsed[i]", "__json_raw_child(parsed, i)")};\n if (item && item._msg) return item;\n ${setElem}\n }\n if (box) box.value = out;\n return null;`,
747
- ),
748
- );
749
- }
750
- return fnName;
751
- },
752
-
753
- // Arrays decode into a fresh array of the fixed length: surplus JSON elements
754
- // are dropped and missing ones are zero. JSON null leaves the array alone.
755
- _getArrayDecoder(base) {
756
- const elemTypeName = typeCodecName(base.elem, "elem");
757
- const fnName = `__json_dec_array${base.size}_${safeIdent(elemTypeName.split(".").pop())}`;
758
- if (!this._jsonCodecs.has(fnName)) {
759
- this._jsonCodecs.set(
760
- fnName,
761
- declareCodec(
762
- fnName,
763
- "parsed, target, box",
764
- this._arrayDecoderBody(base),
765
- ),
766
- );
767
- }
768
- return fnName;
769
- },
770
-
771
- _arrayDecoderBody(base) {
772
- const elemDec = this._genElemDecoder(base.elem);
773
- const typedCtor = typedArrayConstructorForElem(base.elem);
774
- const display = JSON.stringify(this._jsonDisplayType(base));
775
- const init = `new ${typedCtor ?? "Array"}(${base.size})`;
776
- // Typed arrays start zeroed; plain arrays need their zero values filled in.
777
- const zero = this.zeroValueForType(base.elem);
778
- const fill = typedCtor
779
- ? ""
780
- : `\n for (var z = parsed.length; z < ${base.size}; z++) out[z] = ${zero};`;
781
- return ` if (parsed === null) return null;\n if (!Array.isArray(parsed)) return __json_err("cannot unmarshal " + __json_type(parsed) + " into " + __json_ctx(${display}));\n var out = ${init};\n for (var i = 0; i < parsed.length && i < ${base.size}; i++) {\n var item = ${elemDec("parsed[i]", "__json_raw_child(parsed, i)")};\n if (item && item._msg) return item;\n out[i] = item;\n }${fill}\n if (box) box.value = out;\n return null;`;
782
- },
783
-
784
- _getMapDecoder(base) {
785
- const valType = base.value ?? base.elem;
786
- const valTypeName = typeCodecName(valType, "val");
787
- const fnName = `__json_dec_map_${safeIdent(valTypeName.split(".").pop())}`;
788
- if (!this._jsonCodecs.has(fnName)) {
789
- const valDec = this._genElemDecoder(valType);
790
- const display = JSON.stringify(
791
- `map[string]${this._jsonDisplayType(valType)}`,
792
- );
793
- this._jsonCodecs.set(
794
- fnName,
795
- declareCodec(
796
- fnName,
797
- "parsed, target, box",
798
- ` if (parsed === null) { if (box) box.value = null; return null; }\n if (typeof parsed !== "object" || Array.isArray(parsed)) return __json_err("cannot unmarshal " + __json_type(parsed) + " into " + __json_ctx(${display}));\n var out = __json_newmap();\n for (var k in parsed) {\n if (Object.prototype.hasOwnProperty.call(parsed, k)) {\n var item = ${valDec("parsed[k]", "__json_raw_child(parsed, k)")};\n if (item && item._msg) return item;\n out[k] = item;\n }\n }\n if (box) box.value = out;\n return null;`,
799
- ),
800
- );
801
- }
802
- return fnName;
803
- },
804
-
805
- _getScalarDecoder(baseType, base) {
806
- const display = this._jsonDisplayType(baseType);
807
- const fnName = `__json_dec_scalar_${safeIdent(display)}`;
808
- if (!this._jsonCodecs.has(fnName)) {
809
- this._jsonCodecs.set(
810
- fnName,
811
- declareCodec(
812
- fnName,
813
- "parsed, target, box, rawData, rawStr",
814
- ` if (parsed === null) return null;\n var dv = ${this._jsonScalarDecodeExpr(base.name, display, "parsed", "rawStr")};\n if (dv && dv._msg) return dv;\n if (box) box.value = dv;\n return null;`,
815
- ),
816
- );
817
- }
818
- return fnName;
819
- },
820
-
821
- // Go-style display of a type in decode error messages (reflect's Type.String()).
822
- _jsonDisplayType(t) {
823
- const show = (x) => this._jsonDisplayType(x);
824
- const byKind = {
825
- pointer: () => `*${show(t.base)}`,
826
- named: () => this._jsonTypeName(t),
827
- basic: () => BASIC_DISPLAY[t.name] ?? t.name,
828
- slice: () => `[]${show(t.elem)}`,
829
- array: () => `[${t.size}]${show(t.elem)}`,
830
- map: () => `map[${show(t.key)}]${show(t.value ?? t.elem)}`,
831
- };
832
- return byKind[t?.kind]?.() ?? "interface {}";
833
- },
834
-
835
- // Decodes one parsed JSON scalar into `kind`; yields the value or an error (`_msg`).
836
- _jsonScalarDecodeExpr(kind, display, v, rawExpr) {
837
- const d = JSON.stringify(display);
838
- const raw = rawExpr ?? "undefined";
839
- switch (kind) {
840
- case "string":
841
- return `(typeof ${v} === "string" && !${v}.endsWith("__bigint") ? ${v} : __json_err("cannot unmarshal " + __json_type(${v}) + " into " + __json_ctx(${d})))`;
842
- case "bool":
843
- return `(typeof ${v} === "boolean" ? ${v} : __json_err("cannot unmarshal " + __json_type(${v}) + " into " + __json_ctx(${d})))`;
844
- case "float32":
845
- return `__json_float_decode(${v}, 32, ${d}, ${raw})`;
846
- case "float64":
847
- return `__json_float_decode(${v}, 64, ${d}, ${raw})`;
848
- default:
849
- return `__json_int_decode(${v}, ${JSON.stringify(kind)}, ${d}, ${raw})`;
850
- }
851
- },
852
-
853
- _genElemDecoder(elemType) {
854
- if (!elemType) return (v) => `__json_dec_any(${v})`;
855
- if (isNumberType(elemType))
856
- return (v, rawExpr) =>
857
- `__json_number_decode(${v}, "json.Number", ${rawExpr})`;
858
- const unmarshaler = this._unmarshalerElemDecoder(elemType);
859
- if (unmarshaler) return unmarshaler;
860
- if (isRawMessageType(elemType)) {
861
- return (_v, rawExpr) => `__json_raw_bytes(${rawExpr})`;
862
- }
863
- if (isByteSliceType(elemType)) {
864
- return (v) => `(${v} === null ? null : __json_bytes_decode(${v}))`;
865
- }
866
- const base = unwrapNamed(elemType);
867
- if (base?.kind === "basic" && SCALAR_KINDS.has(base.name)) {
868
- const display = this._jsonDisplayType(elemType);
869
- return (v, rawExpr) =>
870
- this._jsonScalarDecodeExpr(base.name, display, v, rawExpr);
871
- }
872
- const ptrElem = this._pointerElemDecoder(elemType);
873
- if (ptrElem) return ptrElem;
874
- if (base?.kind === "struct") {
875
- const dec = this._getJsonDecoder(elemType);
876
- const structName = elemType.name?.split(".").pop() ?? "Object";
877
- return (v) =>
878
- `(() => { var inst = new ${structName}(); var err = ${dec}(${v}, inst); return err || inst; })()`;
879
- }
880
- return this._compositeElemDecoder(elemType, base);
881
- },
882
-
883
- // `*Struct` and `*scalar` elements; null stays nil.
884
- _pointerElemDecoder(elemType) {
885
- if (elemType?.kind !== "pointer" || !elemType.base) return null;
886
- const innerBase = unwrapNamed(elemType.base);
887
- if (innerBase?.kind === "struct") {
888
- const dec = this._getJsonDecoder(elemType.base);
889
- const structName = elemType.base.name?.split(".").pop() ?? "Object";
890
- return (v) =>
891
- `(${v} === null ? null : (() => { var inst = new ${structName}(); var err = ${dec}(${v}, inst); return err || inst; })())`;
892
- }
893
- if (innerBase?.kind !== "basic") return null;
894
- const basicDec = this._genElemDecoder(elemType.base);
895
- return (v, rawExpr) =>
896
- `(${v} === null ? null : (() => { var item = ${basicDec(v, rawExpr)}; return item && item._msg ? item : { value: item, __p: true }; })())`;
897
- },
898
-
899
- // Slices, arrays and maps decode through their own codec into a fresh box.
900
- _compositeElemDecoder(elemType, base) {
901
- if (!COMPOSITE_KINDS.has(base?.kind)) return (v) => `__json_dec_any(${v})`;
902
- const dec = this._getJsonDecoder(elemType);
903
- return (v) =>
904
- `(() => { var b = { value: null }; var err = ${dec}(${v}, null, b); return err || b.value; })()`;
905
- },
906
-
907
- _buildStructDecoder(structType, fnName) {
908
- const underlying = unwrapNamed(structType);
909
- const fields = this.checker?.resolveJsonFields(underlying) ?? [];
910
- const structName = structType.name?.split(".").pop() ?? "T";
911
- const lines = [];
912
- lines.push(
913
- `var ${fnName} = typeof ${fnName} !== "undefined" ? ${fnName} : function ${fnName}(parsed, target, box) {`,
914
- );
915
- lines.push(
916
- ` if (parsed === null) { if (box) box.value = null; return null; }`,
917
- );
918
- lines.push(` if (typeof parsed !== "object" || Array.isArray(parsed)) {`);
919
- lines.push(
920
- ` return __json_err("cannot unmarshal " + __json_type(parsed) + " into " + __json_ctx(${JSON.stringify(this._jsonDisplayType(structType))}));`,
921
- );
922
- lines.push(` }`);
923
- const knownKeys = fields.flatMap((f) => [f.jsonKey, f.goName]);
924
- lines.push(
925
- ` var disallowErr = __json_check_unknown(parsed, ${JSON.stringify(knownKeys)});`,
926
- );
927
- lines.push(` if (disallowErr) return disallowErr;`);
928
- // Go reports errors as "Go struct field <innermost struct>.<json key path>".
929
- lines.push(` __json_structs.push(${JSON.stringify(structName)});`);
930
- lines.push(` try {`);
931
-
932
- for (const f of fields) {
933
- const key = JSON.stringify(f.jsonKey);
934
- const goName = JSON.stringify(f.goName);
935
- lines.push(
936
- ` { var fieldKey = __json_match_field(parsed, ${key}, ${goName}); if (fieldKey !== null) {`,
937
- );
938
- lines.push(` __json_path.push(${key});`);
939
- lines.push(` try {`);
940
- lines.push(` var val = parsed[fieldKey];`);
941
- lines.push(` var raw = __json_raw_child(parsed, fieldKey);`);
942
- lines.push(
943
- ` __json_current_offset = __json_raw_end(parsed, fieldKey) || 0;`,
944
- );
945
- const quotedKind = this._jsonQuotedKind(f);
946
- if (quotedKind) {
947
- const valueType = f.type?.kind === "pointer" ? f.type.base : f.type;
948
- const typeStr = this._jsonDisplayType(valueType);
949
- lines.push(
950
- ` var q = __json_unquote(val, ${JSON.stringify(quotedKind)}, ${JSON.stringify(typeStr)}); if (q && q._msg) return q; val = q.v; raw = q.lit;`,
951
- );
952
- }
953
- lines.push(` if (val !== null) {`);
954
- this._emitFieldDecoder(f, structName, lines, "raw");
955
- lines.push(` } else {`);
956
- this._emitFieldNull(f, lines, "raw");
957
- lines.push(` }`);
958
- lines.push(` } finally { __json_path.pop(); }`);
959
- lines.push(` }`);
960
- lines.push(` }`);
961
- }
962
-
963
- lines.push(` } finally { __json_structs.pop(); }`);
964
- lines.push(` return null;`);
965
- lines.push(`};`);
966
- return lines.join("\n");
967
- },
968
-
969
- _emitUnmarshalerField(f, lines, rawExpr) {
970
- const isPtr = f.type?.kind === "pointer";
971
- const valueType = isPtr ? f.type.base : f.type;
972
- const info = this._jsonUnmarshalerInfo(valueType);
973
- const bytes = `__json_raw_bytes(${rawExpr})`;
974
- if (info.isStruct) {
975
- lines.push(` var sub = target.${f.goName};`);
976
- lines.push(
977
- ` if (!sub) { sub = new ${info.cls}(); target.${f.goName} = sub; }`,
978
- );
979
- lines.push(
980
- ` var uerr = ${this._jsonUnmarshalCall(info, "sub", bytes)};`,
981
- );
982
- } else if (isPtr) {
983
- lines.push(` var sub = target.${f.goName};`);
984
- lines.push(
985
- ` if (!sub) { sub = { value: ${this.zeroValueForType(valueType)} }; target.${f.goName} = sub; }`,
986
- );
987
- lines.push(
988
- ` var uerr = ${this._jsonUnmarshalCall(info, "sub", bytes)};`,
989
- );
990
- } else {
991
- lines.push(
992
- ` var addr = { get value() { return target.${f.goName}; }, set value(__v) { target.${f.goName} = __v; } };`,
993
- );
994
- lines.push(
995
- ` var uerr = ${this._jsonUnmarshalCall(info, "addr", bytes)};`,
996
- );
997
- }
998
- lines.push(` if (uerr) return __json_user_err(uerr);`);
999
- if (f.isEmbedded && info.isStruct) {
1000
- lines.push(` Object.assign(target, sub);`);
1001
- }
1002
- },
1003
-
1004
- _emitNumberFieldDecoder(f, lines, rawExpr) {
1005
- lines.push(
1006
- ` var nv = __json_number_decode(val, "json.Number", ${rawExpr}); if (nv && nv._msg) return nv;`,
1007
- );
1008
- const val = f.type?.kind === "pointer" ? "{ value: nv }" : "nv";
1009
- this._emitAssign(f, val, lines);
1010
- },
1011
-
1012
- _emitFieldDecoder(f, structName, lines, rawExpr) {
1013
- const valueType = f.type?.kind === "pointer" ? f.type.base : f.type;
1014
- if (this._jsonUnmarshalerInfo(valueType)) {
1015
- this._emitUnmarshalerField(f, lines, rawExpr);
1016
- return;
1017
- }
1018
- if (isNumberType(valueType)) {
1019
- this._emitNumberFieldDecoder(f, lines, rawExpr);
1020
- return;
1021
- }
1022
- if (isRawMessageType(f.type)) {
1023
- lines.push(
1024
- ` var rawBytes = __json_raw_bytes(${rawExpr}); rawBytes.__raw = true;`,
1025
- );
1026
- this._emitAssign(f, "rawBytes", lines);
1027
- return;
1028
- }
1029
- if (isByteSliceType(f.type)) {
1030
- lines.push(` var u8v = __json_bytes_decode(val);`);
1031
- lines.push(` if (u8v && u8v._msg) return u8v;`);
1032
- this._emitAssign(f, "u8v", lines);
1033
- return;
1034
- }
1035
-
1036
- const base = unwrapNamed(f.type);
1037
- if (base?.kind === "basic") {
1038
- this._emitBasicFieldDecoder(f, base.name, structName, lines);
1039
- return;
1040
- }
1041
-
1042
- if (f.type?.kind === "pointer" && f.type.base) {
1043
- const innerBase = unwrapNamed(f.type.base);
1044
- if (innerBase?.kind === "basic") {
1045
- this._emitPointerBasicFieldDecoder(
1046
- f,
1047
- innerBase.name,
1048
- structName,
1049
- lines,
1050
- );
1051
- return;
1052
- }
1053
- if (innerBase?.kind === "struct") {
1054
- const innerName = f.type.base.name?.split(".").pop() ?? "Object";
1055
- const dec = this._getJsonDecoder(f.type.base);
1056
- lines.push(` var sub = target.${f.goName};`);
1057
- lines.push(
1058
- ` if (!sub) { sub = new ${innerName}(); target.${f.goName} = sub; }`,
1059
- );
1060
- lines.push(` var err = ${dec}(val, sub);`);
1061
- lines.push(` if (err) return err;`);
1062
- if (f.isEmbedded) {
1063
- lines.push(` Object.assign(target, sub);`);
1064
- }
1065
- return;
1066
- }
1067
- }
1068
-
1069
- if (base?.kind === "struct") {
1070
- const innerName = f.type.name?.split(".").pop() ?? "Object";
1071
- const dec = this._getJsonDecoder(f.type);
1072
- lines.push(` var sub = target.${f.goName};`);
1073
- lines.push(
1074
- ` if (!sub) { sub = new ${innerName}(); target.${f.goName} = sub; }`,
1075
- );
1076
- lines.push(` var err = ${dec}(val, sub);`);
1077
- lines.push(` if (err) return err;`);
1078
- if (f.isEmbedded) {
1079
- lines.push(` Object.assign(target, sub);`);
1080
- }
1081
- return;
1082
- }
1083
-
1084
- if (COMPOSITE_KINDS.has(base?.kind)) {
1085
- const dec = this._getJsonDecoder(f.type);
1086
- lines.push(` var box = { value: null };`);
1087
- lines.push(` var err = ${dec}(val, null, box);`);
1088
- lines.push(` if (err) return err;`);
1089
- this._emitAssign(f, "box.value", lines);
1090
- return;
1091
- }
1092
-
1093
- this._emitAssign(f, "__json_dec_any(val)", lines);
1094
- },
1095
-
1096
- // `*T` scalar fields: Go reports the element type in errors.
1097
- _emitPointerBasicFieldDecoder(f, basicName, _structName, lines) {
1098
- if (!SCALAR_KINDS.has(basicName)) {
1099
- this._emitAssign(f, "__json_dec_any(val)", lines);
1100
- return;
1101
- }
1102
- const display = this._jsonDisplayType(f.type.base);
1103
- lines.push(
1104
- ` var dv = ${this._jsonScalarDecodeExpr(basicName, display, "val", "raw")};`,
1105
- );
1106
- lines.push(` if (dv && dv._msg) return dv;`);
1107
- this._emitAssign(f, "{ value: dv, __p: true }", lines);
1108
- },
1109
-
1110
- _emitBasicFieldDecoder(f, basicName, _structName, lines) {
1111
- if (!SCALAR_KINDS.has(basicName)) {
1112
- this._emitAssign(f, "__json_dec_any(val)", lines);
1113
- return;
1114
- }
1115
- const display = this._jsonDisplayType(f.type);
1116
- lines.push(
1117
- ` var dv = ${this._jsonScalarDecodeExpr(basicName, display, "val", "raw")};`,
1118
- );
1119
- lines.push(` if (dv && dv._msg) return dv;`);
1120
- this._emitAssign(f, "dv", lines);
1121
- },
1122
-
1123
- _emitAssign(f, expr, lines) {
1124
- const path = f.path ?? [{ name: f.goName, isPtr: false }];
1125
- if (path.length === 1) {
1126
- lines.push(` target.${f.goName} = ${expr};`);
1127
- return;
1128
- }
1129
-
1130
- for (let i = 0; i < path.length - 1; i++) {
1131
- const seg = path[i];
1132
- const curPath = `target.${path
1133
- .slice(0, i + 1)
1134
- .map((p) => p.name)
1135
- .join(".")}`;
1136
- const ctor = seg.typeName || seg.name;
1137
- const initVal = ctor && ctor !== "Object" ? `new ${ctor}()` : `{}`;
1138
- lines.push(` if (${curPath} == null) ${curPath} = ${initVal};`);
1139
- }
1140
- lines.push(` target.${path.map((p) => p.name).join(".")} = ${expr};`);
1141
- lines.push(` target.${f.goName} = ${expr};`);
1142
- },
1143
-
1144
- _emitFieldNull(f, lines, rawExpr) {
1145
- // Go hands JSON null to UnmarshalJSON for non-pointer fields; nil pointers stay nil.
1146
- if (f.type?.kind !== "pointer" && this._jsonUnmarshalerInfo(f.type)) {
1147
- this._emitUnmarshalerField(f, lines, `"null"`);
1148
- return;
1149
- }
1150
- if (isRawMessageType(f.type)) {
1151
- lines.push(
1152
- ` var rawNull = __json_raw_bytes(${rawExpr}); rawNull.__raw = true;`,
1153
- );
1154
- this._emitAssign(f, "rawNull", lines);
1155
- return;
1156
- }
1157
- if (!isJsonNullAssignable(f.type)) return;
1158
- const path = f.path ?? [{ name: f.goName, isPtr: false }];
1159
- if (path.length === 1) {
1160
- lines.push(` target.${f.goName} = null;`);
1161
- return;
1162
- }
1163
- lines.push(
1164
- ` target.${path.map((p) => p.name).join(".")} = null; target.${f.goName} = null;`,
1165
- );
1166
- },
1167
- };