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.
- package/README.md +5 -2
- package/cmd/go_js_wasm_exec/main.go +201 -0
- package/cmd/go_js_wasm_exec/main_test.go +83 -0
- package/cmd/goscript/{cmd_compile.go → cmd-compile.go} +7 -0
- package/cmd/goscript/cmd-test.go +14 -0
- package/cmd/goscript/cmd-test_test.go +1 -1
- package/compiler/compile-request.go +12 -9
- package/compiler/compliance_test.go +0 -1
- package/compiler/config.go +2 -0
- package/compiler/gotest/request.go +28 -0
- package/compiler/gotest/runner.go +353 -27
- package/compiler/gotest/runner_test.go +273 -1
- package/compiler/gotest/testdata/browserapi/browserapi_test.go +20 -0
- package/compiler/gotest/testdata/browserapi/go.mod +3 -0
- package/compiler/lowered-program.go +24 -17
- package/compiler/lowering.go +392 -127
- package/compiler/lowering_bench_test.go +41 -27
- package/compiler/override-facts.go +15 -0
- package/compiler/override-parity-verifier.go +450 -0
- package/compiler/override-parity.go +122 -0
- package/compiler/override-registry_test.go +559 -0
- package/compiler/protobuf-ts-binding.go +514 -0
- package/compiler/protobuf-ts-binding_test.go +172 -0
- package/compiler/semantic-model-types.go +9 -4
- package/compiler/semantic-model.go +282 -70
- package/compiler/semantic-model_test.go +82 -1
- package/compiler/service.go +20 -1
- package/compiler/skeleton_test.go +62 -8
- package/compiler/typescript-emitter.go +128 -13
- package/dist/gs/builtin/slice.d.ts +2 -1
- package/dist/gs/builtin/slice.js +29 -4
- package/dist/gs/builtin/slice.js.map +1 -1
- package/dist/gs/builtin/type.d.ts +13 -5
- package/dist/gs/builtin/type.js +153 -60
- package/dist/gs/builtin/type.js.map +1 -1
- package/dist/gs/builtin/varRef.d.ts +11 -0
- package/dist/gs/builtin/varRef.js +57 -2
- package/dist/gs/builtin/varRef.js.map +1 -1
- package/dist/gs/bytes/buffer.gs.js +1 -1
- package/dist/gs/bytes/buffer.gs.js.map +1 -1
- package/dist/gs/bytes/reader.gs.js +1 -1
- package/dist/gs/bytes/reader.gs.js.map +1 -1
- package/dist/gs/compress/zlib/index.d.ts +10 -3
- package/dist/gs/compress/zlib/index.js +50 -16
- package/dist/gs/compress/zlib/index.js.map +1 -1
- package/dist/gs/encoding/json/index.d.ts +114 -0
- package/dist/gs/encoding/json/index.js +544 -36
- package/dist/gs/encoding/json/index.js.map +1 -1
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +100 -0
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +564 -0
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -1
- package/dist/gs/github.com/pkg/errors/errors.js +54 -30
- package/dist/gs/github.com/pkg/errors/errors.js.map +1 -1
- package/dist/gs/go/scanner/index.d.ts +2 -0
- package/dist/gs/go/scanner/index.js +29 -5
- package/dist/gs/go/scanner/index.js.map +1 -1
- package/dist/gs/go/token/index.js +22 -6
- package/dist/gs/go/token/index.js.map +1 -1
- package/dist/gs/hash/index.d.ts +6 -0
- package/dist/gs/hash/index.js +20 -0
- package/dist/gs/hash/index.js.map +1 -1
- package/dist/gs/internal/goarch/index.d.ts +43 -3
- package/dist/gs/internal/goarch/index.js +42 -10
- package/dist/gs/internal/goarch/index.js.map +1 -1
- package/dist/gs/io/fs/fs.js +26 -14
- package/dist/gs/io/fs/fs.js.map +1 -1
- package/dist/gs/io/fs/readdir.js +4 -2
- package/dist/gs/io/fs/readdir.js.map +1 -1
- package/dist/gs/io/fs/sub.js +8 -1
- package/dist/gs/io/fs/sub.js.map +1 -1
- package/dist/gs/io/io.d.ts +2 -0
- package/dist/gs/io/io.js.map +1 -1
- package/dist/gs/math/bits/index.d.ts +5 -0
- package/dist/gs/math/bits/index.js +16 -4
- package/dist/gs/math/bits/index.js.map +1 -1
- package/dist/gs/mime/index.d.ts +16 -0
- package/dist/gs/mime/index.js +315 -6
- package/dist/gs/mime/index.js.map +1 -1
- package/dist/gs/net/http/httptest/index.d.ts +12 -0
- package/dist/gs/net/http/httptest/index.js +85 -6
- package/dist/gs/net/http/httptest/index.js.map +1 -1
- package/dist/gs/net/http/index.d.ts +300 -5
- package/dist/gs/net/http/index.js +1598 -58
- package/dist/gs/net/http/index.js.map +1 -1
- package/dist/gs/os/dir_unix.gs.js +1 -1
- package/dist/gs/os/dir_unix.gs.js.map +1 -1
- package/dist/gs/os/error.gs.js +1 -1
- package/dist/gs/os/error.gs.js.map +1 -1
- package/dist/gs/os/exec.gs.d.ts +1 -0
- package/dist/gs/os/exec.gs.js +4 -8
- package/dist/gs/os/exec.gs.js.map +1 -1
- package/dist/gs/os/exec_posix.gs.js +1 -1
- package/dist/gs/os/exec_posix.gs.js.map +1 -1
- package/dist/gs/os/index.d.ts +1 -1
- package/dist/gs/os/index.js +1 -1
- package/dist/gs/os/index.js.map +1 -1
- package/dist/gs/os/proc.gs.d.ts +4 -0
- package/dist/gs/os/proc.gs.js +12 -6
- package/dist/gs/os/proc.gs.js.map +1 -1
- package/dist/gs/os/root_js.gs.js +1 -1
- package/dist/gs/os/root_js.gs.js.map +1 -1
- package/dist/gs/os/types.gs.js +1 -1
- package/dist/gs/os/types.gs.js.map +1 -1
- package/dist/gs/os/types_js.gs.js +1 -1
- package/dist/gs/os/types_js.gs.js.map +1 -1
- package/dist/gs/os/types_unix.gs.js +1 -1
- package/dist/gs/os/types_unix.gs.js.map +1 -1
- package/dist/gs/path/path.js +11 -7
- package/dist/gs/path/path.js.map +1 -1
- package/dist/gs/reflect/index.d.ts +5 -4
- package/dist/gs/reflect/index.js +4 -3
- package/dist/gs/reflect/index.js.map +1 -1
- package/dist/gs/reflect/map.js +15 -0
- package/dist/gs/reflect/map.js.map +1 -1
- package/dist/gs/reflect/type.d.ts +25 -6
- package/dist/gs/reflect/type.js +1418 -228
- package/dist/gs/reflect/type.js.map +1 -1
- package/dist/gs/reflect/types.d.ts +14 -6
- package/dist/gs/reflect/types.js +35 -1
- package/dist/gs/reflect/types.js.map +1 -1
- package/dist/gs/reflect/value.d.ts +1 -0
- package/dist/gs/reflect/value.js +83 -41
- package/dist/gs/reflect/value.js.map +1 -1
- package/dist/gs/reflect/visiblefields.js +4 -140
- package/dist/gs/reflect/visiblefields.js.map +1 -1
- package/dist/gs/runtime/pprof/index.d.ts +8 -2
- package/dist/gs/runtime/pprof/index.js +50 -30
- package/dist/gs/runtime/pprof/index.js.map +1 -1
- package/dist/gs/runtime/runtime.js +5 -4
- package/dist/gs/runtime/runtime.js.map +1 -1
- package/dist/gs/runtime/trace/index.js +5 -19
- package/dist/gs/runtime/trace/index.js.map +1 -1
- package/dist/gs/strconv/atoi.gs.js +1 -1
- package/dist/gs/strconv/atoi.gs.js.map +1 -1
- package/dist/gs/strconv/complex.gs.d.ts +3 -0
- package/dist/gs/strconv/complex.gs.js +148 -0
- package/dist/gs/strconv/complex.gs.js.map +1 -0
- package/dist/gs/strconv/index.d.ts +1 -0
- package/dist/gs/strconv/index.js +1 -0
- package/dist/gs/strconv/index.js.map +1 -1
- package/dist/gs/strings/builder.js +1 -1
- package/dist/gs/strings/reader.js +9 -5
- package/dist/gs/strings/reader.js.map +1 -1
- package/dist/gs/strings/replace.js +15 -7
- package/dist/gs/strings/replace.js.map +1 -1
- package/dist/gs/strings/strings.d.ts +5 -0
- package/dist/gs/strings/strings.js +57 -5
- package/dist/gs/strings/strings.js.map +1 -1
- package/dist/gs/sync/atomic/type.gs.js +9 -9
- package/dist/gs/sync/atomic/type.gs.js.map +1 -1
- package/dist/gs/sync/atomic/value.gs.js +2 -2
- package/dist/gs/sync/atomic/value.gs.js.map +1 -1
- package/dist/gs/syscall/env.js +22 -14
- package/dist/gs/syscall/env.js.map +1 -1
- package/dist/gs/testing/testing.js +55 -13
- package/dist/gs/testing/testing.js.map +1 -1
- package/dist/gs/time/time.d.ts +24 -1
- package/dist/gs/time/time.js +43 -3
- package/dist/gs/time/time.js.map +1 -1
- package/dist/gs/unique/index.js +7 -1
- package/dist/gs/unique/index.js.map +1 -1
- package/go.mod +3 -3
- package/go.sum +16 -0
- package/gs/builtin/runtime-contract.test.ts +218 -21
- package/gs/builtin/slice.ts +44 -4
- package/gs/builtin/type.ts +226 -59
- package/gs/builtin/varRef.ts +85 -2
- package/gs/bytes/buffer.gs.ts +1 -1
- package/gs/bytes/reader.gs.ts +1 -1
- package/gs/compress/zlib/index.test.ts +62 -1
- package/gs/compress/zlib/index.ts +53 -16
- package/gs/compress/zlib/parity.json +51 -0
- package/gs/encoding/json/index.test.ts +360 -6
- package/gs/encoding/json/index.ts +679 -38
- package/gs/encoding/json/parity.json +81 -0
- package/gs/github.com/aperturerobotics/protobuf-go-lite/index.test.ts +211 -3
- package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +857 -1
- package/gs/github.com/pkg/errors/errors.ts +54 -30
- package/gs/go/scanner/index.test.ts +39 -56
- package/gs/go/scanner/index.ts +33 -5
- package/gs/go/scanner/parity.json +27 -0
- package/gs/go/token/index.ts +22 -6
- package/gs/hash/index.test.ts +20 -33
- package/gs/hash/index.ts +28 -0
- package/gs/hash/parity.json +21 -0
- package/gs/internal/goarch/index.test.ts +32 -0
- package/gs/internal/goarch/index.ts +45 -13
- package/gs/internal/goarch/parity.json +144 -0
- package/gs/io/fs/fs.ts +26 -14
- package/gs/io/fs/readdir.ts +4 -4
- package/gs/io/fs/sub.ts +8 -1
- package/gs/io/io.ts +1 -0
- package/gs/io/parity.json +162 -0
- package/gs/math/bits/index.test.ts +14 -1
- package/gs/math/bits/index.ts +23 -4
- package/gs/math/bits/parity.json +156 -0
- package/gs/mime/index.test.ts +90 -0
- package/gs/mime/index.ts +369 -6
- package/gs/mime/parity.json +36 -0
- package/gs/net/http/httptest/index.test.ts +98 -2
- package/gs/net/http/httptest/index.ts +101 -6
- package/gs/net/http/httptest/parity.json +15 -0
- package/gs/net/http/index.test.ts +781 -12
- package/gs/net/http/index.ts +1860 -139
- package/gs/net/http/meta.json +16 -1
- package/gs/net/http/parity.json +193 -0
- package/gs/os/dir_unix.gs.ts +1 -1
- package/gs/os/error.gs.ts +1 -1
- package/gs/os/exec.gs.ts +4 -8
- package/gs/os/exec_posix.gs.ts +1 -1
- package/gs/os/index.test.ts +9 -0
- package/gs/os/index.ts +1 -0
- package/gs/os/parity.json +9 -0
- package/gs/os/proc.gs.ts +18 -5
- package/gs/os/proc.test.ts +26 -0
- package/gs/os/root_js.gs.ts +1 -1
- package/gs/os/types.gs.ts +1 -1
- package/gs/os/types_js.gs.ts +1 -1
- package/gs/os/types_unix.gs.ts +1 -1
- package/gs/path/path.ts +11 -7
- package/gs/reflect/field.test.ts +37 -15
- package/gs/reflect/function-types.test.ts +518 -22
- package/gs/reflect/index.ts +8 -6
- package/gs/reflect/map.ts +20 -0
- package/gs/reflect/meta.json +6 -4
- package/gs/reflect/parity.json +234 -0
- package/gs/reflect/sliceat.test.ts +156 -0
- package/gs/reflect/structof.test.ts +401 -0
- package/gs/reflect/type.ts +1897 -317
- package/gs/reflect/typefor.test.ts +510 -10
- package/gs/reflect/types.ts +43 -18
- package/gs/reflect/value.ts +105 -45
- package/gs/reflect/visiblefields.ts +5 -168
- package/gs/runtime/parity.json +24 -0
- package/gs/runtime/pprof/index.test.ts +29 -7
- package/gs/runtime/pprof/index.ts +56 -30
- package/gs/runtime/pprof/parity.json +27 -0
- package/gs/runtime/runtime.test.ts +3 -1
- package/gs/runtime/runtime.ts +4 -3
- package/gs/runtime/trace/index.test.ts +5 -3
- package/gs/runtime/trace/index.ts +8 -20
- package/gs/runtime/trace/parity.json +36 -0
- package/gs/strconv/atoi.gs.ts +1 -1
- package/gs/strconv/complex.gs.ts +174 -0
- package/gs/strconv/complex.test.ts +65 -0
- package/gs/strconv/index.ts +1 -0
- package/gs/strconv/parity.json +120 -0
- package/gs/strings/builder.ts +1 -1
- package/gs/strings/parity.json +186 -0
- package/gs/strings/reader.ts +9 -5
- package/gs/strings/replace.ts +15 -7
- package/gs/strings/strings.test.ts +22 -2
- package/gs/strings/strings.ts +64 -6
- package/gs/sync/atomic/type.gs.ts +9 -9
- package/gs/sync/atomic/value.gs.ts +2 -2
- package/gs/syscall/env.ts +29 -14
- package/gs/testing/testing.test.ts +67 -0
- package/gs/testing/testing.ts +87 -19
- package/gs/time/parity.json +225 -0
- package/gs/time/time.test.ts +20 -2
- package/gs/time/time.ts +49 -7
- package/gs/unique/index.ts +7 -1
- package/package.json +4 -2
- package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.d.ts +0 -217
- package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.js +0 -926
- package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.js.map +0 -1
- package/gs/github.com/aperturerobotics/starpc/srpc/index.test.ts +0 -38
- package/gs/github.com/aperturerobotics/starpc/srpc/index.ts +0 -1361
- package/gs/github.com/aperturerobotics/starpc/srpc/meta.json +0 -46
- /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 {
|
|
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
|
-
|
|
127
|
-
|
|
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
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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(
|
|
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', () => {
|
package/gs/reflect/types.ts
CHANGED
|
@@ -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
|
-
|
|
|
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?:
|
|
56
|
+
constructor(init?: StructFieldInit) {
|
|
51
57
|
if (init) {
|
|
52
|
-
|
|
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
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
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
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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
|