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
@@ -0,0 +1,189 @@
1
+ import { describe, expect, it } from 'vitest'
2
+
3
+ import { int64Div, pointerValue } from './builtin.js'
4
+ import {
5
+ GoPanic,
6
+ RuntimeError,
7
+ panic,
8
+ panicValue,
9
+ recover,
10
+ recovered,
11
+ runtimePanic,
12
+ } from './panic.js'
13
+ import { goSlice, index, makeSlice } from './slice.js'
14
+
15
+ // captureThrow returns whatever value the callback throws, or undefined if it
16
+ // did not throw. Go panics surface as a thrown GoPanic in the transpiled
17
+ // runtime, so tests assert on the caught value's shape. Catching here models a
18
+ // program-exit boundary that fully consumes the panic, so it drains the in-flight
19
+ // stack to keep cases independent.
20
+ function captureThrow(fn: () => unknown): unknown {
21
+ try {
22
+ fn()
23
+ } catch (e) {
24
+ if (e instanceof GoPanic) {
25
+ e.recovered = true
26
+ recovered(e)
27
+ }
28
+ return e
29
+ }
30
+ return undefined
31
+ }
32
+
33
+ describe('runtime panics route through GoPanic', () => {
34
+ it('runtimePanic throws a GoPanic carrying a RuntimeError', () => {
35
+ const caught = captureThrow(() => runtimePanic('runtime error: boom'))
36
+
37
+ expect(caught).toBeInstanceOf(GoPanic)
38
+ const value = (caught as GoPanic).value
39
+ expect(value).toBeInstanceOf(RuntimeError)
40
+ // The recovered value implements the Go error interface.
41
+ expect((value as RuntimeError).Error()).toBe('runtime error: boom')
42
+ // The unrecovered crash text matches Go's "panic: <value>" shape.
43
+ expect((caught as GoPanic).message).toBe('panic: runtime error: boom')
44
+ })
45
+
46
+ it('integer divide by zero panics with the Go message', () => {
47
+ const caught = captureThrow(() => int64Div(1n, 0n))
48
+
49
+ expect(caught).toBeInstanceOf(GoPanic)
50
+ expect(((caught as GoPanic).value as RuntimeError).Error()).toBe(
51
+ 'runtime error: integer divide by zero',
52
+ )
53
+ })
54
+
55
+ it('nil pointer dereference panics with the Go message', () => {
56
+ const caught = captureThrow(() => pointerValue(null))
57
+
58
+ expect(caught).toBeInstanceOf(GoPanic)
59
+ expect(((caught as GoPanic).value as RuntimeError).Error()).toBe(
60
+ 'runtime error: invalid memory address or nil pointer dereference',
61
+ )
62
+ })
63
+
64
+ it('index out of range panics with the Go message', () => {
65
+ const caught = captureThrow(() => index(new Uint8Array([1, 2, 3]), 5))
66
+
67
+ expect(caught).toBeInstanceOf(GoPanic)
68
+ expect(((caught as GoPanic).value as RuntimeError).Error()).toBe(
69
+ 'runtime error: index out of range [5] with length 3',
70
+ )
71
+ })
72
+
73
+ it('slice bounds out of range panics with the Go message', () => {
74
+ const caught = captureThrow(() => goSlice([1, 2, 3], 2, 1))
75
+
76
+ expect(caught).toBeInstanceOf(GoPanic)
77
+ expect(((caught as GoPanic).value as RuntimeError).Error()).toBe(
78
+ 'runtime error: slice bounds out of range [2:1]',
79
+ )
80
+ })
81
+
82
+ it('makeslice with negative capacity panics with the Go message', () => {
83
+ const caught = captureThrow(() => makeSlice<number>(1, -1))
84
+
85
+ expect(caught).toBeInstanceOf(GoPanic)
86
+ expect(((caught as GoPanic).value as RuntimeError).Error()).toBe(
87
+ 'runtime error: makeslice: cap out of range',
88
+ )
89
+ })
90
+
91
+ it('panic carries an arbitrary value that panicValue unwraps', () => {
92
+ const caught = captureThrow(() => panic('boom'))
93
+
94
+ expect(caught).toBeInstanceOf(GoPanic)
95
+ expect(panicValue(caught)).toBe('boom')
96
+ // panicValue passes non-panic values through unchanged.
97
+ expect(panicValue('plain')).toBe('plain')
98
+ })
99
+ })
100
+
101
+ describe('recover consumes the in-flight panic', () => {
102
+ // runWithRecover mirrors the code generated for a Go func that defers a
103
+ // function calling recover(): the deferreds run during stack unwinding (here,
104
+ // directly in the catch before recovered() is consulted), then recovered()
105
+ // decides whether the panic is swallowed or rethrown.
106
+ function runWithRecover(
107
+ body: () => void,
108
+ deferred: () => void,
109
+ ): { rethrew: unknown; finished: boolean } {
110
+ try {
111
+ body()
112
+ } catch (e) {
113
+ deferred()
114
+ if (!recovered(e)) {
115
+ // This frame is the outermost boundary in the test. In a real program an
116
+ // unrecovered panic propagates to the entrypoint and crashes the
117
+ // process, leaving nothing in flight; drain it here to model that exit
118
+ // so later cases start with an empty stack.
119
+ if (e instanceof GoPanic) {
120
+ e.recovered = true
121
+ recovered(e)
122
+ }
123
+ return { rethrew: e, finished: false }
124
+ }
125
+ }
126
+ return { rethrew: undefined, finished: true }
127
+ }
128
+
129
+ it('recover returns the panic value and swallows the panic', () => {
130
+ let seen: unknown = 'unset'
131
+ const outcome = runWithRecover(
132
+ () => panic('boom'),
133
+ () => {
134
+ seen = recover()
135
+ },
136
+ )
137
+
138
+ expect(seen).toBe('boom')
139
+ expect(outcome.finished).toBe(true)
140
+ expect(outcome.rethrew).toBeUndefined()
141
+ })
142
+
143
+ it('a deferred that does not recover lets the panic propagate', () => {
144
+ const outcome = runWithRecover(
145
+ () => panic('boom'),
146
+ () => {
147
+ // no recover() call
148
+ },
149
+ )
150
+
151
+ expect(outcome.finished).toBe(false)
152
+ expect(outcome.rethrew).toBeInstanceOf(GoPanic)
153
+ expect((outcome.rethrew as GoPanic).value).toBe('boom')
154
+ })
155
+
156
+ it('recover returns nil when no panic is in flight', () => {
157
+ expect(recover()).toBeNull()
158
+ })
159
+
160
+ it('recover returns nil a second time within the same panic', () => {
161
+ let first: unknown = 'unset'
162
+ let second: unknown = 'unset'
163
+ runWithRecover(
164
+ () => panic('once'),
165
+ () => {
166
+ first = recover()
167
+ second = recover()
168
+ },
169
+ )
170
+
171
+ expect(first).toBe('once')
172
+ expect(second).toBeNull()
173
+ })
174
+
175
+ it('recovers a runtime fault as its RuntimeError value', () => {
176
+ let seen: unknown = 'unset'
177
+ runWithRecover(
178
+ () => int64Div(1n, 0n),
179
+ () => {
180
+ seen = recover()
181
+ },
182
+ )
183
+
184
+ expect(seen).toBeInstanceOf(RuntimeError)
185
+ expect((seen as RuntimeError).Error()).toBe(
186
+ 'runtime error: integer divide by zero',
187
+ )
188
+ })
189
+ })
@@ -0,0 +1,107 @@
1
+ // GoPanic carries a Go panic value as it unwinds the JavaScript call stack. A
2
+ // deferred recover() reads its value and sets recovered; an unrecovered GoPanic
3
+ // surfaces as a thrown error whose message is "panic: <value>", matching Go's
4
+ // crash output.
5
+ export class GoPanic extends Error {
6
+ recovered = false
7
+
8
+ constructor(public readonly value: unknown) {
9
+ super(`panic: ${formatPanicValue(value)}`)
10
+ }
11
+ }
12
+
13
+ // panicStack holds the GoPanics currently unwinding the stack, innermost last.
14
+ // panic() and runtimePanic() push before throwing; a deferred recover() consults
15
+ // the top entry, and the recover-aware catch generated for a defer+recover
16
+ // function removes it once handled. Empty when no panic is in flight, so a
17
+ // recover() outside a panic returns nil.
18
+ const panicStack: GoPanic[] = []
19
+
20
+ // RuntimeError is the value carried by a panic from a Go runtime fault: index
21
+ // out of range, slice bounds out of range, integer divide by zero, or nil
22
+ // pointer dereference. It implements the Go error interface (Error()), so a
23
+ // recovered runtime panic exposes the same shape as Go's runtime.Error.
24
+ export class RuntimeError extends Error {
25
+ constructor(message: string) {
26
+ super(message)
27
+ this.name = 'RuntimeError'
28
+ }
29
+
30
+ Error(): string {
31
+ return this.message
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Implementation of Go's built-in panic function.
37
+ * @param args Arguments passed to panic
38
+ */
39
+ export function panic(...args: unknown[]): never {
40
+ const value = args.length === 1 ? args[0] : args
41
+ const goPanic = new GoPanic(value)
42
+ panicStack.push(goPanic)
43
+ throw goPanic
44
+ }
45
+
46
+ // runtimePanic raises a Go runtime panic carrying a RuntimeError with the given
47
+ // Go-formatted message (for example "runtime error: index out of range [5] with
48
+ // length 3"). Every runtime fault routes through here so recover() observes a
49
+ // GoPanic whose value is a runtime.Error, matching Go.
50
+ export function runtimePanic(message: string): never {
51
+ const goPanic = new GoPanic(new RuntimeError(message))
52
+ panicStack.push(goPanic)
53
+ throw goPanic
54
+ }
55
+
56
+ export function panicValue(value: unknown): unknown {
57
+ if (value instanceof GoPanic) {
58
+ return value.value
59
+ }
60
+ return value
61
+ }
62
+
63
+ function formatPanicValue(value: unknown): string {
64
+ if (value instanceof Error) {
65
+ return value.message
66
+ }
67
+ if (
68
+ value !== null &&
69
+ typeof value === 'object' &&
70
+ 'Error' in value &&
71
+ typeof (value as { Error?: unknown }).Error === 'function'
72
+ ) {
73
+ return String((value as { Error(): string }).Error())
74
+ }
75
+ return String(value)
76
+ }
77
+
78
+ // recover stops the in-flight panic and returns its value, matching Go's
79
+ // built-in recover. It returns nil when no panic is unwinding (an empty stack)
80
+ // or when the current panic was already recovered. Generated code only routes a
81
+ // recover() call to a useful panic when it runs inside a deferred function whose
82
+ // enclosing function is unwinding, so a recover() in ordinary control flow reads
83
+ // an empty stack and returns nil.
84
+ export function recover(): any {
85
+ const goPanic = panicStack[panicStack.length - 1]
86
+ if (goPanic === undefined || goPanic.recovered) {
87
+ return null
88
+ }
89
+ goPanic.recovered = true
90
+ return goPanic.value
91
+ }
92
+
93
+ // recovered reports whether err is a GoPanic that a deferred recover() consumed,
94
+ // and removes it from the in-flight stack. The catch block generated for a
95
+ // defer+recover function calls this: true means swallow the panic and return the
96
+ // function's named results; false means rethrow so an outer frame can recover or
97
+ // the program crashes with the Go panic message.
98
+ export function recovered(err: unknown): boolean {
99
+ if (err instanceof GoPanic && err.recovered) {
100
+ const idx = panicStack.indexOf(err)
101
+ if (idx !== -1) {
102
+ panicStack.splice(idx, 1)
103
+ }
104
+ return true
105
+ }
106
+ return false
107
+ }
@@ -139,8 +139,8 @@ describe('builtin runtime contract helpers', () => {
139
139
  expect(uint(uint64Shl(1n, 63), 32)).toBe(0)
140
140
  expect(uint(uint64Shr(uint64Shl(1n, 63), 60), 32)).toBe(8)
141
141
  expect(uint(uint64Mul(0xffffffffffffffffn, 3), 32)).toBe(0xfffffffd)
142
- expect(uint64Div(0xffffffffffffffffn, 4114)).toBe(4483895010624587)
143
- expect(uint64Mod(0xffffffffffffffffn, 4114)).toBe(697)
142
+ expect(uint64Div(0xffffffffffffffffn, 4114)).toBe(4483895010624587n)
143
+ expect(uint64Mod(0xffffffffffffffffn, 4114)).toBe(697n)
144
144
  expect(uint(uint64Add(0xffffffffffffffffn, 2), 32)).toBe(1)
145
145
  expect(uint(uint64Sub(1n, 2), 32)).toBe(0xffffffff)
146
146
  expect(uint(uint64And(0xf0n, 0x3cn), 32)).toBe(0x30)
@@ -371,8 +371,8 @@ describe('builtin runtime contract helpers', () => {
371
371
 
372
372
  it('asserts fixed-width numeric values by runtime type name', () => {
373
373
  expect(
374
- typeAssertTuple<number>(13, { kind: TypeKind.Basic, name: 'uint64' }),
375
- ).toEqual([13, true])
374
+ typeAssertTuple<bigint>(13n, { kind: TypeKind.Basic, name: 'uint64' }),
375
+ ).toEqual([13n, true])
376
376
  expect(
377
377
  typeAssertTuple<number>(13, { kind: TypeKind.Basic, name: 'int32' }),
378
378
  ).toEqual([13, true])
@@ -530,7 +530,7 @@ describe('builtin runtime contract helpers', () => {
530
530
  expect(array).toEqual([9, 3])
531
531
  expect(source![0]).toBe(2)
532
532
  expect(() => sliceToArray<number>(source, 3)).toThrow(
533
- 'cannot convert slice with length 2 to array with length 3',
533
+ 'cannot convert slice with length 2 to array or pointer to array with length 3',
534
534
  )
535
535
 
536
536
  expect(sliceToArray<number>(new Uint8Array([4, 5, 6]), 2, 'byte')).toEqual(
@@ -7,12 +7,35 @@ import {
7
7
  copy,
8
8
  indexString,
9
9
  len,
10
+ runeToString,
11
+ runesToString,
10
12
  sliceString,
11
13
  stringCompare,
12
14
  stringEqual,
13
15
  stringToBytes,
14
16
  } from './slice.js'
15
17
 
18
+ describe('rune to string encoding (Go string(rune) semantics)', () => {
19
+ it('preserves astral-plane runes above U+FFFF', () => {
20
+ // U+1F600 grinning face; fromCharCode would have truncated to a broken unit.
21
+ expect(runeToString(0x1f600)).toBe('😀')
22
+ expect(runeToString(0x1f600).codePointAt(0)).toBe(0x1f600)
23
+ // CJU+597D '好'
24
+ expect(runeToString(0x597d)).toBe('好')
25
+ })
26
+
27
+ it('maps invalid and surrogate runes to U+FFFD, never throwing', () => {
28
+ expect(runeToString(-1)).toBe('�')
29
+ expect(runeToString(0x110000)).toBe('�')
30
+ expect(runeToString(0xd800)).toBe('�')
31
+ })
32
+
33
+ it('encodes a rune slice with astral planes intact', () => {
34
+ expect(runesToString([0x48, 0x69, 0x1f600] as never)).toBe('Hi😀')
35
+ expect(runesToString([] as never)).toBe('')
36
+ })
37
+ })
38
+
16
39
  describe('builtin string byte representation', () => {
17
40
  it('appends large byte slices without JavaScript argument spreading', () => {
18
41
  const dst = new Uint8Array(0)