goscript 0.0.25 → 0.0.28
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 +4 -4
- package/cmd/goscript/cmd_compile.go +0 -3
- package/cmd/goscript/deps.go +11 -0
- package/compiler/analysis.go +259 -55
- package/compiler/assignment.go +2 -2
- package/compiler/builtin_test.go +1 -1
- package/compiler/compiler.go +201 -49
- package/compiler/compiler_test.go +53 -0
- package/compiler/composite-lit.go +32 -8
- package/compiler/decl.go +6 -6
- package/compiler/expr-call.go +83 -0
- package/compiler/expr.go +1 -1
- package/compiler/protobuf.go +557 -0
- package/compiler/spec-struct.go +4 -0
- package/compiler/spec-value.go +11 -3
- package/compiler/spec.go +18 -1
- package/compiler/stmt-assign.go +35 -0
- package/compiler/type-assert.go +87 -0
- package/compiler/type.go +5 -2
- package/dist/gs/builtin/builtin.d.ts +19 -1
- package/dist/gs/builtin/builtin.js +85 -5
- package/dist/gs/builtin/builtin.js.map +1 -1
- package/dist/gs/builtin/channel.js.map +1 -1
- package/dist/gs/builtin/slice.d.ts +1 -1
- package/dist/gs/builtin/slice.js +59 -26
- package/dist/gs/builtin/slice.js.map +1 -1
- package/dist/gs/cmp/index.js.map +1 -1
- package/dist/gs/context/context.d.ts +1 -1
- package/dist/gs/context/context.js +20 -11
- package/dist/gs/context/context.js.map +1 -1
- package/dist/gs/errors/errors.d.ts +7 -0
- package/dist/gs/errors/errors.js +190 -0
- package/dist/gs/errors/errors.js.map +1 -0
- package/dist/gs/errors/index.d.ts +1 -0
- package/dist/gs/errors/index.js +2 -0
- package/dist/gs/errors/index.js.map +1 -0
- package/dist/gs/internal/goarch/index.js +1 -1
- package/dist/gs/internal/goarch/index.js.map +1 -1
- package/dist/gs/io/index.d.ts +1 -0
- package/dist/gs/io/index.js +2 -0
- package/dist/gs/io/index.js.map +1 -0
- package/dist/gs/io/io.d.ts +107 -0
- package/dist/gs/io/io.js +385 -0
- package/dist/gs/io/io.js.map +1 -0
- package/dist/gs/iter/iter.js.map +1 -1
- package/dist/gs/math/bits/index.js +34 -32
- package/dist/gs/math/bits/index.js.map +1 -1
- package/dist/gs/runtime/runtime.d.ts +1 -0
- package/dist/gs/runtime/runtime.js +15 -18
- package/dist/gs/runtime/runtime.js.map +1 -1
- package/dist/gs/slices/slices.d.ts +1 -1
- package/dist/gs/slices/slices.js +1 -1
- package/dist/gs/slices/slices.js.map +1 -1
- package/dist/gs/strings/builder.d.ts +18 -0
- package/dist/gs/strings/builder.js +205 -0
- package/dist/gs/strings/builder.js.map +1 -0
- package/dist/gs/strings/clone.d.ts +1 -0
- package/dist/gs/strings/clone.js +16 -0
- package/dist/gs/strings/clone.js.map +1 -0
- package/dist/gs/strings/compare.d.ts +1 -0
- package/dist/gs/strings/compare.js +14 -0
- package/dist/gs/strings/compare.js.map +1 -0
- package/dist/gs/strings/index.d.ts +2 -0
- package/dist/gs/strings/index.js +3 -0
- package/dist/gs/strings/index.js.map +1 -0
- package/dist/gs/strings/iter.d.ts +8 -0
- package/dist/gs/strings/iter.js +160 -0
- package/dist/gs/strings/iter.js.map +1 -0
- package/dist/gs/strings/reader.d.ts +34 -0
- package/dist/gs/strings/reader.js +418 -0
- package/dist/gs/strings/reader.js.map +1 -0
- package/dist/gs/strings/replace.d.ts +106 -0
- package/dist/gs/strings/replace.js +1136 -0
- package/dist/gs/strings/replace.js.map +1 -0
- package/dist/gs/strings/search.d.ts +24 -0
- package/dist/gs/strings/search.js +169 -0
- package/dist/gs/strings/search.js.map +1 -0
- package/dist/gs/strings/strings.d.ts +47 -0
- package/dist/gs/strings/strings.js +418 -0
- package/dist/gs/strings/strings.js.map +1 -0
- package/dist/gs/stringslite/index.d.ts +1 -0
- package/dist/gs/stringslite/index.js +2 -0
- package/dist/gs/stringslite/index.js.map +1 -0
- package/dist/gs/stringslite/strings.d.ts +11 -0
- package/dist/gs/stringslite/strings.js +67 -0
- package/dist/gs/stringslite/strings.js.map +1 -0
- package/dist/gs/sync/index.d.ts +1 -0
- package/dist/gs/sync/index.js +2 -0
- package/dist/gs/sync/index.js.map +1 -0
- package/dist/gs/sync/sync.d.ts +79 -0
- package/dist/gs/sync/sync.js +392 -0
- package/dist/gs/sync/sync.js.map +1 -0
- package/dist/gs/time/time.js +7 -7
- package/dist/gs/time/time.js.map +1 -1
- package/dist/gs/unicode/index.d.ts +1 -0
- package/dist/gs/unicode/index.js +2 -0
- package/dist/gs/unicode/index.js.map +1 -0
- package/dist/gs/unicode/unicode.d.ts +105 -0
- package/dist/gs/unicode/unicode.js +332 -0
- package/dist/gs/unicode/unicode.js.map +1 -0
- package/dist/gs/unicode/utf8/index.d.ts +1 -0
- package/dist/gs/unicode/utf8/index.js +3 -0
- package/dist/gs/unicode/utf8/index.js.map +1 -0
- package/dist/gs/unicode/utf8/utf8.d.ts +20 -0
- package/dist/gs/unicode/utf8/utf8.js +196 -0
- package/dist/gs/unicode/utf8/utf8.js.map +1 -0
- package/dist/gs/unsafe/index.d.ts +1 -0
- package/dist/gs/unsafe/index.js +2 -0
- package/dist/gs/unsafe/index.js.map +1 -0
- package/dist/gs/unsafe/unsafe.d.ts +11 -0
- package/dist/gs/unsafe/unsafe.js +44 -0
- package/dist/gs/unsafe/unsafe.js.map +1 -0
- package/go.mod +2 -1
- package/go.sum +6 -2
- package/gs/README.md +6 -0
- package/gs/builtin/builtin.ts +158 -0
- package/gs/builtin/channel.ts +683 -0
- package/gs/builtin/defer.ts +58 -0
- package/gs/builtin/index.ts +1 -0
- package/gs/builtin/io.ts +22 -0
- package/gs/builtin/map.ts +50 -0
- package/gs/builtin/slice.ts +1030 -0
- package/gs/builtin/type.ts +1106 -0
- package/gs/builtin/varRef.ts +25 -0
- package/gs/cmp/godoc.txt +8 -0
- package/gs/cmp/index.ts +29 -0
- package/gs/context/context.ts +401 -0
- package/gs/context/godoc.txt +69 -0
- package/gs/context/index.ts +1 -0
- package/gs/errors/errors.ts +223 -0
- package/gs/errors/godoc.txt +63 -0
- package/gs/errors/index.ts +1 -0
- package/gs/internal/goarch/godoc.txt +39 -0
- package/gs/internal/goarch/index.ts +18 -0
- package/gs/io/godoc.txt +61 -0
- package/gs/io/index.ts +1 -0
- package/gs/io/io.go +75 -0
- package/gs/io/io.ts +546 -0
- package/gs/iter/godoc.txt +203 -0
- package/gs/iter/index.ts +1 -0
- package/gs/iter/iter.ts +117 -0
- package/gs/math/bits/index.ts +356 -0
- package/gs/math/godoc.txt +76 -0
- package/gs/runtime/godoc.txt +331 -0
- package/gs/runtime/index.ts +1 -0
- package/gs/runtime/runtime.ts +178 -0
- package/gs/slices/godoc.txt +44 -0
- package/gs/slices/index.ts +1 -0
- package/gs/slices/slices.ts +22 -0
- package/gs/strings/builder.test.ts +121 -0
- package/gs/strings/builder.ts +223 -0
- package/gs/strings/clone.test.ts +43 -0
- package/gs/strings/clone.ts +17 -0
- package/gs/strings/compare.test.ts +84 -0
- package/gs/strings/compare.ts +13 -0
- package/gs/strings/godoc.txt +66 -0
- package/gs/strings/index.ts +2 -0
- package/gs/strings/iter.test.ts +343 -0
- package/gs/strings/iter.ts +171 -0
- package/gs/strings/reader.test.ts +243 -0
- package/gs/strings/reader.ts +451 -0
- package/gs/strings/replace.test.ts +181 -0
- package/gs/strings/replace.ts +1310 -0
- package/gs/strings/search.test.ts +214 -0
- package/gs/strings/search.ts +213 -0
- package/gs/strings/strings.test.ts +477 -0
- package/gs/strings/strings.ts +510 -0
- package/gs/stringslite/godoc.txt +17 -0
- package/gs/stringslite/index.ts +1 -0
- package/gs/stringslite/strings.ts +82 -0
- package/gs/sync/godoc.txt +21 -0
- package/gs/sync/index.ts +1 -0
- package/gs/sync/sync.go +64 -0
- package/gs/sync/sync.ts +449 -0
- package/gs/time/godoc.md +116 -0
- package/gs/time/godoc.txt +116 -0
- package/gs/time/index.ts +1 -0
- package/gs/time/time.ts +272 -0
- package/gs/unicode/godoc.txt +52 -0
- package/gs/unicode/index.ts +1 -0
- package/gs/unicode/unicode.go +38 -0
- package/gs/unicode/unicode.ts +418 -0
- package/gs/unicode/utf8/godoc.txt +22 -0
- package/gs/unicode/utf8/index.ts +2 -0
- package/gs/unicode/utf8/utf8.ts +227 -0
- package/gs/unsafe/godoc.txt +19 -0
- package/gs/unsafe/index.ts +1 -0
- package/gs/unsafe/unsafe.test.ts +68 -0
- package/gs/unsafe/unsafe.ts +77 -0
- package/package.json +6 -4
|
@@ -0,0 +1,1106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Represents the kinds of Go types that can be registered at runtime.
|
|
3
|
+
*/
|
|
4
|
+
export enum TypeKind {
|
|
5
|
+
Basic = 'basic',
|
|
6
|
+
Interface = 'interface',
|
|
7
|
+
Struct = 'struct',
|
|
8
|
+
Map = 'map',
|
|
9
|
+
Slice = 'slice',
|
|
10
|
+
Array = 'array',
|
|
11
|
+
Pointer = 'pointer',
|
|
12
|
+
Function = 'function',
|
|
13
|
+
Channel = 'channel',
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Base type information shared by all type kinds
|
|
18
|
+
*/
|
|
19
|
+
export interface BaseTypeInfo {
|
|
20
|
+
name?: string
|
|
21
|
+
kind: TypeKind
|
|
22
|
+
zeroValue?: any
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Represents an argument or a return value of a method.
|
|
27
|
+
*/
|
|
28
|
+
export interface MethodArg {
|
|
29
|
+
name?: string // Name of the argument/return value, if available
|
|
30
|
+
type: TypeInfo | string // TypeInfo object or string name of the type
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Represents the signature of a method, including its name, arguments, and return types.
|
|
35
|
+
*/
|
|
36
|
+
export interface MethodSignature {
|
|
37
|
+
name: string
|
|
38
|
+
args: MethodArg[]
|
|
39
|
+
returns: MethodArg[]
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Type information for struct types
|
|
44
|
+
*/
|
|
45
|
+
export interface StructTypeInfo extends BaseTypeInfo {
|
|
46
|
+
kind: TypeKind.Struct
|
|
47
|
+
methods: MethodSignature[] // Array of method signatures
|
|
48
|
+
ctor?: new (...args: any[]) => any
|
|
49
|
+
fields: Record<string, TypeInfo | string> // Field names and types for struct fields
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Type information for interface types
|
|
54
|
+
*/
|
|
55
|
+
export interface InterfaceTypeInfo extends BaseTypeInfo {
|
|
56
|
+
kind: TypeKind.Interface
|
|
57
|
+
methods: MethodSignature[] // Array of method signatures
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Type information for basic types (string, number, boolean)
|
|
62
|
+
*/
|
|
63
|
+
export interface BasicTypeInfo extends BaseTypeInfo {
|
|
64
|
+
kind: TypeKind.Basic
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Type information for map types
|
|
69
|
+
*/
|
|
70
|
+
export interface MapTypeInfo extends BaseTypeInfo {
|
|
71
|
+
kind: TypeKind.Map
|
|
72
|
+
keyType?: string | TypeInfo
|
|
73
|
+
elemType?: string | TypeInfo
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Type information for slice types
|
|
78
|
+
*/
|
|
79
|
+
export interface SliceTypeInfo extends BaseTypeInfo {
|
|
80
|
+
kind: TypeKind.Slice
|
|
81
|
+
elemType?: string | TypeInfo
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Type information for array types
|
|
86
|
+
*/
|
|
87
|
+
export interface ArrayTypeInfo extends BaseTypeInfo {
|
|
88
|
+
kind: TypeKind.Array
|
|
89
|
+
elemType?: string | TypeInfo
|
|
90
|
+
length: number
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Type information for pointer types
|
|
95
|
+
*/
|
|
96
|
+
export interface PointerTypeInfo extends BaseTypeInfo {
|
|
97
|
+
kind: TypeKind.Pointer
|
|
98
|
+
elemType?: string | TypeInfo
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Type information for function types
|
|
103
|
+
*/
|
|
104
|
+
export interface FunctionTypeInfo extends BaseTypeInfo {
|
|
105
|
+
kind: TypeKind.Function
|
|
106
|
+
params?: (string | TypeInfo)[]
|
|
107
|
+
results?: (string | TypeInfo)[]
|
|
108
|
+
isVariadic?: boolean // True if the function is variadic (e.g., ...T)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Type information for channel types
|
|
113
|
+
*/
|
|
114
|
+
export interface ChannelTypeInfo extends BaseTypeInfo {
|
|
115
|
+
kind: TypeKind.Channel
|
|
116
|
+
elemType?: string | TypeInfo
|
|
117
|
+
direction?: 'send' | 'receive' | 'both'
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* TypeInfo is used for runtime type checking.
|
|
122
|
+
* Can be a registered type (from typeRegistry) or an ad-hoc type description.
|
|
123
|
+
* When used as input to typeAssert, it can be a string (type name) or a structured description.
|
|
124
|
+
*/
|
|
125
|
+
export type TypeInfo =
|
|
126
|
+
| StructTypeInfo
|
|
127
|
+
| InterfaceTypeInfo
|
|
128
|
+
| BasicTypeInfo
|
|
129
|
+
| MapTypeInfo
|
|
130
|
+
| SliceTypeInfo
|
|
131
|
+
| ArrayTypeInfo
|
|
132
|
+
| PointerTypeInfo
|
|
133
|
+
| FunctionTypeInfo
|
|
134
|
+
| ChannelTypeInfo
|
|
135
|
+
|
|
136
|
+
// Type guard functions for TypeInfo variants
|
|
137
|
+
export function isStructTypeInfo(info: TypeInfo): info is StructTypeInfo {
|
|
138
|
+
return info.kind === TypeKind.Struct
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function isInterfaceTypeInfo(info: TypeInfo): info is InterfaceTypeInfo {
|
|
142
|
+
return info.kind === TypeKind.Interface
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function isBasicTypeInfo(info: TypeInfo): info is BasicTypeInfo {
|
|
146
|
+
return info.kind === TypeKind.Basic
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function isMapTypeInfo(info: TypeInfo): info is MapTypeInfo {
|
|
150
|
+
return info.kind === TypeKind.Map
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function isSliceTypeInfo(info: TypeInfo): info is SliceTypeInfo {
|
|
154
|
+
return info.kind === TypeKind.Slice
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function isArrayTypeInfo(info: TypeInfo): info is ArrayTypeInfo {
|
|
158
|
+
return info.kind === TypeKind.Array
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function isPointerTypeInfo(info: TypeInfo): info is PointerTypeInfo {
|
|
162
|
+
return info.kind === TypeKind.Pointer
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export function isFunctionTypeInfo(info: TypeInfo): info is FunctionTypeInfo {
|
|
166
|
+
return info.kind === TypeKind.Function
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export function isChannelTypeInfo(info: TypeInfo): info is ChannelTypeInfo {
|
|
170
|
+
return info.kind === TypeKind.Channel
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Comparable interface for Go's comparable constraint.
|
|
175
|
+
* Types that implement this can be compared with == and !=.
|
|
176
|
+
*/
|
|
177
|
+
export interface Comparable {
|
|
178
|
+
// This is a marker interface - any type that can be compared implements this
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Registry to store runtime type information
|
|
182
|
+
const typeRegistry = new Map<string, TypeInfo>()
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Registers a struct type with the runtime type system.
|
|
186
|
+
*
|
|
187
|
+
* @param name The name of the type.
|
|
188
|
+
* @param zeroValue The zero value for the type.
|
|
189
|
+
* @param methods Array of method signatures for the struct.
|
|
190
|
+
* @param ctor Constructor for the struct.
|
|
191
|
+
* @param fields Record of field names and their types.
|
|
192
|
+
* @returns The struct type information object.
|
|
193
|
+
*/
|
|
194
|
+
export const registerStructType = (
|
|
195
|
+
name: string,
|
|
196
|
+
zeroValue: any,
|
|
197
|
+
methods: MethodSignature[],
|
|
198
|
+
ctor: new (...args: any[]) => any,
|
|
199
|
+
fields: Record<string, TypeInfo | string> = {},
|
|
200
|
+
): StructTypeInfo => {
|
|
201
|
+
const typeInfo: StructTypeInfo = {
|
|
202
|
+
name,
|
|
203
|
+
kind: TypeKind.Struct,
|
|
204
|
+
zeroValue,
|
|
205
|
+
methods,
|
|
206
|
+
ctor,
|
|
207
|
+
fields,
|
|
208
|
+
}
|
|
209
|
+
typeRegistry.set(name, typeInfo)
|
|
210
|
+
return typeInfo
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Registers an interface type with the runtime type system.
|
|
215
|
+
*
|
|
216
|
+
* @param name The name of the type.
|
|
217
|
+
* @param zeroValue The zero value for the type (usually null).
|
|
218
|
+
* @param methods Array of method signatures for the interface.
|
|
219
|
+
* @returns The interface type information object.
|
|
220
|
+
*/
|
|
221
|
+
export const registerInterfaceType = (
|
|
222
|
+
name: string,
|
|
223
|
+
zeroValue: any,
|
|
224
|
+
methods: MethodSignature[],
|
|
225
|
+
): InterfaceTypeInfo => {
|
|
226
|
+
const typeInfo: InterfaceTypeInfo = {
|
|
227
|
+
name,
|
|
228
|
+
kind: TypeKind.Interface,
|
|
229
|
+
zeroValue,
|
|
230
|
+
methods,
|
|
231
|
+
}
|
|
232
|
+
typeRegistry.set(name, typeInfo)
|
|
233
|
+
return typeInfo
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Represents the result of a type assertion.
|
|
238
|
+
*/
|
|
239
|
+
export interface TypeAssertResult<T> {
|
|
240
|
+
value: T
|
|
241
|
+
ok: boolean
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Normalizes a type info to a structured TypeInfo object.
|
|
246
|
+
*
|
|
247
|
+
* @param info The type info or name.
|
|
248
|
+
* @returns A normalized TypeInfo object.
|
|
249
|
+
*/
|
|
250
|
+
function normalizeTypeInfo(info: string | TypeInfo): TypeInfo {
|
|
251
|
+
if (typeof info === 'string') {
|
|
252
|
+
const typeInfo = typeRegistry.get(info)
|
|
253
|
+
if (typeInfo) {
|
|
254
|
+
return typeInfo
|
|
255
|
+
}
|
|
256
|
+
return {
|
|
257
|
+
kind: TypeKind.Basic,
|
|
258
|
+
name: info,
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return info
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function compareOptionalTypeInfo(
|
|
266
|
+
type1?: string | TypeInfo,
|
|
267
|
+
type2?: string | TypeInfo,
|
|
268
|
+
): boolean {
|
|
269
|
+
if (type1 === undefined && type2 === undefined) return true
|
|
270
|
+
if (type1 === undefined || type2 === undefined) return false
|
|
271
|
+
// Assuming areTypeInfosIdentical will handle normalization if needed,
|
|
272
|
+
// but type1 and type2 here are expected to be direct fields from TypeInfo objects.
|
|
273
|
+
return areTypeInfosIdentical(type1, type2)
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function areFuncParamOrResultArraysIdentical(
|
|
277
|
+
arr1?: (string | TypeInfo)[],
|
|
278
|
+
arr2?: (string | TypeInfo)[],
|
|
279
|
+
): boolean {
|
|
280
|
+
if (arr1 === undefined && arr2 === undefined) return true
|
|
281
|
+
if (arr1 === undefined || arr2 === undefined) return false
|
|
282
|
+
if (arr1.length !== arr2.length) return false
|
|
283
|
+
for (let i = 0; i < arr1.length; i++) {
|
|
284
|
+
if (!areTypeInfosIdentical(arr1[i], arr2[i])) {
|
|
285
|
+
return false
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return true
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function areFuncSignaturesIdentical(
|
|
292
|
+
func1: FunctionTypeInfo,
|
|
293
|
+
func2: FunctionTypeInfo,
|
|
294
|
+
): boolean {
|
|
295
|
+
if ((func1.isVariadic || false) !== (func2.isVariadic || false)) {
|
|
296
|
+
return false
|
|
297
|
+
}
|
|
298
|
+
return (
|
|
299
|
+
areFuncParamOrResultArraysIdentical(func1.params, func2.params) &&
|
|
300
|
+
areFuncParamOrResultArraysIdentical(func1.results, func2.results)
|
|
301
|
+
)
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function areMethodArgsArraysIdentical(
|
|
305
|
+
args1?: MethodArg[],
|
|
306
|
+
args2?: MethodArg[],
|
|
307
|
+
): boolean {
|
|
308
|
+
if (args1 === undefined && args2 === undefined) return true
|
|
309
|
+
if (args1 === undefined || args2 === undefined) return false
|
|
310
|
+
if (args1.length !== args2.length) return false
|
|
311
|
+
for (let i = 0; i < args1.length; i++) {
|
|
312
|
+
// Compare based on type only, names of args/results don't affect signature identity here.
|
|
313
|
+
if (!areTypeInfosIdentical(args1[i].type, args2[i].type)) {
|
|
314
|
+
return false
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
return true
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
export function areTypeInfosIdentical(
|
|
321
|
+
type1InfoOrName: string | TypeInfo,
|
|
322
|
+
type2InfoOrName: string | TypeInfo,
|
|
323
|
+
): boolean {
|
|
324
|
+
const t1Norm = normalizeTypeInfo(type1InfoOrName)
|
|
325
|
+
const t2Norm = normalizeTypeInfo(type2InfoOrName)
|
|
326
|
+
|
|
327
|
+
if (t1Norm === t2Norm) return true // Object identity
|
|
328
|
+
if (t1Norm.kind !== t2Norm.kind) return false
|
|
329
|
+
|
|
330
|
+
// If types have names, the names must match for identity.
|
|
331
|
+
// If one has a name and the other doesn't, they are not identical.
|
|
332
|
+
if (t1Norm.name !== t2Norm.name) return false
|
|
333
|
+
|
|
334
|
+
// If both are named and names match, for Basic, Struct, Interface, this is sufficient for identity.
|
|
335
|
+
if (
|
|
336
|
+
t1Norm.name !== undefined /* && t2Norm.name is also defined and equal */
|
|
337
|
+
) {
|
|
338
|
+
if (
|
|
339
|
+
t1Norm.kind === TypeKind.Basic ||
|
|
340
|
+
t1Norm.kind === TypeKind.Struct ||
|
|
341
|
+
t1Norm.kind === TypeKind.Interface
|
|
342
|
+
) {
|
|
343
|
+
return true
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
// For other types (Pointer, Slice, etc.), or if both are anonymous (name is undefined),
|
|
347
|
+
// structural comparison is needed.
|
|
348
|
+
|
|
349
|
+
switch (t1Norm.kind) {
|
|
350
|
+
case TypeKind.Basic:
|
|
351
|
+
// Names matched if they were defined, or both undefined (which means true by t1Norm.name !== t2Norm.name being false)
|
|
352
|
+
return true
|
|
353
|
+
case TypeKind.Pointer:
|
|
354
|
+
return compareOptionalTypeInfo(
|
|
355
|
+
(t1Norm as PointerTypeInfo).elemType,
|
|
356
|
+
(t2Norm as PointerTypeInfo).elemType,
|
|
357
|
+
)
|
|
358
|
+
case TypeKind.Slice:
|
|
359
|
+
return compareOptionalTypeInfo(
|
|
360
|
+
(t1Norm as SliceTypeInfo).elemType,
|
|
361
|
+
(t2Norm as SliceTypeInfo).elemType,
|
|
362
|
+
)
|
|
363
|
+
case TypeKind.Array:
|
|
364
|
+
return (
|
|
365
|
+
(t1Norm as ArrayTypeInfo).length === (t2Norm as ArrayTypeInfo).length &&
|
|
366
|
+
compareOptionalTypeInfo(
|
|
367
|
+
(t1Norm as ArrayTypeInfo).elemType,
|
|
368
|
+
(t2Norm as ArrayTypeInfo).elemType,
|
|
369
|
+
)
|
|
370
|
+
)
|
|
371
|
+
case TypeKind.Map:
|
|
372
|
+
return (
|
|
373
|
+
compareOptionalTypeInfo(
|
|
374
|
+
(t1Norm as MapTypeInfo).keyType,
|
|
375
|
+
(t2Norm as MapTypeInfo).keyType,
|
|
376
|
+
) &&
|
|
377
|
+
compareOptionalTypeInfo(
|
|
378
|
+
(t1Norm as MapTypeInfo).elemType,
|
|
379
|
+
(t2Norm as MapTypeInfo).elemType,
|
|
380
|
+
)
|
|
381
|
+
)
|
|
382
|
+
case TypeKind.Channel:
|
|
383
|
+
return (
|
|
384
|
+
// Ensure direction property exists before comparing, or handle undefined if it can be
|
|
385
|
+
((t1Norm as ChannelTypeInfo).direction || 'both') ===
|
|
386
|
+
((t2Norm as ChannelTypeInfo).direction || 'both') &&
|
|
387
|
+
compareOptionalTypeInfo(
|
|
388
|
+
(t1Norm as ChannelTypeInfo).elemType,
|
|
389
|
+
(t2Norm as ChannelTypeInfo).elemType,
|
|
390
|
+
)
|
|
391
|
+
)
|
|
392
|
+
case TypeKind.Function:
|
|
393
|
+
return areFuncSignaturesIdentical(
|
|
394
|
+
t1Norm as FunctionTypeInfo,
|
|
395
|
+
t2Norm as FunctionTypeInfo,
|
|
396
|
+
)
|
|
397
|
+
case TypeKind.Struct:
|
|
398
|
+
case TypeKind.Interface:
|
|
399
|
+
// If we reach here, names were undefined (both anonymous) or names matched but was not Basic/Struct/Interface.
|
|
400
|
+
// For anonymous Struct/Interface, strict identity means full structural comparison.
|
|
401
|
+
// For now, we consider anonymous types not identical unless they are the same object (caught above).
|
|
402
|
+
// If they were named and matched, 'return true' was hit earlier for these kinds.
|
|
403
|
+
return false
|
|
404
|
+
default:
|
|
405
|
+
return false
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Validates that a map key matches the expected type info.
|
|
411
|
+
*
|
|
412
|
+
* @param key The key to validate
|
|
413
|
+
* @param keyTypeInfo The normalized type info for the key
|
|
414
|
+
* @returns True if the key matches the type info, false otherwise
|
|
415
|
+
*/
|
|
416
|
+
function validateMapKey(key: any, keyTypeInfo: TypeInfo): boolean {
|
|
417
|
+
if (keyTypeInfo.kind === TypeKind.Basic) {
|
|
418
|
+
// For string keys
|
|
419
|
+
if (keyTypeInfo.name === 'string') {
|
|
420
|
+
return typeof key === 'string'
|
|
421
|
+
} else if (
|
|
422
|
+
keyTypeInfo.name === 'int' ||
|
|
423
|
+
keyTypeInfo.name === 'float64' ||
|
|
424
|
+
keyTypeInfo.name === 'number'
|
|
425
|
+
) {
|
|
426
|
+
if (typeof key === 'string') {
|
|
427
|
+
return /^-?\d+(\.\d+)?$/.test(key)
|
|
428
|
+
} else {
|
|
429
|
+
return typeof key === 'number'
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
return false
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Checks if a value matches a basic type info.
|
|
438
|
+
*
|
|
439
|
+
* @param value The value to check.
|
|
440
|
+
* @param info The basic type info to match against.
|
|
441
|
+
* @returns True if the value matches the basic type, false otherwise.
|
|
442
|
+
*/
|
|
443
|
+
function matchesBasicType(value: any, info: TypeInfo): boolean {
|
|
444
|
+
if (info.name === 'string') return typeof value === 'string'
|
|
445
|
+
if (info.name === 'number' || info.name === 'int' || info.name === 'float64')
|
|
446
|
+
return typeof value === 'number'
|
|
447
|
+
if (info.name === 'boolean' || info.name === 'bool')
|
|
448
|
+
return typeof value === 'boolean'
|
|
449
|
+
return false
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Checks if a value matches a struct type info.
|
|
454
|
+
*
|
|
455
|
+
* @param value The value to check.
|
|
456
|
+
* @param info The struct type info to match against.
|
|
457
|
+
* @returns True if the value matches the struct type, false otherwise.
|
|
458
|
+
*/
|
|
459
|
+
function matchesStructType(value: any, info: TypeInfo): boolean {
|
|
460
|
+
if (!isStructTypeInfo(info)) return false
|
|
461
|
+
|
|
462
|
+
// For structs, use instanceof with the constructor
|
|
463
|
+
if (info.ctor && value instanceof info.ctor) {
|
|
464
|
+
return true
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// Check if the value has all methods defined in the struct's TypeInfo
|
|
468
|
+
// This is a structural check, not a signature check here.
|
|
469
|
+
// Signature checks are more relevant for interface satisfaction.
|
|
470
|
+
if (info.methods && typeof value === 'object' && value !== null) {
|
|
471
|
+
const allMethodsExist = info.methods.every(
|
|
472
|
+
(methodSig) => typeof (value as any)[methodSig.name] === 'function',
|
|
473
|
+
)
|
|
474
|
+
if (!allMethodsExist) {
|
|
475
|
+
return false
|
|
476
|
+
}
|
|
477
|
+
// Further signature checking could be added here if needed for struct-to-struct assignability
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
if (typeof value === 'object' && value !== null && info.fields) {
|
|
481
|
+
const fieldNames = Object.keys(info.fields || {})
|
|
482
|
+
const valueFields = Object.keys(value)
|
|
483
|
+
|
|
484
|
+
const fieldsExist = fieldNames.every((field) => field in value)
|
|
485
|
+
const sameFieldCount = valueFields.length === fieldNames.length
|
|
486
|
+
const allFieldsInStruct = valueFields.every((field) =>
|
|
487
|
+
fieldNames.includes(field),
|
|
488
|
+
)
|
|
489
|
+
|
|
490
|
+
if (fieldsExist && sameFieldCount && allFieldsInStruct) {
|
|
491
|
+
return Object.entries(info.fields).every(([fieldName, fieldType]) => {
|
|
492
|
+
return matchesType(
|
|
493
|
+
value[fieldName],
|
|
494
|
+
normalizeTypeInfo(fieldType as TypeInfo | string),
|
|
495
|
+
)
|
|
496
|
+
})
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
return false
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
return false
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* Checks if a value matches an interface type info by verifying it implements
|
|
507
|
+
* all required methods with compatible signatures.
|
|
508
|
+
*
|
|
509
|
+
* @param value The value to check.
|
|
510
|
+
* @param info The interface type info to match against.
|
|
511
|
+
* @returns True if the value matches the interface type, false otherwise.
|
|
512
|
+
*/
|
|
513
|
+
function matchesInterfaceType(value: any, info: TypeInfo): boolean {
|
|
514
|
+
// Check basic conditions first
|
|
515
|
+
if (
|
|
516
|
+
!isInterfaceTypeInfo(info) ||
|
|
517
|
+
typeof value !== 'object' ||
|
|
518
|
+
value === null
|
|
519
|
+
) {
|
|
520
|
+
return false
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// For interfaces, check if the value has all the required methods with compatible signatures
|
|
524
|
+
return info.methods.every((requiredMethodSig) => {
|
|
525
|
+
const actualMethod = (value as any)[requiredMethodSig.name]
|
|
526
|
+
|
|
527
|
+
// Method must exist and be a function
|
|
528
|
+
if (typeof actualMethod !== 'function') {
|
|
529
|
+
return false
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// Check parameter count (basic arity check)
|
|
533
|
+
// Note: This is a simplified check as JavaScript functions can have optional/rest parameters
|
|
534
|
+
const declaredParamCount = actualMethod.length
|
|
535
|
+
const requiredParamCount = requiredMethodSig.args.length
|
|
536
|
+
|
|
537
|
+
// Strict arity checking can be problematic in JS, so we'll be lenient
|
|
538
|
+
// A method with fewer params than required is definitely incompatible
|
|
539
|
+
if (declaredParamCount < requiredParamCount) {
|
|
540
|
+
return false
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// Check return types if we can determine them
|
|
544
|
+
// This is challenging in JavaScript without runtime type information
|
|
545
|
+
|
|
546
|
+
// If the value has a __goTypeName property, it might be a registered type
|
|
547
|
+
// with more type information available
|
|
548
|
+
if (value.__goTypeName) {
|
|
549
|
+
const valueTypeInfo = typeRegistry.get(value.__goTypeName)
|
|
550
|
+
if (valueTypeInfo && isStructTypeInfo(valueTypeInfo)) {
|
|
551
|
+
// Find the matching method in the value's type info
|
|
552
|
+
const valueMethodSig = valueTypeInfo.methods.find(
|
|
553
|
+
(m) => m.name === requiredMethodSig.name,
|
|
554
|
+
)
|
|
555
|
+
|
|
556
|
+
if (valueMethodSig) {
|
|
557
|
+
// Compare return types
|
|
558
|
+
if (
|
|
559
|
+
valueMethodSig.returns.length !== requiredMethodSig.returns.length
|
|
560
|
+
) {
|
|
561
|
+
return false
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
// Compare each return type for compatibility
|
|
565
|
+
for (let i = 0; i < requiredMethodSig.returns.length; i++) {
|
|
566
|
+
const requiredReturnType = normalizeTypeInfo(
|
|
567
|
+
requiredMethodSig.returns[i].type,
|
|
568
|
+
)
|
|
569
|
+
const valueReturnType = normalizeTypeInfo(
|
|
570
|
+
valueMethodSig.returns[i].type,
|
|
571
|
+
)
|
|
572
|
+
|
|
573
|
+
// For interface return types, we need to check if the value's return type
|
|
574
|
+
// implements the required interface
|
|
575
|
+
if (isInterfaceTypeInfo(requiredReturnType)) {
|
|
576
|
+
// This would be a recursive check, but we'll simplify for now
|
|
577
|
+
// by just checking if the types are the same or if the value type
|
|
578
|
+
// is registered as implementing the interface
|
|
579
|
+
if (requiredReturnType.name !== valueReturnType.name) {
|
|
580
|
+
// Check if valueReturnType implements requiredReturnType
|
|
581
|
+
// This would require additional implementation tracking
|
|
582
|
+
return false
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
// For non-interface types, check direct type compatibility
|
|
586
|
+
else if (requiredReturnType.name !== valueReturnType.name) {
|
|
587
|
+
return false
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
// Similarly, we could check parameter types for compatibility
|
|
592
|
+
// but we'll skip that for brevity
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// If we can't determine detailed type information, we'll accept the method
|
|
598
|
+
// as long as it exists with a compatible arity
|
|
599
|
+
return true
|
|
600
|
+
})
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
/**
|
|
604
|
+
* Checks if a value matches a map type info.
|
|
605
|
+
*
|
|
606
|
+
* @param value The value to check.
|
|
607
|
+
* @param info The map type info to match against.
|
|
608
|
+
* @returns True if the value matches the map type, false otherwise.
|
|
609
|
+
*/
|
|
610
|
+
function matchesMapType(value: any, info: TypeInfo): boolean {
|
|
611
|
+
if (typeof value !== 'object' || value === null) return false
|
|
612
|
+
if (!isMapTypeInfo(info)) return false
|
|
613
|
+
|
|
614
|
+
if (info.keyType || info.elemType) {
|
|
615
|
+
let entries: [any, any][] = []
|
|
616
|
+
|
|
617
|
+
if (value instanceof Map) {
|
|
618
|
+
entries = Array.from(value.entries())
|
|
619
|
+
} else {
|
|
620
|
+
entries = Object.entries(value)
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
if (entries.length === 0) return true // Empty map matches any map type
|
|
624
|
+
|
|
625
|
+
const sampleSize = Math.min(5, entries.length)
|
|
626
|
+
for (let i = 0; i < sampleSize; i++) {
|
|
627
|
+
const [k, v] = entries[i]
|
|
628
|
+
|
|
629
|
+
if (info.keyType) {
|
|
630
|
+
if (
|
|
631
|
+
!validateMapKey(
|
|
632
|
+
k,
|
|
633
|
+
normalizeTypeInfo(info.keyType as string | TypeInfo),
|
|
634
|
+
)
|
|
635
|
+
) {
|
|
636
|
+
return false
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
if (
|
|
641
|
+
info.elemType &&
|
|
642
|
+
!matchesType(v, normalizeTypeInfo(info.elemType as string | TypeInfo))
|
|
643
|
+
) {
|
|
644
|
+
return false
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
return true
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* Checks if a value matches an array or slice type info.
|
|
654
|
+
*
|
|
655
|
+
* @param value The value to check.
|
|
656
|
+
* @param info The array or slice type info to match against.
|
|
657
|
+
* @returns True if the value matches the array or slice type, false otherwise.
|
|
658
|
+
*/
|
|
659
|
+
function matchesArrayOrSliceType(value: any, info: TypeInfo): boolean {
|
|
660
|
+
// For slices and arrays, check if the value is an array and sample element types
|
|
661
|
+
if (!Array.isArray(value)) return false
|
|
662
|
+
if (!isArrayTypeInfo(info) && !isSliceTypeInfo(info)) return false
|
|
663
|
+
|
|
664
|
+
if (info.elemType) {
|
|
665
|
+
const arr = value as any[]
|
|
666
|
+
if (arr.length === 0) return true // Empty array matches any array type
|
|
667
|
+
|
|
668
|
+
const sampleSize = Math.min(5, arr.length)
|
|
669
|
+
for (let i = 0; i < sampleSize; i++) {
|
|
670
|
+
if (
|
|
671
|
+
!matchesType(
|
|
672
|
+
arr[i],
|
|
673
|
+
normalizeTypeInfo(info.elemType as string | TypeInfo),
|
|
674
|
+
)
|
|
675
|
+
) {
|
|
676
|
+
return false
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
return true
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
/**
|
|
685
|
+
* Checks if a value matches a pointer type info.
|
|
686
|
+
*
|
|
687
|
+
* @param value The value to check.
|
|
688
|
+
* @param info The pointer type info to match against.
|
|
689
|
+
* @returns True if the value matches the pointer type, false otherwise.
|
|
690
|
+
*/
|
|
691
|
+
function matchesPointerType(value: any, info: TypeInfo): boolean {
|
|
692
|
+
// Allow null/undefined values to match pointer types to support nil pointer assertions
|
|
693
|
+
// This enables Go's nil pointer type assertions like `ptr, ok := i.(*SomeType)` to work correctly
|
|
694
|
+
if (value === null || value === undefined) {
|
|
695
|
+
return true
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
// Check if the value is a VarRef (has a 'value' property)
|
|
699
|
+
if (typeof value !== 'object' || !('value' in value)) {
|
|
700
|
+
return false
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
if (!isPointerTypeInfo(info)) return false
|
|
704
|
+
|
|
705
|
+
if (info.elemType) {
|
|
706
|
+
const elemTypeInfo = normalizeTypeInfo(info.elemType as string | TypeInfo)
|
|
707
|
+
return matchesType(value.value, elemTypeInfo)
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
return true
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
/**
|
|
714
|
+
* Checks if a value matches a function type info.
|
|
715
|
+
*
|
|
716
|
+
* @param value The value to check.
|
|
717
|
+
* @param info The function type info to match against.
|
|
718
|
+
* @returns True if the value matches the function type, false otherwise.
|
|
719
|
+
*/
|
|
720
|
+
function matchesFunctionType(value: any, info: FunctionTypeInfo): boolean {
|
|
721
|
+
// First check if the value is a function
|
|
722
|
+
if (typeof value !== 'function') {
|
|
723
|
+
return false
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
// This is important for named function types
|
|
727
|
+
if (info.name && value.__goTypeName) {
|
|
728
|
+
return info.name === value.__goTypeName
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
return true
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
/**
|
|
735
|
+
* Checks if a value matches a channel type info.
|
|
736
|
+
*
|
|
737
|
+
* @param value The value to check.
|
|
738
|
+
* @param info The channel type info to match against.
|
|
739
|
+
* @returns True if the value matches the channel type, false otherwise.
|
|
740
|
+
*/
|
|
741
|
+
function matchesChannelType(value: any, info: ChannelTypeInfo): boolean {
|
|
742
|
+
// First check if it's a channel or channel reference
|
|
743
|
+
if (typeof value !== 'object' || value === null) {
|
|
744
|
+
return false
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
// If it's a ChannelRef, get the underlying channel
|
|
748
|
+
let channel = value
|
|
749
|
+
let valueDirection = 'both'
|
|
750
|
+
|
|
751
|
+
if ('channel' in value && 'direction' in value) {
|
|
752
|
+
channel = value.channel
|
|
753
|
+
valueDirection = value.direction
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
// Check if it has channel methods
|
|
757
|
+
if (
|
|
758
|
+
!('send' in channel) ||
|
|
759
|
+
!('receive' in channel) ||
|
|
760
|
+
!('close' in channel) ||
|
|
761
|
+
typeof channel.send !== 'function' ||
|
|
762
|
+
typeof channel.receive !== 'function' ||
|
|
763
|
+
typeof channel.close !== 'function'
|
|
764
|
+
) {
|
|
765
|
+
return false
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
if (info.elemType) {
|
|
769
|
+
if (
|
|
770
|
+
info.elemType === 'string' &&
|
|
771
|
+
'zeroValue' in channel &&
|
|
772
|
+
channel.zeroValue !== ''
|
|
773
|
+
) {
|
|
774
|
+
return false
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
if (
|
|
778
|
+
info.elemType === 'number' &&
|
|
779
|
+
'zeroValue' in channel &&
|
|
780
|
+
typeof channel.zeroValue !== 'number'
|
|
781
|
+
) {
|
|
782
|
+
return false
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
if (info.direction) {
|
|
787
|
+
return valueDirection === info.direction
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
return true
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
/**
|
|
794
|
+
* Checks if a value matches a type info.
|
|
795
|
+
*
|
|
796
|
+
* @param value The value to check.
|
|
797
|
+
* @param info The type info to match against.
|
|
798
|
+
* @returns True if the value matches the type info, false otherwise.
|
|
799
|
+
*/
|
|
800
|
+
function matchesType(value: any, info: TypeInfo): boolean {
|
|
801
|
+
if (value === null || value === undefined) {
|
|
802
|
+
return false
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
switch (info.kind) {
|
|
806
|
+
case TypeKind.Basic:
|
|
807
|
+
return matchesBasicType(value, info)
|
|
808
|
+
|
|
809
|
+
case TypeKind.Struct:
|
|
810
|
+
return matchesStructType(value, info)
|
|
811
|
+
|
|
812
|
+
case TypeKind.Interface:
|
|
813
|
+
return matchesInterfaceType(value, info)
|
|
814
|
+
|
|
815
|
+
case TypeKind.Map:
|
|
816
|
+
return matchesMapType(value, info)
|
|
817
|
+
|
|
818
|
+
case TypeKind.Slice:
|
|
819
|
+
case TypeKind.Array:
|
|
820
|
+
return matchesArrayOrSliceType(value, info)
|
|
821
|
+
|
|
822
|
+
case TypeKind.Pointer:
|
|
823
|
+
return matchesPointerType(value, info)
|
|
824
|
+
|
|
825
|
+
case TypeKind.Function:
|
|
826
|
+
return matchesFunctionType(value, info as FunctionTypeInfo)
|
|
827
|
+
|
|
828
|
+
case TypeKind.Channel:
|
|
829
|
+
return matchesChannelType(value, info)
|
|
830
|
+
|
|
831
|
+
default:
|
|
832
|
+
console.warn(
|
|
833
|
+
`Type matching for kind '${(info as TypeInfo).kind}' not implemented.`,
|
|
834
|
+
)
|
|
835
|
+
return false
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
/**
|
|
840
|
+
* Performs a type assertion on a value against a specified type.
|
|
841
|
+
* Returns an object containing the value (cast to type T) and a boolean indicating success.
|
|
842
|
+
* This is used to implement Go's type assertion with comma-ok idiom: value, ok := x.(Type)
|
|
843
|
+
*
|
|
844
|
+
* @param value The value to check against the type
|
|
845
|
+
* @param typeInfo The type information to check against (can be a string name or TypeInfo object)
|
|
846
|
+
* @returns An object with the asserted value and a boolean indicating if the assertion succeeded
|
|
847
|
+
*/
|
|
848
|
+
export function typeAssert<T>(
|
|
849
|
+
value: any,
|
|
850
|
+
typeInfo: string | TypeInfo,
|
|
851
|
+
): TypeAssertResult<T> {
|
|
852
|
+
const normalizedType = normalizeTypeInfo(typeInfo)
|
|
853
|
+
|
|
854
|
+
if (isPointerTypeInfo(normalizedType) && value === null) {
|
|
855
|
+
return { value: null as unknown as T, ok: true }
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
if (
|
|
859
|
+
isStructTypeInfo(normalizedType) &&
|
|
860
|
+
normalizedType.methods &&
|
|
861
|
+
normalizedType.methods.length > 0 &&
|
|
862
|
+
typeof value === 'object' &&
|
|
863
|
+
value !== null
|
|
864
|
+
) {
|
|
865
|
+
// Check if the value implements all methods of the struct type with compatible signatures.
|
|
866
|
+
// This is more for interface satisfaction by a struct.
|
|
867
|
+
// For struct-to-struct assertion, usually instanceof or field checks are primary.
|
|
868
|
+
const allMethodsMatch = normalizedType.methods.every(
|
|
869
|
+
(requiredMethodSig) => {
|
|
870
|
+
const actualMethod = (value as any)[requiredMethodSig.name]
|
|
871
|
+
if (typeof actualMethod !== 'function') {
|
|
872
|
+
return false
|
|
873
|
+
}
|
|
874
|
+
const valueTypeInfoVal = (value as any).$typeInfo
|
|
875
|
+
if (valueTypeInfoVal) {
|
|
876
|
+
const normalizedValueType = normalizeTypeInfo(valueTypeInfoVal)
|
|
877
|
+
if (
|
|
878
|
+
isStructTypeInfo(normalizedValueType) ||
|
|
879
|
+
isInterfaceTypeInfo(normalizedValueType)
|
|
880
|
+
) {
|
|
881
|
+
const actualValueMethodSig = normalizedValueType.methods.find(
|
|
882
|
+
(m) => m.name === requiredMethodSig.name,
|
|
883
|
+
)
|
|
884
|
+
if (actualValueMethodSig) {
|
|
885
|
+
// Perform full signature comparison using MethodSignatures
|
|
886
|
+
const paramsMatch = areMethodArgsArraysIdentical(
|
|
887
|
+
requiredMethodSig.args,
|
|
888
|
+
actualValueMethodSig.args,
|
|
889
|
+
)
|
|
890
|
+
const resultsMatch = areMethodArgsArraysIdentical(
|
|
891
|
+
requiredMethodSig.returns,
|
|
892
|
+
actualValueMethodSig.returns,
|
|
893
|
+
)
|
|
894
|
+
return paramsMatch && resultsMatch
|
|
895
|
+
} else {
|
|
896
|
+
// Value has TypeInfo listing methods, but this specific method isn't listed.
|
|
897
|
+
// This implies a mismatch for strict signature check based on TypeInfo.
|
|
898
|
+
return false
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
// If the function exists and there's no type info, assume it matches.
|
|
904
|
+
// TODO check this
|
|
905
|
+
return true
|
|
906
|
+
},
|
|
907
|
+
)
|
|
908
|
+
|
|
909
|
+
if (allMethodsMatch) {
|
|
910
|
+
return { value: value as T, ok: true }
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
if (
|
|
915
|
+
isStructTypeInfo(normalizedType) &&
|
|
916
|
+
normalizedType.fields &&
|
|
917
|
+
typeof value === 'object' &&
|
|
918
|
+
value !== null
|
|
919
|
+
) {
|
|
920
|
+
const fieldNames = Object.keys(normalizedType.fields)
|
|
921
|
+
const valueFields = Object.keys(value)
|
|
922
|
+
|
|
923
|
+
// For struct type assertions, we need exact field matching
|
|
924
|
+
const structFieldsMatch =
|
|
925
|
+
fieldNames.length === valueFields.length &&
|
|
926
|
+
fieldNames.every((field: string) => field in value) &&
|
|
927
|
+
valueFields.every((field) => fieldNames.includes(field))
|
|
928
|
+
|
|
929
|
+
if (structFieldsMatch) {
|
|
930
|
+
const typesMatch = Object.entries(normalizedType.fields).every(
|
|
931
|
+
([fieldName, fieldType]) => {
|
|
932
|
+
return matchesType(
|
|
933
|
+
value[fieldName],
|
|
934
|
+
normalizeTypeInfo(fieldType as TypeInfo | string),
|
|
935
|
+
)
|
|
936
|
+
},
|
|
937
|
+
)
|
|
938
|
+
|
|
939
|
+
return { value: value as T, ok: typesMatch }
|
|
940
|
+
} else {
|
|
941
|
+
return { value: null as unknown as T, ok: false }
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
if (
|
|
946
|
+
isMapTypeInfo(normalizedType) &&
|
|
947
|
+
typeof value === 'object' &&
|
|
948
|
+
value !== null
|
|
949
|
+
) {
|
|
950
|
+
if (normalizedType.keyType || normalizedType.elemType) {
|
|
951
|
+
let entries: [any, any][] = []
|
|
952
|
+
|
|
953
|
+
if (value instanceof Map) {
|
|
954
|
+
entries = Array.from(value.entries())
|
|
955
|
+
} else {
|
|
956
|
+
entries = Object.entries(value)
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
if (entries.length === 0) {
|
|
960
|
+
return { value: value as T, ok: true }
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
const sampleSize = Math.min(5, entries.length)
|
|
964
|
+
for (let i = 0; i < sampleSize; i++) {
|
|
965
|
+
const [k, v] = entries[i]
|
|
966
|
+
|
|
967
|
+
if (normalizedType.keyType) {
|
|
968
|
+
if (
|
|
969
|
+
!validateMapKey(
|
|
970
|
+
k,
|
|
971
|
+
normalizeTypeInfo(normalizedType.keyType as string | TypeInfo),
|
|
972
|
+
)
|
|
973
|
+
) {
|
|
974
|
+
return { value: null as unknown as T, ok: false }
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
if (normalizedType.elemType) {
|
|
979
|
+
const elemTypeInfo = normalizeTypeInfo(
|
|
980
|
+
normalizedType.elemType as string | TypeInfo,
|
|
981
|
+
)
|
|
982
|
+
if (!matchesType(v, elemTypeInfo)) {
|
|
983
|
+
return { value: null as unknown as T, ok: false }
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
// If we get here, the map type assertion passes
|
|
989
|
+
return { value: value as T, ok: true }
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
const matches = matchesType(value, normalizedType)
|
|
994
|
+
|
|
995
|
+
if (matches) {
|
|
996
|
+
return { value: value as T, ok: true }
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
// If we get here, the assertion failed
|
|
1000
|
+
// For registered types, use the zero value from the registry
|
|
1001
|
+
if (typeof typeInfo === 'string') {
|
|
1002
|
+
const registeredType = typeRegistry.get(typeInfo)
|
|
1003
|
+
if (registeredType && registeredType.zeroValue !== undefined) {
|
|
1004
|
+
return { value: registeredType.zeroValue as T, ok: false }
|
|
1005
|
+
}
|
|
1006
|
+
} else if (normalizedType.zeroValue !== undefined) {
|
|
1007
|
+
return { value: normalizedType.zeroValue as T, ok: false }
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
return { value: null as unknown as T, ok: false }
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
/**
|
|
1014
|
+
* Performs a type assertion on a value against a specified type.
|
|
1015
|
+
* Returns the value (cast to type T) if the assertion is successful,
|
|
1016
|
+
* otherwise throws a runtime error.
|
|
1017
|
+
* This is used to implement Go's single-value type assertion: value := x.(Type)
|
|
1018
|
+
*
|
|
1019
|
+
* @param value The value to check against the type
|
|
1020
|
+
* @param typeInfo The type information to check against (can be a string name or TypeInfo object)
|
|
1021
|
+
* @returns The asserted value if the assertion succeeded
|
|
1022
|
+
* @throws Error if the type assertion fails
|
|
1023
|
+
*/
|
|
1024
|
+
export function mustTypeAssert<T>(value: any, typeInfo: string | TypeInfo): T {
|
|
1025
|
+
const { value: assertedValue, ok } = typeAssert<T>(value, typeInfo)
|
|
1026
|
+
if (!ok) {
|
|
1027
|
+
const targetTypeName =
|
|
1028
|
+
typeof typeInfo === 'string' ? typeInfo : (
|
|
1029
|
+
typeInfo.name || JSON.stringify(typeInfo)
|
|
1030
|
+
)
|
|
1031
|
+
let valueTypeName: string | 'nil' = typeof value
|
|
1032
|
+
if (value && value.constructor && value.constructor.name) {
|
|
1033
|
+
valueTypeName = value.constructor.name
|
|
1034
|
+
}
|
|
1035
|
+
if (value === null) {
|
|
1036
|
+
valueTypeName = 'nil'
|
|
1037
|
+
}
|
|
1038
|
+
throw new Error(
|
|
1039
|
+
`inline type conversion panic: value is ${valueTypeName}, not ${targetTypeName}`,
|
|
1040
|
+
)
|
|
1041
|
+
}
|
|
1042
|
+
return assertedValue
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
/**
|
|
1046
|
+
* Checks if a value is of a specific type.
|
|
1047
|
+
* Similar to typeAssert but only returns a boolean without extracting the value.
|
|
1048
|
+
*
|
|
1049
|
+
* @param value The value to check
|
|
1050
|
+
* @param typeInfo The type information to check against
|
|
1051
|
+
* @returns True if the value matches the type, false otherwise
|
|
1052
|
+
*/
|
|
1053
|
+
export function is(value: any, typeInfo: string | TypeInfo): boolean {
|
|
1054
|
+
return matchesType(value, normalizeTypeInfo(typeInfo))
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
/**
|
|
1058
|
+
* Represents a case in a type switch statement.
|
|
1059
|
+
* Each case matches against one or more types and contains a body function to execute when matched.
|
|
1060
|
+
*/
|
|
1061
|
+
export interface TypeSwitchCase {
|
|
1062
|
+
types: (string | TypeInfo)[] // Array of types for this case (e.g., case int, string:)
|
|
1063
|
+
body: (value?: any) => void // Function representing the case body. 'value' is the asserted value if applicable.
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
/**
|
|
1067
|
+
* Helper for Go's type switch statement.
|
|
1068
|
+
* Executes the body of the first case whose type matches the value.
|
|
1069
|
+
*
|
|
1070
|
+
* @param value The value being switched upon.
|
|
1071
|
+
* @param cases An array of TypeSwitchCase objects.
|
|
1072
|
+
* @param defaultCase Optional function for the default case.
|
|
1073
|
+
*/
|
|
1074
|
+
export function typeSwitch(
|
|
1075
|
+
value: any,
|
|
1076
|
+
cases: TypeSwitchCase[],
|
|
1077
|
+
defaultCase?: () => void,
|
|
1078
|
+
): void {
|
|
1079
|
+
for (const caseObj of cases) {
|
|
1080
|
+
// For cases with multiple types (case T1, T2:), use $.is
|
|
1081
|
+
if (caseObj.types.length > 1) {
|
|
1082
|
+
const matchesAny = caseObj.types.some((typeInfo) => is(value, typeInfo))
|
|
1083
|
+
if (matchesAny) {
|
|
1084
|
+
// For multi-type cases, the case variable (if any) gets the original value
|
|
1085
|
+
caseObj.body(value)
|
|
1086
|
+
return // Found a match, exit switch
|
|
1087
|
+
}
|
|
1088
|
+
} else if (caseObj.types.length === 1) {
|
|
1089
|
+
// For single-type cases (case T:), use $.typeAssert to get the typed value and ok status
|
|
1090
|
+
const typeInfo = caseObj.types[0]
|
|
1091
|
+
const { value: assertedValue, ok } = typeAssert(value, typeInfo)
|
|
1092
|
+
if (ok) {
|
|
1093
|
+
// Pass the asserted value to the case body function
|
|
1094
|
+
caseObj.body(assertedValue)
|
|
1095
|
+
|
|
1096
|
+
return // Found a match, exit switch
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
// Note: Cases with 0 types are not valid in Go type switches
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
// If no case matched and a default case exists, execute it
|
|
1103
|
+
if (defaultCase) {
|
|
1104
|
+
defaultCase()
|
|
1105
|
+
}
|
|
1106
|
+
}
|