goscript 0.1.4 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (270) hide show
  1. package/README.md +5 -2
  2. package/cmd/go_js_wasm_exec/main.go +201 -0
  3. package/cmd/go_js_wasm_exec/main_test.go +83 -0
  4. package/cmd/goscript/{cmd_compile.go → cmd-compile.go} +7 -0
  5. package/cmd/goscript/cmd-test.go +14 -0
  6. package/cmd/goscript/cmd-test_test.go +1 -1
  7. package/compiler/compile-request.go +12 -9
  8. package/compiler/compliance_test.go +0 -1
  9. package/compiler/config.go +2 -0
  10. package/compiler/gotest/request.go +28 -0
  11. package/compiler/gotest/runner.go +353 -27
  12. package/compiler/gotest/runner_test.go +273 -1
  13. package/compiler/gotest/testdata/browserapi/browserapi_test.go +20 -0
  14. package/compiler/gotest/testdata/browserapi/go.mod +3 -0
  15. package/compiler/lowered-program.go +24 -17
  16. package/compiler/lowering.go +392 -127
  17. package/compiler/lowering_bench_test.go +41 -27
  18. package/compiler/override-facts.go +15 -0
  19. package/compiler/override-parity-verifier.go +450 -0
  20. package/compiler/override-parity.go +122 -0
  21. package/compiler/override-registry_test.go +559 -0
  22. package/compiler/protobuf-ts-binding.go +514 -0
  23. package/compiler/protobuf-ts-binding_test.go +172 -0
  24. package/compiler/semantic-model-types.go +9 -4
  25. package/compiler/semantic-model.go +282 -70
  26. package/compiler/semantic-model_test.go +82 -1
  27. package/compiler/service.go +20 -1
  28. package/compiler/skeleton_test.go +62 -8
  29. package/compiler/typescript-emitter.go +128 -13
  30. package/dist/gs/builtin/slice.d.ts +2 -1
  31. package/dist/gs/builtin/slice.js +29 -4
  32. package/dist/gs/builtin/slice.js.map +1 -1
  33. package/dist/gs/builtin/type.d.ts +13 -5
  34. package/dist/gs/builtin/type.js +153 -60
  35. package/dist/gs/builtin/type.js.map +1 -1
  36. package/dist/gs/builtin/varRef.d.ts +11 -0
  37. package/dist/gs/builtin/varRef.js +57 -2
  38. package/dist/gs/builtin/varRef.js.map +1 -1
  39. package/dist/gs/bytes/buffer.gs.js +1 -1
  40. package/dist/gs/bytes/buffer.gs.js.map +1 -1
  41. package/dist/gs/bytes/reader.gs.js +1 -1
  42. package/dist/gs/bytes/reader.gs.js.map +1 -1
  43. package/dist/gs/compress/zlib/index.d.ts +10 -3
  44. package/dist/gs/compress/zlib/index.js +50 -16
  45. package/dist/gs/compress/zlib/index.js.map +1 -1
  46. package/dist/gs/encoding/json/index.d.ts +114 -0
  47. package/dist/gs/encoding/json/index.js +544 -36
  48. package/dist/gs/encoding/json/index.js.map +1 -1
  49. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +100 -0
  50. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +564 -0
  51. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -1
  52. package/dist/gs/github.com/pkg/errors/errors.js +54 -30
  53. package/dist/gs/github.com/pkg/errors/errors.js.map +1 -1
  54. package/dist/gs/go/scanner/index.d.ts +2 -0
  55. package/dist/gs/go/scanner/index.js +29 -5
  56. package/dist/gs/go/scanner/index.js.map +1 -1
  57. package/dist/gs/go/token/index.js +22 -6
  58. package/dist/gs/go/token/index.js.map +1 -1
  59. package/dist/gs/hash/index.d.ts +6 -0
  60. package/dist/gs/hash/index.js +20 -0
  61. package/dist/gs/hash/index.js.map +1 -1
  62. package/dist/gs/internal/goarch/index.d.ts +43 -3
  63. package/dist/gs/internal/goarch/index.js +42 -10
  64. package/dist/gs/internal/goarch/index.js.map +1 -1
  65. package/dist/gs/io/fs/fs.js +26 -14
  66. package/dist/gs/io/fs/fs.js.map +1 -1
  67. package/dist/gs/io/fs/readdir.js +4 -2
  68. package/dist/gs/io/fs/readdir.js.map +1 -1
  69. package/dist/gs/io/fs/sub.js +8 -1
  70. package/dist/gs/io/fs/sub.js.map +1 -1
  71. package/dist/gs/io/io.d.ts +2 -0
  72. package/dist/gs/io/io.js.map +1 -1
  73. package/dist/gs/math/bits/index.d.ts +5 -0
  74. package/dist/gs/math/bits/index.js +16 -4
  75. package/dist/gs/math/bits/index.js.map +1 -1
  76. package/dist/gs/mime/index.d.ts +16 -0
  77. package/dist/gs/mime/index.js +315 -6
  78. package/dist/gs/mime/index.js.map +1 -1
  79. package/dist/gs/net/http/httptest/index.d.ts +12 -0
  80. package/dist/gs/net/http/httptest/index.js +85 -6
  81. package/dist/gs/net/http/httptest/index.js.map +1 -1
  82. package/dist/gs/net/http/index.d.ts +300 -5
  83. package/dist/gs/net/http/index.js +1598 -58
  84. package/dist/gs/net/http/index.js.map +1 -1
  85. package/dist/gs/os/dir_unix.gs.js +1 -1
  86. package/dist/gs/os/dir_unix.gs.js.map +1 -1
  87. package/dist/gs/os/error.gs.js +1 -1
  88. package/dist/gs/os/error.gs.js.map +1 -1
  89. package/dist/gs/os/exec.gs.d.ts +1 -0
  90. package/dist/gs/os/exec.gs.js +4 -8
  91. package/dist/gs/os/exec.gs.js.map +1 -1
  92. package/dist/gs/os/exec_posix.gs.js +1 -1
  93. package/dist/gs/os/exec_posix.gs.js.map +1 -1
  94. package/dist/gs/os/index.d.ts +1 -1
  95. package/dist/gs/os/index.js +1 -1
  96. package/dist/gs/os/index.js.map +1 -1
  97. package/dist/gs/os/proc.gs.d.ts +4 -0
  98. package/dist/gs/os/proc.gs.js +12 -6
  99. package/dist/gs/os/proc.gs.js.map +1 -1
  100. package/dist/gs/os/root_js.gs.js +1 -1
  101. package/dist/gs/os/root_js.gs.js.map +1 -1
  102. package/dist/gs/os/types.gs.js +1 -1
  103. package/dist/gs/os/types.gs.js.map +1 -1
  104. package/dist/gs/os/types_js.gs.js +1 -1
  105. package/dist/gs/os/types_js.gs.js.map +1 -1
  106. package/dist/gs/os/types_unix.gs.js +1 -1
  107. package/dist/gs/os/types_unix.gs.js.map +1 -1
  108. package/dist/gs/path/path.js +11 -7
  109. package/dist/gs/path/path.js.map +1 -1
  110. package/dist/gs/reflect/index.d.ts +5 -4
  111. package/dist/gs/reflect/index.js +4 -3
  112. package/dist/gs/reflect/index.js.map +1 -1
  113. package/dist/gs/reflect/map.js +15 -0
  114. package/dist/gs/reflect/map.js.map +1 -1
  115. package/dist/gs/reflect/type.d.ts +25 -6
  116. package/dist/gs/reflect/type.js +1418 -228
  117. package/dist/gs/reflect/type.js.map +1 -1
  118. package/dist/gs/reflect/types.d.ts +14 -6
  119. package/dist/gs/reflect/types.js +35 -1
  120. package/dist/gs/reflect/types.js.map +1 -1
  121. package/dist/gs/reflect/value.d.ts +1 -0
  122. package/dist/gs/reflect/value.js +83 -41
  123. package/dist/gs/reflect/value.js.map +1 -1
  124. package/dist/gs/reflect/visiblefields.js +4 -140
  125. package/dist/gs/reflect/visiblefields.js.map +1 -1
  126. package/dist/gs/runtime/pprof/index.d.ts +8 -2
  127. package/dist/gs/runtime/pprof/index.js +50 -30
  128. package/dist/gs/runtime/pprof/index.js.map +1 -1
  129. package/dist/gs/runtime/runtime.js +5 -4
  130. package/dist/gs/runtime/runtime.js.map +1 -1
  131. package/dist/gs/runtime/trace/index.js +5 -19
  132. package/dist/gs/runtime/trace/index.js.map +1 -1
  133. package/dist/gs/strconv/atoi.gs.js +1 -1
  134. package/dist/gs/strconv/atoi.gs.js.map +1 -1
  135. package/dist/gs/strconv/complex.gs.d.ts +3 -0
  136. package/dist/gs/strconv/complex.gs.js +148 -0
  137. package/dist/gs/strconv/complex.gs.js.map +1 -0
  138. package/dist/gs/strconv/index.d.ts +1 -0
  139. package/dist/gs/strconv/index.js +1 -0
  140. package/dist/gs/strconv/index.js.map +1 -1
  141. package/dist/gs/strings/builder.js +1 -1
  142. package/dist/gs/strings/reader.js +9 -5
  143. package/dist/gs/strings/reader.js.map +1 -1
  144. package/dist/gs/strings/replace.js +15 -7
  145. package/dist/gs/strings/replace.js.map +1 -1
  146. package/dist/gs/strings/strings.d.ts +5 -0
  147. package/dist/gs/strings/strings.js +57 -5
  148. package/dist/gs/strings/strings.js.map +1 -1
  149. package/dist/gs/sync/atomic/type.gs.js +9 -9
  150. package/dist/gs/sync/atomic/type.gs.js.map +1 -1
  151. package/dist/gs/sync/atomic/value.gs.js +2 -2
  152. package/dist/gs/sync/atomic/value.gs.js.map +1 -1
  153. package/dist/gs/syscall/env.js +22 -14
  154. package/dist/gs/syscall/env.js.map +1 -1
  155. package/dist/gs/testing/testing.js +55 -13
  156. package/dist/gs/testing/testing.js.map +1 -1
  157. package/dist/gs/time/time.d.ts +24 -1
  158. package/dist/gs/time/time.js +43 -3
  159. package/dist/gs/time/time.js.map +1 -1
  160. package/dist/gs/unique/index.js +7 -1
  161. package/dist/gs/unique/index.js.map +1 -1
  162. package/go.mod +3 -3
  163. package/go.sum +16 -0
  164. package/gs/builtin/runtime-contract.test.ts +218 -21
  165. package/gs/builtin/slice.ts +44 -4
  166. package/gs/builtin/type.ts +226 -59
  167. package/gs/builtin/varRef.ts +85 -2
  168. package/gs/bytes/buffer.gs.ts +1 -1
  169. package/gs/bytes/reader.gs.ts +1 -1
  170. package/gs/compress/zlib/index.test.ts +62 -1
  171. package/gs/compress/zlib/index.ts +53 -16
  172. package/gs/compress/zlib/parity.json +51 -0
  173. package/gs/encoding/json/index.test.ts +360 -6
  174. package/gs/encoding/json/index.ts +679 -38
  175. package/gs/encoding/json/parity.json +81 -0
  176. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.test.ts +211 -3
  177. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +857 -1
  178. package/gs/github.com/pkg/errors/errors.ts +54 -30
  179. package/gs/go/scanner/index.test.ts +39 -56
  180. package/gs/go/scanner/index.ts +33 -5
  181. package/gs/go/scanner/parity.json +27 -0
  182. package/gs/go/token/index.ts +22 -6
  183. package/gs/hash/index.test.ts +20 -33
  184. package/gs/hash/index.ts +28 -0
  185. package/gs/hash/parity.json +21 -0
  186. package/gs/internal/goarch/index.test.ts +32 -0
  187. package/gs/internal/goarch/index.ts +45 -13
  188. package/gs/internal/goarch/parity.json +144 -0
  189. package/gs/io/fs/fs.ts +26 -14
  190. package/gs/io/fs/readdir.ts +4 -4
  191. package/gs/io/fs/sub.ts +8 -1
  192. package/gs/io/io.ts +1 -0
  193. package/gs/io/parity.json +162 -0
  194. package/gs/math/bits/index.test.ts +14 -1
  195. package/gs/math/bits/index.ts +23 -4
  196. package/gs/math/bits/parity.json +156 -0
  197. package/gs/mime/index.test.ts +90 -0
  198. package/gs/mime/index.ts +369 -6
  199. package/gs/mime/parity.json +36 -0
  200. package/gs/net/http/httptest/index.test.ts +98 -2
  201. package/gs/net/http/httptest/index.ts +101 -6
  202. package/gs/net/http/httptest/parity.json +15 -0
  203. package/gs/net/http/index.test.ts +781 -12
  204. package/gs/net/http/index.ts +1860 -139
  205. package/gs/net/http/meta.json +16 -1
  206. package/gs/net/http/parity.json +193 -0
  207. package/gs/os/dir_unix.gs.ts +1 -1
  208. package/gs/os/error.gs.ts +1 -1
  209. package/gs/os/exec.gs.ts +4 -8
  210. package/gs/os/exec_posix.gs.ts +1 -1
  211. package/gs/os/index.test.ts +9 -0
  212. package/gs/os/index.ts +1 -0
  213. package/gs/os/parity.json +9 -0
  214. package/gs/os/proc.gs.ts +18 -5
  215. package/gs/os/proc.test.ts +26 -0
  216. package/gs/os/root_js.gs.ts +1 -1
  217. package/gs/os/types.gs.ts +1 -1
  218. package/gs/os/types_js.gs.ts +1 -1
  219. package/gs/os/types_unix.gs.ts +1 -1
  220. package/gs/path/path.ts +11 -7
  221. package/gs/reflect/field.test.ts +37 -15
  222. package/gs/reflect/function-types.test.ts +518 -22
  223. package/gs/reflect/index.ts +8 -6
  224. package/gs/reflect/map.ts +20 -0
  225. package/gs/reflect/meta.json +6 -4
  226. package/gs/reflect/parity.json +234 -0
  227. package/gs/reflect/sliceat.test.ts +156 -0
  228. package/gs/reflect/structof.test.ts +401 -0
  229. package/gs/reflect/type.ts +1897 -317
  230. package/gs/reflect/typefor.test.ts +510 -10
  231. package/gs/reflect/types.ts +43 -18
  232. package/gs/reflect/value.ts +105 -45
  233. package/gs/reflect/visiblefields.ts +5 -168
  234. package/gs/runtime/parity.json +24 -0
  235. package/gs/runtime/pprof/index.test.ts +29 -7
  236. package/gs/runtime/pprof/index.ts +56 -30
  237. package/gs/runtime/pprof/parity.json +27 -0
  238. package/gs/runtime/runtime.test.ts +3 -1
  239. package/gs/runtime/runtime.ts +4 -3
  240. package/gs/runtime/trace/index.test.ts +5 -3
  241. package/gs/runtime/trace/index.ts +8 -20
  242. package/gs/runtime/trace/parity.json +36 -0
  243. package/gs/strconv/atoi.gs.ts +1 -1
  244. package/gs/strconv/complex.gs.ts +174 -0
  245. package/gs/strconv/complex.test.ts +65 -0
  246. package/gs/strconv/index.ts +1 -0
  247. package/gs/strconv/parity.json +120 -0
  248. package/gs/strings/builder.ts +1 -1
  249. package/gs/strings/parity.json +186 -0
  250. package/gs/strings/reader.ts +9 -5
  251. package/gs/strings/replace.ts +15 -7
  252. package/gs/strings/strings.test.ts +22 -2
  253. package/gs/strings/strings.ts +64 -6
  254. package/gs/sync/atomic/type.gs.ts +9 -9
  255. package/gs/sync/atomic/value.gs.ts +2 -2
  256. package/gs/syscall/env.ts +29 -14
  257. package/gs/testing/testing.test.ts +67 -0
  258. package/gs/testing/testing.ts +87 -19
  259. package/gs/time/parity.json +225 -0
  260. package/gs/time/time.test.ts +20 -2
  261. package/gs/time/time.ts +49 -7
  262. package/gs/unique/index.ts +7 -1
  263. package/package.json +4 -2
  264. package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.d.ts +0 -217
  265. package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.js +0 -926
  266. package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.js.map +0 -1
  267. package/gs/github.com/aperturerobotics/starpc/srpc/index.test.ts +0 -38
  268. package/gs/github.com/aperturerobotics/starpc/srpc/index.ts +0 -1361
  269. package/gs/github.com/aperturerobotics/starpc/srpc/meta.json +0 -46
  270. /package/compiler/{wasm_api.go → wasm-api.go} +0 -0
