goscript 0.2.5 → 0.2.7

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 (264) hide show
  1. package/cmd/goscript/cmd-compile.go +7 -0
  2. package/cmd/goscript/cmd_compile_test.go +83 -0
  3. package/compiler/compile-request.go +3 -0
  4. package/compiler/compiler-cache.go +828 -0
  5. package/compiler/compiler-cache_test.go +705 -0
  6. package/compiler/config.go +2 -0
  7. package/compiler/index.test.ts +26 -1
  8. package/compiler/index.ts +5 -0
  9. package/compiler/lowered-program.go +31 -20
  10. package/compiler/lowering.go +349 -93
  11. package/compiler/lowering_bench_test.go +1 -0
  12. package/compiler/override-facts.go +309 -8
  13. package/compiler/override-parity-verifier.go +45 -1
  14. package/compiler/override-parity-verifier_test.go +100 -0
  15. package/compiler/override-registry_test.go +1 -0
  16. package/compiler/package-graph.go +40 -12
  17. package/compiler/package-graph_test.go +29 -0
  18. package/compiler/runtime-contract.go +8 -0
  19. package/compiler/service.go +98 -11
  20. package/compiler/skeleton_test.go +110 -14
  21. package/compiler/typescript-emitter.go +120 -23
  22. package/dist/compiler/index.d.ts +2 -0
  23. package/dist/compiler/index.js +3 -0
  24. package/dist/compiler/index.js.map +1 -1
  25. package/dist/gs/builtin/builtin.d.ts +24 -33
  26. package/dist/gs/builtin/builtin.js +54 -61
  27. package/dist/gs/builtin/builtin.js.map +1 -1
  28. package/dist/gs/builtin/hostio.d.ts +1 -0
  29. package/dist/gs/builtin/hostio.js +1 -1
  30. package/dist/gs/builtin/hostio.js.map +1 -1
  31. package/dist/gs/builtin/index.d.ts +1 -0
  32. package/dist/gs/builtin/index.js +1 -0
  33. package/dist/gs/builtin/index.js.map +1 -1
  34. package/dist/gs/builtin/panic.d.ts +18 -0
  35. package/dist/gs/builtin/panic.js +98 -0
  36. package/dist/gs/builtin/panic.js.map +1 -0
  37. package/dist/gs/builtin/slice.d.ts +10 -0
  38. package/dist/gs/builtin/slice.js +110 -53
  39. package/dist/gs/builtin/slice.js.map +1 -1
  40. package/dist/gs/builtin/type.js +15 -3
  41. package/dist/gs/builtin/type.js.map +1 -1
  42. package/dist/gs/builtin/varRef.d.ts +1 -1
  43. package/dist/gs/builtin/varRef.js +3 -2
  44. package/dist/gs/builtin/varRef.js.map +1 -1
  45. package/dist/gs/bytes/bytes.gs.js +51 -38
  46. package/dist/gs/bytes/bytes.gs.js.map +1 -1
  47. package/dist/gs/bytes/reader.gs.d.ts +1 -1
  48. package/dist/gs/bytes/reader.gs.js +6 -7
  49. package/dist/gs/bytes/reader.gs.js.map +1 -1
  50. package/dist/gs/cmp/index.d.ts +1 -1
  51. package/dist/gs/cmp/index.js +43 -10
  52. package/dist/gs/cmp/index.js.map +1 -1
  53. package/dist/gs/context/context.d.ts +2 -2
  54. package/dist/gs/context/context.js +1 -1
  55. package/dist/gs/context/context.js.map +1 -1
  56. package/dist/gs/embed/index.js +1 -1
  57. package/dist/gs/embed/index.js.map +1 -1
  58. package/dist/gs/encoding/binary/index.js +201 -8
  59. package/dist/gs/encoding/binary/index.js.map +1 -1
  60. package/dist/gs/encoding/json/index.d.ts +5 -0
  61. package/dist/gs/encoding/json/index.js +388 -25
  62. package/dist/gs/encoding/json/index.js.map +1 -1
  63. package/dist/gs/errors/errors.js +17 -24
  64. package/dist/gs/errors/errors.js.map +1 -1
  65. package/dist/gs/fmt/fmt.js +129 -35
  66. package/dist/gs/fmt/fmt.js.map +1 -1
  67. package/dist/gs/golang.org/x/crypto/cryptobyte/index.js +1 -1
  68. package/dist/gs/golang.org/x/crypto/cryptobyte/index.js.map +1 -1
  69. package/dist/gs/internal/bytealg/index.js +43 -8
  70. package/dist/gs/internal/bytealg/index.js.map +1 -1
  71. package/dist/gs/internal/byteorder/index.d.ts +2 -2
  72. package/dist/gs/internal/byteorder/index.js +2 -2
  73. package/dist/gs/internal/byteorder/index.js.map +1 -1
  74. package/dist/gs/io/fs/format.js +2 -2
  75. package/dist/gs/io/fs/format.js.map +1 -1
  76. package/dist/gs/io/fs/fs.d.ts +1 -1
  77. package/dist/gs/io/fs/fs.js +1 -1
  78. package/dist/gs/io/fs/fs.js.map +1 -1
  79. package/dist/gs/io/io.d.ts +21 -21
  80. package/dist/gs/io/io.js +49 -50
  81. package/dist/gs/io/io.js.map +1 -1
  82. package/dist/gs/math/bits/index.js +26 -8
  83. package/dist/gs/math/bits/index.js.map +1 -1
  84. package/dist/gs/math/copysign.gs.js +10 -17
  85. package/dist/gs/math/copysign.gs.js.map +1 -1
  86. package/dist/gs/math/pow.gs.js +5 -0
  87. package/dist/gs/math/pow.gs.js.map +1 -1
  88. package/dist/gs/math/signbit.gs.js +6 -2
  89. package/dist/gs/math/signbit.gs.js.map +1 -1
  90. package/dist/gs/mime/index.js +1 -0
  91. package/dist/gs/mime/index.js.map +1 -1
  92. package/dist/gs/net/http/index.d.ts +6 -6
  93. package/dist/gs/net/http/index.js +507 -43
  94. package/dist/gs/net/http/index.js.map +1 -1
  95. package/dist/gs/os/stat.gs.d.ts +2 -2
  96. package/dist/gs/os/types.gs.d.ts +1 -1
  97. package/dist/gs/os/types.gs.js +1 -1
  98. package/dist/gs/os/types.gs.js.map +1 -1
  99. package/dist/gs/os/types_js.gs.d.ts +1 -1
  100. package/dist/gs/os/types_js.gs.js +7 -7
  101. package/dist/gs/os/types_js.gs.js.map +1 -1
  102. package/dist/gs/os/types_unix.gs.d.ts +1 -1
  103. package/dist/gs/os/types_unix.gs.js +1 -1
  104. package/dist/gs/os/types_unix.gs.js.map +1 -1
  105. package/dist/gs/os/zero_copy_posix.gs.d.ts +1 -1
  106. package/dist/gs/os/zero_copy_posix.gs.js +1 -1
  107. package/dist/gs/os/zero_copy_posix.gs.js.map +1 -1
  108. package/dist/gs/path/filepath/match.js +8 -4
  109. package/dist/gs/path/filepath/match.js.map +1 -1
  110. package/dist/gs/path/filepath/path.js +216 -42
  111. package/dist/gs/path/filepath/path.js.map +1 -1
  112. package/dist/gs/path/match.js +6 -3
  113. package/dist/gs/path/match.js.map +1 -1
  114. package/dist/gs/reflect/type.d.ts +5 -4
  115. package/dist/gs/reflect/type.js +29 -11
  116. package/dist/gs/reflect/type.js.map +1 -1
  117. package/dist/gs/slices/slices.js +11 -11
  118. package/dist/gs/slices/slices.js.map +1 -1
  119. package/dist/gs/strconv/atof.gs.js +156 -43
  120. package/dist/gs/strconv/atof.gs.js.map +1 -1
  121. package/dist/gs/strconv/atoi.gs.d.ts +3 -2
  122. package/dist/gs/strconv/atoi.gs.js +86 -67
  123. package/dist/gs/strconv/atoi.gs.js.map +1 -1
  124. package/dist/gs/strconv/ftoa.gs.js +73 -3
  125. package/dist/gs/strconv/ftoa.gs.js.map +1 -1
  126. package/dist/gs/strconv/itoa.gs.d.ts +4 -4
  127. package/dist/gs/strconv/itoa.gs.js +5 -4
  128. package/dist/gs/strconv/itoa.gs.js.map +1 -1
  129. package/dist/gs/strconv/quote.gs.d.ts +1 -1
  130. package/dist/gs/strconv/quote.gs.js +311 -103
  131. package/dist/gs/strconv/quote.gs.js.map +1 -1
  132. package/dist/gs/strings/reader.d.ts +1 -1
  133. package/dist/gs/strings/reader.js +8 -8
  134. package/dist/gs/strings/reader.js.map +1 -1
  135. package/dist/gs/strings/strings.js +87 -61
  136. package/dist/gs/strings/strings.js.map +1 -1
  137. package/dist/gs/sync/atomic/doc_64.gs.d.ts +14 -14
  138. package/dist/gs/sync/atomic/doc_64.gs.js +10 -10
  139. package/dist/gs/sync/atomic/doc_64.gs.js.map +1 -1
  140. package/dist/gs/sync/atomic/type.gs.d.ts +22 -22
  141. package/dist/gs/sync/atomic/type.gs.js +4 -4
  142. package/dist/gs/sync/atomic/type.gs.js.map +1 -1
  143. package/dist/gs/sync/sync.js +50 -12
  144. package/dist/gs/sync/sync.js.map +1 -1
  145. package/dist/gs/syscall/fs.d.ts +6 -6
  146. package/dist/gs/syscall/fs.js +1 -1
  147. package/dist/gs/syscall/fs.js.map +1 -1
  148. package/dist/gs/time/time.d.ts +18 -18
  149. package/dist/gs/time/time.js +58 -55
  150. package/dist/gs/time/time.js.map +1 -1
  151. package/dist/gs/unicode/tables.d.ts +11 -0
  152. package/dist/gs/unicode/tables.js +635 -0
  153. package/dist/gs/unicode/tables.js.map +1 -0
  154. package/dist/gs/unicode/unicode.d.ts +58 -38
  155. package/dist/gs/unicode/unicode.js +362 -278
  156. package/dist/gs/unicode/unicode.js.map +1 -1
  157. package/go.sum +13 -0
  158. package/gs/builtin/builtin.ts +83 -93
  159. package/gs/builtin/hostio.ts +1 -1
  160. package/gs/builtin/index.ts +1 -0
  161. package/gs/builtin/panic.test.ts +189 -0
  162. package/gs/builtin/panic.ts +107 -0
  163. package/gs/builtin/runtime-contract.test.ts +5 -5
  164. package/gs/builtin/slice.test.ts +23 -0
  165. package/gs/builtin/slice.ts +133 -95
  166. package/gs/builtin/type.ts +16 -3
  167. package/gs/builtin/varRef.ts +4 -2
  168. package/gs/builtin/wide-int.test.ts +41 -0
  169. package/gs/bytes/bytes.gs.ts +54 -41
  170. package/gs/bytes/bytes.test.ts +18 -1
  171. package/gs/bytes/reader.gs.ts +7 -8
  172. package/gs/cmp/index.test.ts +55 -0
  173. package/gs/cmp/index.ts +45 -9
  174. package/gs/context/context.ts +3 -3
  175. package/gs/embed/index.ts +2 -2
  176. package/gs/encoding/binary/index.test.ts +104 -0
  177. package/gs/encoding/binary/index.ts +259 -11
  178. package/gs/encoding/json/index.test.ts +107 -0
  179. package/gs/encoding/json/index.ts +400 -29
  180. package/gs/errors/errors.test.ts +44 -1
  181. package/gs/errors/errors.ts +15 -31
  182. package/gs/fmt/fmt.test.ts +70 -2
  183. package/gs/fmt/fmt.ts +128 -34
  184. package/gs/golang.org/x/crypto/cryptobyte/index.ts +1 -1
  185. package/gs/internal/bytealg/index.test.ts +26 -1
  186. package/gs/internal/bytealg/index.ts +44 -8
  187. package/gs/internal/byteorder/index.ts +6 -4
  188. package/gs/io/fs/format.ts +2 -2
  189. package/gs/io/fs/fs.ts +2 -2
  190. package/gs/io/fs/stat.test.ts +2 -2
  191. package/gs/io/fs/sub.test.ts +2 -2
  192. package/gs/io/fs/walk.test.ts +2 -2
  193. package/gs/io/io.test.ts +47 -5
  194. package/gs/io/io.ts +73 -73
  195. package/gs/io/limit.test.ts +103 -0
  196. package/gs/math/bits/index.test.ts +128 -0
  197. package/gs/math/bits/index.ts +26 -8
  198. package/gs/math/copysign.gs.test.ts +3 -1
  199. package/gs/math/copysign.gs.ts +10 -22
  200. package/gs/math/pow.gs.test.ts +4 -5
  201. package/gs/math/pow.gs.ts +5 -0
  202. package/gs/math/signbit.gs.test.ts +2 -1
  203. package/gs/math/signbit.gs.ts +6 -3
  204. package/gs/mime/index.ts +1 -0
  205. package/gs/net/http/index.test.ts +683 -2
  206. package/gs/net/http/index.ts +598 -57
  207. package/gs/net/http/meta.json +3 -0
  208. package/gs/os/stat.gs.ts +2 -2
  209. package/gs/os/types.gs.ts +2 -2
  210. package/gs/os/types_js.gs.ts +9 -9
  211. package/gs/os/types_unix.gs.ts +2 -2
  212. package/gs/os/zero_copy_posix.gs.ts +2 -2
  213. package/gs/path/filepath/match.test.ts +16 -0
  214. package/gs/path/filepath/match.ts +8 -4
  215. package/gs/path/filepath/path.test.ts +91 -9
  216. package/gs/path/filepath/path.ts +223 -49
  217. package/gs/path/match.test.ts +32 -0
  218. package/gs/path/match.ts +6 -3
  219. package/gs/reflect/deepequal.test.ts +1 -1
  220. package/gs/reflect/field.test.ts +1 -1
  221. package/gs/reflect/function-types.test.ts +6 -6
  222. package/gs/reflect/sliceat.test.ts +13 -13
  223. package/gs/reflect/structof.test.ts +4 -4
  224. package/gs/reflect/type.ts +34 -14
  225. package/gs/reflect/typefor.test.ts +5 -5
  226. package/gs/runtime/pprof/index.test.ts +20 -0
  227. package/gs/runtime/trace/index.test.ts +3 -0
  228. package/gs/slices/slices.test.ts +31 -0
  229. package/gs/slices/slices.ts +11 -11
  230. package/gs/strconv/append.test.ts +99 -0
  231. package/gs/strconv/atof.gs.ts +156 -42
  232. package/gs/strconv/atof.test.ts +45 -0
  233. package/gs/strconv/atoi.gs.ts +87 -69
  234. package/gs/strconv/atoi.test.ts +49 -0
  235. package/gs/strconv/ftoa.gs.ts +85 -10
  236. package/gs/strconv/ftoa.test.ts +43 -0
  237. package/gs/strconv/itoa.gs.ts +10 -9
  238. package/gs/strconv/quote.gs.ts +335 -108
  239. package/gs/strconv/quote.test.ts +111 -0
  240. package/gs/strings/reader.test.ts +10 -10
  241. package/gs/strings/reader.ts +9 -9
  242. package/gs/strings/strings.test.ts +18 -5
  243. package/gs/strings/strings.ts +81 -68
  244. package/gs/sync/atomic/doc_64.gs.ts +24 -24
  245. package/gs/sync/atomic/doc_64.test.ts +5 -5
  246. package/gs/sync/atomic/type.gs.ts +28 -28
  247. package/gs/sync/sync.test.ts +109 -1
  248. package/gs/sync/sync.ts +46 -12
  249. package/gs/syscall/fs.ts +8 -8
  250. package/gs/syscall/net.test.ts +1 -1
  251. package/gs/time/parse.test.ts +45 -0
  252. package/gs/time/time.test.ts +46 -23
  253. package/gs/time/time.ts +69 -66
  254. package/gs/unicode/gen.go +198 -0
  255. package/gs/unicode/tables.ts +646 -0
  256. package/gs/unicode/unicode.test.ts +69 -0
  257. package/gs/unicode/unicode.ts +396 -312
  258. package/package.json +2 -2
  259. package/dist/gs/github.com/aperturerobotics/util/conc/index.d.ts +0 -20
  260. package/dist/gs/github.com/aperturerobotics/util/conc/index.js +0 -134
  261. package/dist/gs/github.com/aperturerobotics/util/conc/index.js.map +0 -1
  262. package/gs/github.com/aperturerobotics/util/conc/index.test.ts +0 -30
  263. package/gs/github.com/aperturerobotics/util/conc/index.ts +0 -172
  264. package/gs/github.com/aperturerobotics/util/conc/meta.json +0 -9