@@ -3,6 +3,7 @@ import {
3
3
  makeMap,
4
4
  mapGet,
5
5
  mapSet,
6
+ markAsStructValue,
6
7
  namedValueInterfaceValue,
7
8
  TypeKind,
8
9
  registerInterfaceType,
@@ -10,7 +11,17 @@ import {
10
11
  varRef,
11
12
  } from '../builtin/index.js'
12
13
  import { StructField } from './types.js'
13
- import { Int, Ptr, Struct, TypeFor, TypeOf, Uint64, ValueOf } from './type.js'
14
+ import {
15
+ Int,
16
+ Ptr,
17
+ Struct,
18
+ StructOf,
19
+ TypeFor,
20
+ TypeOf,
21
+ Uint64,
22
+ ValueOf,
23
+ } from './type.js'
24
+ import { Indirect, New, Zero } from './value.js'
14
25
 
15
26
  describe('TypeFor', () => {
16
27
  it('exposes StructField PkgPath and exported semantics', () => {
@@ -50,6 +61,101 @@ describe('TypeFor', () => {
50
61
  expect(namedIntType.Kind()).toBe(Int)
51
62
  })
52
63
 
64
+ it('preserves generic method signatures for named basic types', () => {
65
+ const stringResult = { kind: TypeKind.Basic, name: 'string' } as const
66
+ const namedIntDescriptor = {
67
+ kind: TypeKind.Basic,
68
+ name: 'int',
69
+ typeName: 'main.MyInt',
70
+ } as const
71
+ const methodSignatures = [
72
+ {
73
+ name: 'String',
74
+ args: [],
75
+ returns: [{ type: stringResult }],
76
+ },
77
+ ]
78
+ const preexisting = TypeFor({
79
+ T: {
80
+ type: namedIntDescriptor,
81
+ zero: () => 0,
82
+ },
83
+ })
84
+
85
+ const namedIntType = TypeFor({
86
+ T: {
87
+ type: namedIntDescriptor,
88
+ zero: () => 0,
89
+ methods: { String: (receiver: number) => String(receiver) },
90
+ methodSignatures,
91
+ },
92
+ })
93
+ const stringerType = TypeFor({
94
+ T: {
95
+ type: {
96
+ kind: TypeKind.Interface,
97
+ methods: methodSignatures,
98
+ },
99
+ zero: () => null,
100
+ },
101
+ })
102
+
103
+ expect(namedIntType).toBe(preexisting)
104
+ expect(namedIntType.NumMethod()).toBe(1)
105
+ const [method, ok] = namedIntType.MethodByName('String')
106
+ expect(ok).toBe(true)
107
+ expect(method.Type.NumIn()).toBe(1)
108
+ expect(method.Type.NumOut()).toBe(1)
109
+ expect(method.Type.Out(0).String()).toBe('string')
110
+ expect(namedIntType.Implements(stringerType)).toBe(true)
111
+ })
112
+
113
+ it('preserves generic method signatures for pointers to named basic types', () => {
114
+ const stringResult = { kind: TypeKind.Basic, name: 'string' } as const
115
+ const methodSignatures = [
116
+ {
117
+ name: 'String',
118
+ args: [],
119
+ returns: [{ type: stringResult }],
120
+ },
121
+ ]
122
+ const pointerType = TypeFor({
123
+ T: {
124
+ type: {
125
+ kind: TypeKind.Pointer,
126
+ elemType: {
127
+ kind: TypeKind.Basic,
128
+ name: 'bool',
129
+ typeName: 'main.MyBool',
130
+ },
131
+ },
132
+ zero: () => null,
133
+ methods: { String: () => 'true' },
134
+ methodSignatures,
135
+ },
136
+ })
137
+ const stringerType = TypeFor({
138
+ T: {
139
+ type: {
140
+ kind: TypeKind.Interface,
141
+ methods: methodSignatures,
142
+ },
143
+ zero: () => null,
144
+ },
145
+ })
146
+
147
+ expect(pointerType.Kind()).toBe(Ptr)
148
+ expect(pointerType.String()).toBe('*main.MyBool')
149
+ expect(pointerType.NumMethod()).toBe(1)
150
+ const [method, ok] = pointerType.MethodByName('String')
151
+ expect(ok).toBe(true)
152
+ expect(method.Type.NumIn()).toBe(1)
153
+ expect(method.Type.In(0)).toBe(pointerType)
154
+ expect(method.Type.NumOut()).toBe(1)
155
+ expect(method.Type.Out(0).String()).toBe('string')
156
+ expect(pointerType.Implements(stringerType)).toBe(true)
157
+ })
158
+
53
159
  it('preserves named pointer type metadata on interface boxes', () => {
54
160
  const target = varRef(0)
55
161
  const boxed = namedValueInterfaceValue(
@@ -92,6 +198,130 @@ describe('TypeFor', () => {
92
198
  expect(ifaceType.String()).toBe('interface { SomeMethod() }')
93
199
  })
94
200
 
201
+ it('handles recursive interface method metadata', () => {
202
+ const recursiveMethod = {
203
+ name: 'Visit',
204
+ args: [{ name: 'next', type: 'main.RecursiveInterface' }],
205
+ returns: [{ name: '_r0', type: 'main.RecursiveInterface' }],
206
+ }
207
+ registerInterfaceType('main.RecursiveInterface', null, [recursiveMethod])
208
+
209
+ class RecursiveImpl {
210
+ public Visit(next: unknown): unknown {
211
+ return next
212
+ }
213
+ }
214
+
215
+ const typeInfo = registerStructType(
216
+ 'main.RecursiveImpl',
217
+ new RecursiveImpl(),
218
+ [recursiveMethod],
219
+ RecursiveImpl,
220
+ [],
221
+ )
222
+ ;(RecursiveImpl as any).__typeInfo = typeInfo
223
+
224
+ const ifaceType = TypeFor({
225
+ T: { type: 'main.RecursiveInterface', zero: () => null },
226
+ })
227
+
228
+ expect(ifaceType.String()).toBe(
229
+ 'interface { Visit(main.RecursiveInterface) main.RecursiveInterface }',
230
+ )
231
+ expect(ifaceType.AssignableTo(ifaceType)).toBe(true)
232
+ expect(TypeOf(new RecursiveImpl()).AssignableTo(ifaceType)).toBe(true)
233
+ })
234
+
235
+ it('handles recursive struct field metadata', () => {
236
+ class RecursiveNode {
237
+ public Next: RecursiveNode | null = null
238
+ }
239
+
240
+ const typeInfo = registerStructType(
241
+ 'main.RecursiveNode',
242
+ new RecursiveNode(),
243
+ [],
244
+ RecursiveNode,
245
+ [
246
+ {
247
+ name: 'Next',
248
+ key: 'Next',
249
+ type: {
250
+ kind: TypeKind.Pointer,
251
+ elemType: 'main.RecursiveNode',
252
+ },
253
+ },
254
+ ],
255
+ )
256
+ Object.assign(RecursiveNode, { __typeInfo: typeInfo })
257
+
258
+ const nodeType = TypeFor({
259
+ T: { type: 'main.RecursiveNode', zero: () => new RecursiveNode() },
260
+ })
261
+
262
+ expect(nodeType.Kind()).toBe(Struct)
263
+ expect(nodeType.Field(0).Name).toBe('Next')
264
+ expect(nodeType.Field(0).Type.String()).toBe('*main.RecursiveNode')
265
+ expect(nodeType.Field(0).Type.Elem().NumField()).toBe(1)
266
+ expect(nodeType.Field(0).Type.Elem().Field(0).Type.String()).toBe(
267
+ '*main.RecursiveNode',
268
+ )
269
+ expect(TypeOf(new RecursiveNode()).Field(0).Type.String()).toBe(
270
+ '*main.RecursiveNode',
271
+ )
272
+ expect(TypeOf(new RecursiveNode()).Field(0).Type.Elem().NumField()).toBe(1)
273
+ })
274
+
275
+ it('keeps mutually recursive registered struct identity canonical', () => {
276
+ class RecursiveA {
277
+ public B: RecursiveB | null = null
278
+ }
279
+ class RecursiveB {
280
+ public A: RecursiveA | null = null
281
+ }
282
+
283
+ const aInfo = registerStructType(
284
+ 'main.RecursiveA',
285
+ new RecursiveA(),
286
+ [],
287
+ RecursiveA,
288
+ [
289
+ {
290
+ name: 'B',
291
+ key: 'B',
292
+ type: { kind: TypeKind.Pointer, elemType: 'main.RecursiveB' },
293
+ },
294
+ ],
295
+ )
296
+ const bInfo = registerStructType(
297
+ 'main.RecursiveB',
298
+ new RecursiveB(),
299
+ [],
300
+ RecursiveB,
301
+ [
302
+ {
303
+ name: 'A',
304
+ key: 'A',
305
+ type: { kind: TypeKind.Pointer, elemType: 'main.RecursiveA' },
306
+ },
307
+ ],
308
+ )
309
+ Object.assign(RecursiveA, { __typeInfo: aInfo })
310
+ Object.assign(RecursiveB, { __typeInfo: bInfo })
311
+
312
+ const aType = TypeFor({
313
+ T: { type: 'main.RecursiveA', zero: () => new RecursiveA() },
314
+ })
315
+ const bType = TypeFor({
316
+ T: { type: 'main.RecursiveB', zero: () => new RecursiveB() },
317
+ })
318
+ const bFromA = aType.Field(0).Type.Elem()
319
+
320
+ expect(bFromA.NumField()).toBe(1)
321
+ expect(bFromA).toBe(bType)
322
+ expect(bType.Field(0).Type.Elem()).toBe(aType)
323
+ })
324
+
95
325
  it('formats unnamed function signatures from type metadata', () => {
96
326
  const fnType = TypeFor({
97
327
  T: {
@@ -105,6 +335,194 @@ describe('TypeFor', () => {
105
335
  })
106
336
 
107
337
  expect(fnType.String()).toBe('func(int) string')
338
+ expect(fnType.NumIn()).toBe(1)
339
+ expect(fnType.In(0).String()).toBe('int')
340
+ expect(fnType.NumOut()).toBe(1)
341
+ expect(fnType.Out(0).String()).toBe('string')
342
+ expect(fnType.IsVariadic()).toBe(false)
343
+ })
344
+
345
+ it('preserves named and variadic function descriptors', () => {
346
+ const namedFnType = TypeFor({
347
+ T: {
348
+ type: {
349
+ kind: TypeKind.Function,
350
+ name: 'example.test.Callback',
351
+ params: [{ kind: TypeKind.Basic, name: 'int' }],
352
+ results: [{ kind: TypeKind.Basic, name: 'string' }],
353
+ },
354
+ zero: () => null,
355
+ },
356
+ })
357
+
358
+ expect(namedFnType.String()).toBe('example.test.Callback')
359
+ expect(namedFnType.PkgPath()).toBe('example.test')
360
+ expect(namedFnType.Name()).toBe('Callback')
361
+ expect(namedFnType.NumIn()).toBe(1)
362
+ expect(namedFnType.In(0).String()).toBe('int')
363
+ expect(namedFnType.NumOut()).toBe(1)
364
+ expect(namedFnType.Out(0).String()).toBe('string')
365
+
366
+ const variadicFnType = TypeFor({
367
+ T: {
368
+ type: {
369
+ kind: TypeKind.Function,
370
+ params: [
371
+ {
372
+ kind: TypeKind.Slice,
373
+ elemType: { kind: TypeKind.Basic, name: 'string' },
374
+ },
375
+ ],
376
+ results: [{ kind: TypeKind.Basic, name: 'int' }],
377
+ isVariadic: true,
378
+ },
379
+ zero: () => null,
380
+ },
381
+ })
382
+
383
+ expect(variadicFnType.String()).toBe('func(...string) int')
384
+ expect(variadicFnType.NumIn()).toBe(1)
385
+ expect(variadicFnType.In(0).String()).toBe('[]string')
386
+ expect(variadicFnType.NumOut()).toBe(1)
387
+ expect(variadicFnType.Out(0).String()).toBe('int')
388
+ expect(variadicFnType.IsVariadic()).toBe(true)
389
+ })
390
+
391
+ it('rehydrates anonymous struct descriptors as unnamed struct types', () => {
392
+ const intType = TypeOf(0)
393
+ const descriptor = {
394
+ kind: TypeKind.Struct,
395
+ methods: [],
396
+ fields: [
397
+ {
398
+ name: 'X',
399
+ key: 'X',
400
+ type: { kind: TypeKind.Basic, name: 'int' },
401
+ tag: 'json:"x"',
402
+ index: [0],
403
+ offset: 0,
404
+ exported: true,
405
+ },
406
+ ],
407
+ } as const
408
+
409
+ const typ = TypeFor({
410
+ T: {
411
+ type: descriptor,
412
+ zero: () => ({ X: 0 }),
413
+ },
414
+ })
415
+ const dynamic = StructOf([
416
+ new StructField({
417
+ Name: 'X',
418
+ Type: intType,
419
+ Tag: 'json:"x"',
420
+ }),
421
+ ])
422
+
423
+ expect(typ.String()).toBe('struct { X int "json:\\"x\\"" }')
424
+ expect(typ.Name()).toBe('')
425
+ expect(typ.PkgPath()).toBe('')
426
+ expect(typ.NumField()).toBe(1)
427
+ expect(typ.Field(0).Name).toBe('X')
428
+ expect(typ.Field(0).Tag.Get('json')).toBe('x')
429
+ expect(typ.AssignableTo(dynamic)).toBe(true)
430
+ expect(dynamic.AssignableTo(typ)).toBe(true)
431
+ })
432
+
433
+ it('uses full anonymous struct identity in method signatures', () => {
434
+ const baseField = {
435
+ name: 'X',
436
+ key: 'X',
437
+ type: { kind: TypeKind.Basic, name: 'int' },
438
+ index: [0],
439
+ offset: 0,
440
+ exported: true,
441
+ } as const
442
+ const taggedParam = {
443
+ kind: TypeKind.Struct,
444
+ methods: [],
445
+ fields: [{ ...baseField, tag: 'json:"a"' }],
446
+ } as const
447
+ const methodSignatures = [
448
+ {
449
+ name: 'Accept',
450
+ args: [{ type: taggedParam }],
451
+ returns: [],
452
+ },
453
+ ]
454
+ const receiverType = TypeFor({
455
+ T: {
456
+ type: { kind: TypeKind.Basic, name: 'int', typeName: 'main.Receiver' },
457
+ zero: () => 0,
458
+ methodSignatures,
459
+ },
460
+ })
461
+ const sameInterface = TypeFor({
462
+ T: {
463
+ type: {
464
+ kind: TypeKind.Interface,
465
+ methods: methodSignatures,
466
+ },
467
+ zero: () => null,
468
+ },
469
+ })
470
+ const differentTagInterface = TypeFor({
471
+ T: {
472
+ type: {
473
+ kind: TypeKind.Interface,
474
+ methods: [
475
+ {
476
+ name: 'Accept',
477
+ args: [
478
+ {
479
+ type: {
480
+ kind: TypeKind.Struct,
481
+ methods: [],
482
+ fields: [{ ...baseField, tag: 'json:"b"' }],
483
+ },
484
+ },
485
+ ],
486
+ returns: [],
487
+ },
488
+ ],
489
+ },
490
+ zero: () => null,
491
+ },
492
+ })
493
+ const differentEmbeddingInterface = TypeFor({
494
+ T: {
495
+ type: {
496
+ kind: TypeKind.Interface,
497
+ methods: [
498
+ {
499
+ name: 'Accept',
500
+ args: [
501
+ {
502
+ type: {
503
+ kind: TypeKind.Struct,
504
+ methods: [],
505
+ fields: [
506
+ {
507
+ ...baseField,
508
+ tag: 'json:"a"',
509
+ anonymous: true,
510
+ },
511
+ ],
512
+ },
513
+ },
514
+ ],
515
+ returns: [],
516
+ },
517
+ ],
518
+ },
519
+ zero: () => null,
520
+ },
521
+ })
522
+
523
+ expect(receiverType.Implements(sameInterface)).toBe(true)
524
+ expect(receiverType.Implements(differentTagInterface)).toBe(false)
525
+ expect(receiverType.Implements(differentEmbeddingInterface)).toBe(false)
108
526
  })
109
527
 
110
528
  it('resolves registered type names from descriptors', () => {
@@ -115,25 +533,39 @@ describe('TypeFor', () => {
115
533
  new RegisteredStruct(),
116
534
  [],
117
535
  RegisteredStruct,
118
- {},
536
+ [],
119
537
  )
120
538
  registerStructType(
121
539
  'main.RegisteredWithFields',
122
540
  new RegisteredWithFields(),
123
541
  [],
124
542
  RegisteredWithFields,
125
- {
126
- Name: { kind: TypeKind.Basic, name: 'string' },
127
- Count: {
543
+ [
544
+ {
545
+ name: 'Name',
546
+ key: 'Name',
547
+ type: { kind: TypeKind.Basic, name: 'string' },
548
+ },
549
+ {
550
+ key: 'Count',
128
551
  name: 'Total',
129
552
  type: { kind: TypeKind.Basic, name: 'int' },
130
553
  tag: 'json:"total"',
554
+ pkgPath: 'main',
555
+ anonymous: true,
556
+ index: [3],
557
+ offset: 24,
558
+ exported: false,
131
559
  },
132
- Items: {
133
- kind: TypeKind.Slice,
134
- elemType: 'main.RegisteredStruct',
560
+ {
561
+ name: 'Items',
562
+ key: 'Items',
563
+ type: {
564
+ kind: TypeKind.Slice,
565
+ elemType: 'main.RegisteredStruct',
566
+ },
135
567
  },
136
- },
568
+ ],
137
569
  )
138
570
  registerInterfaceType('main.RegisteredInterface', null, [
139
571
  { name: 'SomeMethod', args: [], returns: [] },
@@ -160,9 +592,77 @@ describe('TypeFor', () => {
160
592
  expect(fieldsType.Field(0).Name).toBe('Name')
161
593
  expect(fieldsType.Field(0).Type.String()).toBe('string')
162
594
  expect(fieldsType.Field(1).Name).toBe('Total')
595
+ expect(fieldsType.Field(1).PkgPath).toBe('main')
596
+ expect(fieldsType.Field(1).IsExported()).toBe(false)
597
+ expect(fieldsType.Field(1).Anonymous).toBe(true)
598
+ expect(fieldsType.Field(1).Index).toEqual([3])
599
+ expect(fieldsType.Field(1).Offset).toBe(24)
163
600
  expect(fieldsType.Field(1).Tag.Get('json')).toBe('total')
164
601
  expect(fieldsType.Field(2).Type.String()).toBe('[]main.RegisteredStruct')
165
- expect(fieldsType.Field(2).Type.Elem().String()).toBe('main.RegisteredStruct')
602
+ expect(fieldsType.Field(2).Type.Elem().String()).toBe(
603
+ 'main.RegisteredStruct',
604
+ )
605
+ })
606
+
607
+ it('allocates registered struct zero values through the named constructor', () => {
608
+ class RegisteredZero {
609
+ public get Name(): string {
610
+ return this._fields.Name.value
611
+ }
612
+ public set Name(value: string) {
613
+ this._fields.Name.value = value
614
+ }
615
+
616
+ public _fields: {
617
+ Name: ReturnType<typeof varRef<string>>
618
+ }
619
+
620
+ constructor(init?: Partial<{ Name?: string }>) {
621
+ this._fields = {
622
+ Name: varRef(init?.Name ?? ''),
623
+ }
624
+ }
625
+
626
+ public clone(): RegisteredZero {
627
+ return markAsStructValue(new RegisteredZero({ Name: this.Name }))
628
+ }
629
+ }
630
+
631
+ const typeInfo = registerStructType(
632
+ 'main.RegisteredZero',
633
+ () => new RegisteredZero(),
634
+ [],
635
+ RegisteredZero,
636
+ [
637
+ {
638
+ name: 'Name',
639
+ key: 'Name',
640
+ type: { kind: TypeKind.Basic, name: 'string' },
641
+ index: [0],
642
+ offset: 0,
643
+ exported: true,
644
+ },
645
+ ],
646
+ )
647
+ ;(RegisteredZero as any).__typeInfo = typeInfo
648
+
649
+ const typ = TypeOf(new RegisteredZero())
650
+ expect(Zero(typ).Interface()).toBeInstanceOf(RegisteredZero)
651
+ expect(TypeOf(Zero(typ).Interface()).String()).toBe('main.RegisteredZero')
652
+
653
+ const ptr = New(typ)
654
+ expect(ptr.Elem().Interface()).toBeInstanceOf(RegisteredZero)
655
+ ptr.Elem().FieldByName('Name').SetString('Ada')
656
+ expect((ptr.Elem().Interface() as RegisteredZero).Name).toBe('Ada')
657
+ })
658
+
659
+ it('unwraps New pointers through Indirect', () => {
660
+ const pointer = New(TypeOf(0))
661
+ const value = Indirect(pointer)
662
+
663
+ expect(value.Int()).toBe(0)
664
+ value.SetInt(7)
665
+ expect(pointer.Elem().Int()).toBe(7)
166
666
  })
167
667
 
168
668
  it('interns runtime type descriptors for reflect.Type map keys', () => {
@@ -11,6 +11,8 @@ export interface UnsafePointer {
11
11
 
12
12
  export type Pointer = UnsafePointer | null
13
13
 
14
+ export type ReflectFunc = (...args: unknown[]) => unknown
15
+
14
16
  // Define the possible JavaScript values that can be reflected
15
17
  export type ReflectValue =
16
18
  | null
@@ -20,7 +22,7 @@ export type ReflectValue =
20
22
  | bigint
21
23
  | string
22
24
  | symbol
23
- | Function //eslint-disable-line @typescript-eslint/no-unsafe-function-type
25
+ | ReflectFunc
24
26
  | object
25
27
  | unknown[]
26
28
  | Map<unknown, unknown>
@@ -37,6 +39,10 @@ export type ReflectValue =
37
39
  // Import Type and Kind from the main type module
38
40
  import { Type, Kind, Value, Kind_String, ChanDir } from './type.js'
39
41
 
42
+ type StructFieldInit = Omit<Partial<StructField>, 'Tag'> & {
43
+ Tag?: StructTag | string
44
+ }
45
+
40
46
  // Struct field representation
41
47
  export class StructField {
42
48
  public Name: string = ''
@@ -47,9 +53,13 @@ export class StructField {
47
53
  public Index: number[] = []
48
54
  public Anonymous: boolean = false
49
55
 
50
- constructor(init?: Partial<StructField>) {
56
+ constructor(init?: StructFieldInit) {
51
57
  if (init) {
52
- Object.assign(this, init)
58
+ const { Tag, ...rest } = init
59
+ Object.assign(this, rest)
60
+ if (Tag !== undefined) {
61
+ this.Tag = typeof Tag === 'string' ? new StructTag(Tag) : Tag
62
+ }
53
63
  }
54
64
  }
55
65
 
@@ -102,12 +112,17 @@ export function StructTag_Get(tag: StructTag | undefined, key: string): string {
102
112
  return tag.Get(key)
103
113
  }
104
114
 
105
- // Method representation
106
- export interface Method {
107
- Name: string
108
- Type: Type
109
- Func: Function //eslint-disable-line @typescript-eslint/no-unsafe-function-type
110
- Index: number
115
+ export class Method {
116
+ public Name = ''
117
+ public Type!: Type
118
+ public Func!: ReflectFunc
119
+ public Index = 0
120
+
121
+ constructor(init?: Partial<Method>) {
122
+ if (init) {
123
+ Object.assign(this, init)
124
+ }
125
+ }
111
126
  }
112
127
 
113
128
  // Channel type for reflection
@@ -139,17 +154,27 @@ export const SelectSend: SelectDir = 1
139
154
  export const SelectRecv: SelectDir = 2
140
155
  export const SelectDefault: SelectDir = 3
141
156
 
142
- // Slice header (internal representation)
143
- export interface SliceHeader {
144
- Data: uintptr
145
- Len: number
146
- Cap: number
157
+ export class SliceHeader {
158
+ public Data: uintptr = 0
159
+ public Len = 0
160
+ public Cap = 0
161
+
162
+ constructor(init?: Partial<SliceHeader>) {
163
+ if (init) {
164
+ Object.assign(this, init)
165
+ }
166
+ }
147
167
  }
148
168
 
149
- // String header (internal representation)
150
- export interface StringHeader {
151
- Data: uintptr
152
- Len: number
169
+ export class StringHeader {
170
+ public Data: uintptr = 0
171
+ public Len = 0
172
+
173
+ constructor(init?: Partial<StringHeader>) {
174
+ if (init) {
175
+ Object.assign(this, init)
176
+ }
177
+ }
153
178
  }
154
179
 
155
180
  // Map iterator with proper typing