@@ -48,8 +48,17 @@ type boxedValue = {
48
48
  __goValue?: unknown
49
49
  }
50
50
 
51
+ // binShape recursively describes a fixed-size value for the struct and complex
52
+ // paths that the flat fixedKind union cannot express.
53
+ type binShape =
54
+ | { tag: 'scalar'; kind: fixedKind }
55
+ | { tag: 'complex'; elem: 'float32' | 'float64' }
56
+ | { tag: 'array'; elem: binShape; count: number }
57
+ | { tag: 'struct'; fields: { key: string; shape: binShape }[] }
58
+
51
59
  type decodedTarget = {
52
- kind: fixedKind
60
+ kind: fixedKind | null
61
+ shape?: binShape
53
62
  value: unknown
54
63
  settable: ((value: unknown) => void) | null
55
64
  }
@@ -391,7 +400,7 @@ export async function Read(
391
400
  if (target === null) {
392
401
  return unsupportedError('Read', data)
393
402
  }
394
- const size = fixedSize(target.kind, target.value)
403
+ const size = targetSize(target)
395
404
  if (size < 0) {
396
405
  return unsupportedError('Read', data)
397
406
  }
@@ -414,7 +423,7 @@ export function Decode(
414
423
  if (target === null) {
415
424
  return [0, unsupportedError('Decode', data)]
416
425
  }
417
- const size = fixedSize(target.kind, target.value)
426
+ const size = targetSize(target)
418
427
  if (size < 0) {
419
428
  return [0, unsupportedError('Decode', data)]
420
429
  }
@@ -471,7 +480,14 @@ export function Size(v: unknown): number {
471
480
  if (target === null) {
472
481
  return -1
473
482
  }
474
- return fixedSize(target.kind, target.value)
483
+ return targetSize(target)
484
+ }
485
+
486
+ function targetSize(target: decodedTarget): number {
487
+ if (target.shape !== undefined) {
488
+ return shapeSize(target.shape)
489
+ }
490
+ return fixedSize(target.kind as fixedKind, target.value)
475
491
  }
476
492
 
477
493
  function requireByteOrder(order: ByteOrder | null): ByteOrder {
@@ -488,14 +504,36 @@ function decodeFixed(
488
504
  order: ByteOrder,
489
505
  target: decodedTarget,
490
506
  ): void {
491
- if (target.kind.startsWith('[]')) {
492
- decodeFixedSlice(buf, order, target.kind, target.value)
507
+ if (target.shape !== undefined) {
508
+ decodeShapeTarget(buf, order, target)
509
+ return
510
+ }
511
+ const kind = target.kind as fixedKind
512
+ if (kind.startsWith('[]')) {
513
+ decodeFixedSlice(buf, order, kind, target.value)
493
514
  return
494
515
  }
495
516
  if (target.settable === null) {
496
517
  return
497
518
  }
498
- target.settable(decodeScalar(buf, order, target.kind))
519
+ target.settable(decodeScalar(buf, order, kind))
520
+ }
521
+
522
+ function decodeShapeTarget(
523
+ buf: $.Slice<number>,
524
+ order: ByteOrder,
525
+ target: decodedTarget,
526
+ ): void {
527
+ const shape = target.shape!
528
+ if (shape.tag === 'struct') {
529
+ // Populate the existing struct instance's fields in place.
530
+ shapeDecodeInto(buf, order, shape, target.value)
531
+ return
532
+ }
533
+ if (target.settable === null) {
534
+ return
535
+ }
536
+ target.settable(shapeDecodeValue(buf, order, shape))
499
537
  }
500
538
 
501
539
  function decodeFixedSlice(
@@ -521,12 +559,16 @@ function encodeData(order: ByteOrder, data: unknown): $.Slice<number> | null {
521
559
  if (target === null) {
522
560
  return null
523
561
  }
524
- const size = fixedSize(target.kind, target.value)
562
+ const size = targetSize(target)
525
563
  if (size < 0) {
526
564
  return null
527
565
  }
528
566
  const out = $.makeSlice<number>(size, undefined, 'byte')
529
- encodeFixed(out, order, target.kind, target.value)
567
+ if (target.shape !== undefined) {
568
+ shapeEncode(out, order, target.shape, target.value)
569
+ } else {
570
+ encodeFixed(out, order, target.kind as fixedKind, target.value)
571
+ }
530
572
  return out
531
573
  }
532
574
 
@@ -625,8 +667,8 @@ function decodeTarget(data: unknown): decodedTarget | null {
625
667
  const typeName = goTypeName(data)
626
668
  const value = goValue(data)
627
669
  if (typeName !== '') {
628
- const kind =
629
- fixedKindFromType(typeName) ?? fixedKindFromTypeInfo(goTypeInfo(data))
670
+ const info = goTypeInfo(data)
671
+ const kind = fixedKindFromType(typeName) ?? fixedKindFromTypeInfo(info)
630
672
  if (kind !== null) {
631
673
  return {
632
674
  kind,
@@ -634,6 +676,17 @@ function decodeTarget(data: unknown): decodedTarget | null {
634
676
  settable: setterFor(typeName, value),
635
677
  }
636
678
  }
679
+ // Struct and complex types cannot be named by a flat fixedKind; describe
680
+ // them recursively so Size/Append/Read/Decode handle them like Go.
681
+ const shape = shapeFromType(info ?? typeName)
682
+ if (shape !== null && shape.tag !== 'scalar') {
683
+ return {
684
+ kind: null,
685
+ shape,
686
+ value: pointerValueForKind(typeName, value),
687
+ settable: setterFor(typeName, value),
688
+ }
689
+ }
637
690
  return null
638
691
  }
639
692
  if (value instanceof Uint8Array) {
@@ -802,6 +855,201 @@ function fixedSize(kind: fixedKind, value: unknown): number {
802
855
  return -1
803
856
  }
804
857
 
858
+ // shapeFromType builds a recursive fixed-size description from type info, or
859
+ // null when any element is variable-size or otherwise unsupported (which makes
860
+ // the whole struct unsupported, as in Go's dataSize).
861
+ function shapeFromType(info: $.TypeInfo | string | undefined): binShape | null {
862
+ if (info === undefined) {
863
+ return null
864
+ }
865
+ if (typeof info === 'string') {
866
+ if (info === 'complex64') {
867
+ return { tag: 'complex', elem: 'float32' }
868
+ }
869
+ if (info === 'complex128') {
870
+ return { tag: 'complex', elem: 'float64' }
871
+ }
872
+ const kind = fixedKindFromType(info)
873
+ return kind !== null && !kind.startsWith('[]') ?
874
+ { tag: 'scalar', kind }
875
+ : null
876
+ }
877
+ switch (info.kind) {
878
+ case $.TypeKind.Pointer:
879
+ return shapeFromType(info.elemType)
880
+ case $.TypeKind.Basic: {
881
+ if (info.name === 'complex64') {
882
+ return { tag: 'complex', elem: 'float32' }
883
+ }
884
+ if (info.name === 'complex128') {
885
+ return { tag: 'complex', elem: 'float64' }
886
+ }
887
+ const kind = fixedKindFromBasicName(info.name)
888
+ return kind !== null ? { tag: 'scalar', kind } : null
889
+ }
890
+ case $.TypeKind.Array: {
891
+ const elem = shapeFromType(info.elemType)
892
+ return elem === null ? null : (
893
+ { tag: 'array', elem, count: (info as $.ArrayTypeInfo).length }
894
+ )
895
+ }
896
+ case $.TypeKind.Struct: {
897
+ const fields: { key: string; shape: binShape }[] = []
898
+ for (const field of (info as $.StructTypeInfo).fields) {
899
+ const shape = shapeFromType(field.type)
900
+ if (shape === null) {
901
+ return null
902
+ }
903
+ fields.push({ key: field.key ?? field.name, shape })
904
+ }
905
+ return { tag: 'struct', fields }
906
+ }
907
+ default:
908
+ return null
909
+ }
910
+ }
911
+
912
+ function shapeSize(shape: binShape): number {
913
+ switch (shape.tag) {
914
+ case 'scalar':
915
+ return fixedSize(shape.kind, 0)
916
+ case 'complex':
917
+ return shape.elem === 'float32' ? 8 : 16
918
+ case 'array':
919
+ return shapeSize(shape.elem) * shape.count
920
+ case 'struct': {
921
+ let total = 0
922
+ for (const field of shape.fields) {
923
+ total += shapeSize(field.shape)
924
+ }
925
+ return total
926
+ }
927
+ }
928
+ }
929
+
930
+ function structFieldValue(instance: unknown, key: string): unknown {
931
+ const ref = (instance as { _fields: Record<string, $.VarRef<unknown>> })
932
+ ._fields[key]
933
+ return ref.value
934
+ }
935
+
936
+ function shapeEncode(
937
+ buf: $.Slice<number>,
938
+ order: ByteOrder,
939
+ shape: binShape,
940
+ value: unknown,
941
+ ): void {
942
+ switch (shape.tag) {
943
+ case 'scalar':
944
+ encodeScalar(buf, order, shape.kind, value)
945
+ return
946
+ case 'complex': {
947
+ const c = value as { real: number; imag: number }
948
+ if (shape.elem === 'float32') {
949
+ order.PutUint32($.goSlice(buf, 0, 4), float32Bits(c.real))
950
+ order.PutUint32($.goSlice(buf, 4, 8), float32Bits(c.imag))
951
+ } else {
952
+ order.PutUint64(
953
+ $.goSlice(buf, 0, 8),
954
+ float64Bits(c.real) as unknown as number,
955
+ )
956
+ order.PutUint64(
957
+ $.goSlice(buf, 8, 16),
958
+ float64Bits(c.imag) as unknown as number,
959
+ )
960
+ }
961
+ return
962
+ }
963
+ case 'array': {
964
+ const width = shapeSize(shape.elem)
965
+ for (let i = 0; i < shape.count; i++) {
966
+ shapeEncode(
967
+ $.goSlice(buf, i * width, i * width + width),
968
+ order,
969
+ shape.elem,
970
+ (value as $.Slice<unknown>)![i],
971
+ )
972
+ }
973
+ return
974
+ }
975
+ case 'struct': {
976
+ let offset = 0
977
+ for (const field of shape.fields) {
978
+ const width = shapeSize(field.shape)
979
+ shapeEncode(
980
+ $.goSlice(buf, offset, offset + width),
981
+ order,
982
+ field.shape,
983
+ structFieldValue(value, field.key),
984
+ )
985
+ offset += width
986
+ }
987
+ return
988
+ }
989
+ }
990
+ }
991
+
992
+ function shapeDecodeValue(
993
+ buf: $.Slice<number>,
994
+ order: ByteOrder,
995
+ shape: binShape,
996
+ ): unknown {
997
+ switch (shape.tag) {
998
+ case 'scalar':
999
+ return decodeScalar(buf, order, shape.kind)
1000
+ case 'complex':
1001
+ if (shape.elem === 'float32') {
1002
+ return {
1003
+ real: float32FromBits(order.Uint32($.goSlice(buf, 0, 4))),
1004
+ imag: float32FromBits(order.Uint32($.goSlice(buf, 4, 8))),
1005
+ }
1006
+ }
1007
+ return {
1008
+ real: float64FromBits(order.Uint64($.goSlice(buf, 0, 8))),
1009
+ imag: float64FromBits(order.Uint64($.goSlice(buf, 8, 16))),
1010
+ }
1011
+ case 'array': {
1012
+ const width = shapeSize(shape.elem)
1013
+ const out: unknown[] = []
1014
+ for (let i = 0; i < shape.count; i++) {
1015
+ out.push(
1016
+ shapeDecodeValue(
1017
+ $.goSlice(buf, i * width, i * width + width),
1018
+ order,
1019
+ shape.elem,
1020
+ ),
1021
+ )
1022
+ }
1023
+ return out
1024
+ }
1025
+ case 'struct':
1026
+ // Nested structs are populated in place by shapeDecodeInto.
1027
+ return null
1028
+ }
1029
+ }
1030
+
1031
+ function shapeDecodeInto(
1032
+ buf: $.Slice<number>,
1033
+ order: ByteOrder,
1034
+ shape: { tag: 'struct'; fields: { key: string; shape: binShape }[] },
1035
+ instance: unknown,
1036
+ ): void {
1037
+ const fields = (instance as { _fields: Record<string, $.VarRef<unknown>> })
1038
+ ._fields
1039
+ let offset = 0
1040
+ for (const field of shape.fields) {
1041
+ const width = shapeSize(field.shape)
1042
+ const slice = $.goSlice(buf, offset, offset + width)
1043
+ const ref = fields[field.key]
1044
+ if (field.shape.tag === 'struct') {
1045
+ shapeDecodeInto(slice, order, field.shape, ref.value)
1046
+ } else {
1047
+ ref.value = shapeDecodeValue(slice, order, field.shape)
1048
+ }
1049
+ offset += width
1050
+ }
1051
+ }
1052
+
805
1053
  function unsupportedError(fn: string, data: unknown): $.GoError {
806
1054
  return $.newError(
807
1055
  `encoding/binary: ${fn} of ${unsupportedKind(data)} is not supported in the GoScript browser build`,
@@ -401,6 +401,113 @@ describe('encoding/json override', () => {
401
401
  expect(escaped.String()).toBe('"\\u003ctag\\u003e\\u0026"')
402
402
  })
403
403
 
404
+ it('preserves number and string literal spelling through Compact and Indent', () => {
405
+ const compact = new bytes.Buffer()
406
+ expect(
407
+ Compact(
408
+ compact,
409
+ $.stringToBytes('{"n": 1e+00, "big": 9007199254740993, "s": "abc"}'),
410
+ ),
411
+ ).toBeNull()
412
+ expect(compact.String()).toBe(
413
+ '{"n":1e+00,"big":9007199254740993,"s":"abc"}',
414
+ )
415
+
416
+ const compact2 = new bytes.Buffer()
417
+ expect(Compact(compact2, $.stringToBytes('{"n":1e+00}'))).toBeNull()
418
+ expect(compact2.String()).toBe('{"n":1e+00}')
419
+
420
+ const indented = new bytes.Buffer()
421
+ expect(
422
+ Indent(indented, $.stringToBytes('{"a":1,"b":[2,3],"e":{}}'), '', ' '),
423
+ ).toBeNull()
424
+ expect(indented.String()).toBe(
425
+ '{\n "a": 1,\n "b": [\n 2,\n 3\n ],\n "e": {}\n}',
426
+ )
427
+
428
+ // Go's Indent copies trailing whitespace after the value verbatim.
429
+ const trailing = new bytes.Buffer()
430
+ expect(
431
+ Indent(trailing, $.stringToBytes('{"a":1}\n'), '', ' '),
432
+ ).toBeNull()
433
+ expect(trailing.String()).toBe('{\n "a": 1\n}\n')
434
+ })
435
+
436
+ it('keeps exact source literals for UseNumber beyond float64 precision', () => {
437
+ const reader = bytes.NewBufferString('{"big":9007199254740993,"f":1e+00}')!
438
+ const decoder = NewDecoder(reader)
439
+ decoder.UseNumber()
440
+ const target = $.varRef<Map<string, unknown> | null>(null)
441
+
442
+ expect(decoder.Decode(target)).toBeNull()
443
+ expect(target.value?.get('big')).toBe('9007199254740993')
444
+ expect(target.value?.get('f')).toBe('1e+00')
445
+ })
446
+
447
+ it('returns a SyntaxError with the Go byte offset for malformed input', () => {
448
+ const cases: Array<[string, number]> = [
449
+ ['[1,]', 4],
450
+ ['{"a":}', 6],
451
+ ['[1 2]', 4],
452
+ ['truex', 5],
453
+ ['123abc', 4],
454
+ ]
455
+ for (const [input, offset] of cases) {
456
+ const target = $.varRef<unknown>(null)
457
+ const err = Unmarshal($.stringToBytes(input), target)
458
+ expect(err).toBeInstanceOf(JSONSyntaxError)
459
+ expect((err as InstanceType<typeof JSONSyntaxError>).Offset).toBe(offset)
460
+ }
461
+ })
462
+
463
+ it('decodes one value per Decode and buffers the rest of the stream', () => {
464
+ const decoder = NewDecoder(bytes.NewBufferString('1 2')!)
465
+ const first = $.varRef<unknown>(null)
466
+ const second = $.varRef<unknown>(null)
467
+
468
+ expect(decoder.Decode(first)).toBeNull()
469
+ expect(first.value).toBe(1)
470
+ expect(decoder.Decode(second)).toBeNull()
471
+ expect(second.value).toBe(2)
472
+ expect(decoder.Decode($.varRef<unknown>(null))?.Error()).toBe('EOF')
473
+ })
474
+
475
+ it('streams delimiters and values through Token and reports More', () => {
476
+ const tokens = NewDecoder(bytes.NewBufferString('[1]')!)
477
+ expect(tokens.Token()).toEqual(['['.charCodeAt(0), null])
478
+ expect(tokens.Token()).toEqual([1, null])
479
+ expect(tokens.Token()).toEqual([']'.charCodeAt(0), null])
480
+ expect(tokens.Token()[1]?.Error()).toBe('EOF')
481
+
482
+ const more = NewDecoder(bytes.NewBufferString('[1,2]')!)
483
+ more.Token() // consume [
484
+ expect(more.More()).toBe(true)
485
+ more.Token() // 1
486
+ expect(more.More()).toBe(true)
487
+ more.Token() // 2
488
+ expect(more.More()).toBe(false)
489
+ })
490
+
491
+ it('marshals a RawMessage field without normalizing its token spelling', () => {
492
+ class RawHolder {
493
+ public _fields = {
494
+ R: $.varRef($.stringToBytes('1e+00')),
495
+ }
496
+
497
+ static __typeInfo = $.registerStructType(
498
+ 'test.RawHolder',
499
+ new RawHolder(),
500
+ [],
501
+ RawHolder,
502
+ [{ name: 'R', key: 'R', type: 'json.RawMessage', tag: 'json:"r"' }],
503
+ )
504
+ }
505
+
506
+ const [data, err] = Marshal(new RawHolder())
507
+ expect(err).toBeNull()
508
+ expect($.bytesToString(data)).toBe('{"r":1e+00}')
509
+ })
510
+
404
511
  it('decodes from readers and exposes raw message and number helpers', () => {
405
512
  const reader = bytes.NewBufferString('{"name":"Dana","age":28}')!
406
513
  const decoder = NewDecoder(reader)