goscript 0.0.13 → 0.0.16
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/LICENSE +2 -1
- package/README.md +163 -45
- package/builtin/builtin.ts +1169 -178
- package/cmd/goscript/cmd_compile.go +2 -2
- package/compiler/analysis.go +726 -0
- package/compiler/compiler.go +5701 -4
- package/compiler/compiler_test.go +104 -0
- package/compiler/config.go +3 -3
- package/compiler/config_test.go +89 -0
- package/compiler/index.ts +1 -1
- package/compiler/output.go +26 -0
- package/compiler/write-type-spec.go +506 -0
- package/compiler/writer.go +11 -0
- package/dist/builtin/builtin.d.ts +204 -67
- package/dist/builtin/builtin.js +846 -144
- package/dist/builtin/builtin.js.map +1 -1
- package/dist/compiler/index.d.ts +1 -1
- package/go.mod +5 -6
- package/go.sum +6 -11
- package/package.json +21 -5
- package/compiler/compile.go +0 -190
- package/compiler/compile_comment.go +0 -41
- package/compiler/compile_decls.go +0 -84
- package/compiler/compile_expr.go +0 -1022
- package/compiler/compile_field.go +0 -110
- package/compiler/compile_spec.go +0 -566
- package/compiler/compile_stmt.go +0 -1616
- package/compiler/context.go +0 -9
- package/compiler/file_compiler.go +0 -80
- package/compiler/output_path.go +0 -31
- package/compiler/pkg_compiler.go +0 -72
- package/compiler/types/tokens.go +0 -66
- package/compiler/types/types.go +0 -46
- package/dist/compliance/tests/array_literal/array_literal.gs.d.ts +0 -1
- package/dist/compliance/tests/array_literal/array_literal.gs.js +0 -15
- package/dist/compliance/tests/array_literal/array_literal.gs.js.map +0 -1
- package/dist/compliance/tests/async_basic/async_basic.gs.d.ts +0 -1
- package/dist/compliance/tests/async_basic/async_basic.gs.js +0 -24
- package/dist/compliance/tests/async_basic/async_basic.gs.js.map +0 -1
- package/dist/compliance/tests/async_defer_statement/async_defer_statement.gs.d.ts +0 -1
- package/dist/compliance/tests/async_defer_statement/async_defer_statement.gs.js +0 -82
- package/dist/compliance/tests/async_defer_statement/async_defer_statement.gs.js.map +0 -1
- package/dist/compliance/tests/basic_arithmetic/basic_arithmetic.gs.d.ts +0 -1
- package/dist/compliance/tests/basic_arithmetic/basic_arithmetic.gs.js +0 -16
- package/dist/compliance/tests/basic_arithmetic/basic_arithmetic.gs.js.map +0 -1
- package/dist/compliance/tests/boolean_logic/boolean_logic.gs.d.ts +0 -1
- package/dist/compliance/tests/boolean_logic/boolean_logic.gs.js +0 -14
- package/dist/compliance/tests/boolean_logic/boolean_logic.gs.js.map +0 -1
- package/dist/compliance/tests/channel_basic/channel_basic.gs.d.ts +0 -1
- package/dist/compliance/tests/channel_basic/channel_basic.gs.js +0 -14
- package/dist/compliance/tests/channel_basic/channel_basic.gs.js.map +0 -1
- package/dist/compliance/tests/composite_literal_assignment/composite_literal_assignment.gs.d.ts +0 -1
- package/dist/compliance/tests/composite_literal_assignment/composite_literal_assignment.gs.js +0 -27
- package/dist/compliance/tests/composite_literal_assignment/composite_literal_assignment.gs.js.map +0 -1
- package/dist/compliance/tests/constants/constants.gs.d.ts +0 -1
- package/dist/compliance/tests/constants/constants.gs.js +0 -16
- package/dist/compliance/tests/constants/constants.gs.js.map +0 -1
- package/dist/compliance/tests/copy_independence/copy_independence.gs.d.ts +0 -1
- package/dist/compliance/tests/copy_independence/copy_independence.gs.js +0 -33
- package/dist/compliance/tests/copy_independence/copy_independence.gs.js.map +0 -1
- package/dist/compliance/tests/defer_statement/defer_statement.gs.d.ts +0 -1
- package/dist/compliance/tests/defer_statement/defer_statement.gs.js +0 -75
- package/dist/compliance/tests/defer_statement/defer_statement.gs.js.map +0 -1
- package/dist/compliance/tests/embedded_interface_assertion/embedded_interface_assertion.gs.d.ts +0 -1
- package/dist/compliance/tests/embedded_interface_assertion/embedded_interface_assertion.gs.js +0 -37
- package/dist/compliance/tests/embedded_interface_assertion/embedded_interface_assertion.gs.js.map +0 -1
- package/dist/compliance/tests/flag_bitwise_op/flag_bitwise_op.gs.d.ts +0 -1
- package/dist/compliance/tests/flag_bitwise_op/flag_bitwise_op.gs.js +0 -29
- package/dist/compliance/tests/flag_bitwise_op/flag_bitwise_op.gs.js.map +0 -1
- package/dist/compliance/tests/float64/float64.gs.d.ts +0 -1
- package/dist/compliance/tests/float64/float64.gs.js +0 -24
- package/dist/compliance/tests/float64/float64.gs.js.map +0 -1
- package/dist/compliance/tests/for_loop_basic/for_loop_basic.gs.d.ts +0 -1
- package/dist/compliance/tests/for_loop_basic/for_loop_basic.gs.js +0 -10
- package/dist/compliance/tests/for_loop_basic/for_loop_basic.gs.js.map +0 -1
- package/dist/compliance/tests/for_loop_condition_only/for_loop_condition_only.gs.d.ts +0 -1
- package/dist/compliance/tests/for_loop_condition_only/for_loop_condition_only.gs.js +0 -11
- package/dist/compliance/tests/for_loop_condition_only/for_loop_condition_only.gs.js.map +0 -1
- package/dist/compliance/tests/for_loop_condition_only/main.gs.d.ts +0 -1
- package/dist/compliance/tests/for_loop_condition_only/main.gs.js +0 -10
- package/dist/compliance/tests/for_loop_condition_only/main.gs.js.map +0 -1
- package/dist/compliance/tests/for_loop_infinite/for_loop_infinite.gs.d.ts +0 -1
- package/dist/compliance/tests/for_loop_infinite/for_loop_infinite.gs.js +0 -14
- package/dist/compliance/tests/for_loop_infinite/for_loop_infinite.gs.js.map +0 -1
- package/dist/compliance/tests/for_range/for_range.gs.d.ts +0 -1
- package/dist/compliance/tests/for_range/for_range.gs.js +0 -39
- package/dist/compliance/tests/for_range/for_range.gs.js.map +0 -1
- package/dist/compliance/tests/for_range_index_use/for_range_index_use.gs.d.ts +0 -1
- package/dist/compliance/tests/for_range_index_use/for_range_index_use.gs.js +0 -15
- package/dist/compliance/tests/for_range_index_use/for_range_index_use.gs.js.map +0 -1
- package/dist/compliance/tests/func_literal/func_literal.gs.d.ts +0 -1
- package/dist/compliance/tests/func_literal/func_literal.gs.js +0 -10
- package/dist/compliance/tests/func_literal/func_literal.gs.js.map +0 -1
- package/dist/compliance/tests/function_call_result_assignment/function_call_result_assignment.gs.d.ts +0 -12
- package/dist/compliance/tests/function_call_result_assignment/function_call_result_assignment.gs.js +0 -30
- package/dist/compliance/tests/function_call_result_assignment/function_call_result_assignment.gs.js.map +0 -1
- package/dist/compliance/tests/if_statement/if_statement.gs.d.ts +0 -1
- package/dist/compliance/tests/if_statement/if_statement.gs.js +0 -13
- package/dist/compliance/tests/if_statement/if_statement.gs.js.map +0 -1
- package/dist/compliance/tests/interface_method_comments/interface_method_comments.gs.d.ts +0 -1
- package/dist/compliance/tests/interface_method_comments/interface_method_comments.gs.js +0 -12
- package/dist/compliance/tests/interface_method_comments/interface_method_comments.gs.js.map +0 -1
- package/dist/compliance/tests/interface_multi_param_return/interface_multi_param_return.gs.d.ts +0 -1
- package/dist/compliance/tests/interface_multi_param_return/interface_multi_param_return.gs.js +0 -34
- package/dist/compliance/tests/interface_multi_param_return/interface_multi_param_return.gs.js.map +0 -1
- package/dist/compliance/tests/interface_to_interface_type_assertion/interface_to_interface_type_assertion.gs.d.ts +0 -1
- package/dist/compliance/tests/interface_to_interface_type_assertion/interface_to_interface_type_assertion.gs.js +0 -32
- package/dist/compliance/tests/interface_to_interface_type_assertion/interface_to_interface_type_assertion.gs.js.map +0 -1
- package/dist/compliance/tests/interface_type_assertion/interface_type_assertion.gs.d.ts +0 -1
- package/dist/compliance/tests/interface_type_assertion/interface_type_assertion.gs.js +0 -40
- package/dist/compliance/tests/interface_type_assertion/interface_type_assertion.gs.js.map +0 -1
- package/dist/compliance/tests/interface_type_assertion_signature_mismatch/interface_type_assertion_signature_mismatch.gs.d.ts +0 -1
- package/dist/compliance/tests/interface_type_assertion_signature_mismatch/interface_type_assertion_signature_mismatch.gs.js +0 -51
- package/dist/compliance/tests/interface_type_assertion_signature_mismatch/interface_type_assertion_signature_mismatch.gs.js.map +0 -1
- package/dist/compliance/tests/map_support/map_support.gs.d.ts +0 -1
- package/dist/compliance/tests/map_support/map_support.gs.js +0 -88
- package/dist/compliance/tests/map_support/map_support.gs.js.map +0 -1
- package/dist/compliance/tests/method_call_on_pointer_receiver/method_call_on_pointer_receiver.gs.d.ts +0 -1
- package/dist/compliance/tests/method_call_on_pointer_receiver/method_call_on_pointer_receiver.gs.js +0 -24
- package/dist/compliance/tests/method_call_on_pointer_receiver/method_call_on_pointer_receiver.gs.js.map +0 -1
- package/dist/compliance/tests/method_call_on_pointer_via_value/method_call_on_pointer_via_value.gs.d.ts +0 -1
- package/dist/compliance/tests/method_call_on_pointer_via_value/method_call_on_pointer_via_value.gs.js +0 -33
- package/dist/compliance/tests/method_call_on_pointer_via_value/method_call_on_pointer_via_value.gs.js.map +0 -1
- package/dist/compliance/tests/method_call_on_value_receiver/method_call_on_value_receiver.gs.d.ts +0 -1
- package/dist/compliance/tests/method_call_on_value_receiver/method_call_on_value_receiver.gs.js +0 -22
- package/dist/compliance/tests/method_call_on_value_receiver/method_call_on_value_receiver.gs.js.map +0 -1
- package/dist/compliance/tests/method_call_on_value_via_pointer/method_call_on_value_via_pointer.gs.d.ts +0 -1
- package/dist/compliance/tests/method_call_on_value_via_pointer/method_call_on_value_via_pointer.gs.js +0 -33
- package/dist/compliance/tests/method_call_on_value_via_pointer/method_call_on_value_via_pointer.gs.js.map +0 -1
- package/dist/compliance/tests/multiple_return_values/multiple_return_values.gs.d.ts +0 -1
- package/dist/compliance/tests/multiple_return_values/multiple_return_values.gs.js +0 -17
- package/dist/compliance/tests/multiple_return_values/multiple_return_values.gs.js.map +0 -1
- package/dist/compliance/tests/pointer_assignment_no_copy/pointer_assignment_no_copy.gs.d.ts +0 -1
- package/dist/compliance/tests/pointer_assignment_no_copy/pointer_assignment_no_copy.gs.js +0 -29
- package/dist/compliance/tests/pointer_assignment_no_copy/pointer_assignment_no_copy.gs.js.map +0 -1
- package/dist/compliance/tests/pointer_composite_literal_assignment/pointer_composite_literal_assignment.gs.d.ts +0 -1
- package/dist/compliance/tests/pointer_composite_literal_assignment/pointer_composite_literal_assignment.gs.js +0 -27
- package/dist/compliance/tests/pointer_composite_literal_assignment/pointer_composite_literal_assignment.gs.js.map +0 -1
- package/dist/compliance/tests/pointer_deref_multiassign/pointer_deref_multiassign.gs.d.ts +0 -1
- package/dist/compliance/tests/pointer_deref_multiassign/pointer_deref_multiassign.gs.js +0 -22
- package/dist/compliance/tests/pointer_deref_multiassign/pointer_deref_multiassign.gs.js.map +0 -1
- package/dist/compliance/tests/pointer_initialization/pointer_initialization.gs.d.ts +0 -1
- package/dist/compliance/tests/pointer_initialization/pointer_initialization.gs.js +0 -20
- package/dist/compliance/tests/pointer_initialization/pointer_initialization.gs.js.map +0 -1
- package/dist/compliance/tests/select_receive_on_closed_channel_no_default/select_receive_on_closed_channel_no_default.gs.d.ts +0 -1
- package/dist/compliance/tests/select_receive_on_closed_channel_no_default/select_receive_on_closed_channel_no_default.gs.js +0 -28
- package/dist/compliance/tests/select_receive_on_closed_channel_no_default/select_receive_on_closed_channel_no_default.gs.js.map +0 -1
- package/dist/compliance/tests/select_send_on_full_buffered_channel_with_default/select_send_on_full_buffered_channel_with_default.gs.d.ts +0 -1
- package/dist/compliance/tests/select_send_on_full_buffered_channel_with_default/select_send_on_full_buffered_channel_with_default.gs.js +0 -30
- package/dist/compliance/tests/select_send_on_full_buffered_channel_with_default/select_send_on_full_buffered_channel_with_default.gs.js.map +0 -1
- package/dist/compliance/tests/select_statement/select_statement.gs.d.ts +0 -1
- package/dist/compliance/tests/select_statement/select_statement.gs.js +0 -207
- package/dist/compliance/tests/select_statement/select_statement.gs.js.map +0 -1
- package/dist/compliance/tests/simple/simple.gs.d.ts +0 -1
- package/dist/compliance/tests/simple/simple.gs.js +0 -6
- package/dist/compliance/tests/simple/simple.gs.js.map +0 -1
- package/dist/compliance/tests/simple_deref_assignment/simple_deref_assignment.gs.d.ts +0 -1
- package/dist/compliance/tests/simple_deref_assignment/simple_deref_assignment.gs.js +0 -24
- package/dist/compliance/tests/simple_deref_assignment/simple_deref_assignment.gs.js.map +0 -1
- package/dist/compliance/tests/slices/slices.gs.d.ts +0 -1
- package/dist/compliance/tests/slices/slices.gs.js +0 -294
- package/dist/compliance/tests/slices/slices.gs.js.map +0 -1
- package/dist/compliance/tests/string_conversion/string_conversion.gs.d.ts +0 -1
- package/dist/compliance/tests/string_conversion/string_conversion.gs.js +0 -41
- package/dist/compliance/tests/string_conversion/string_conversion.gs.js.map +0 -1
- package/dist/compliance/tests/string_rune_conversion/string_rune_conversion.gs.d.ts +0 -1
- package/dist/compliance/tests/string_rune_conversion/string_rune_conversion.gs.js +0 -17
- package/dist/compliance/tests/string_rune_conversion/string_rune_conversion.gs.js.map +0 -1
- package/dist/compliance/tests/struct_embedding/struct_embedding.gs.d.ts +0 -1
- package/dist/compliance/tests/struct_embedding/struct_embedding.gs.js +0 -48
- package/dist/compliance/tests/struct_embedding/struct_embedding.gs.js.map +0 -1
- package/dist/compliance/tests/struct_field_access/struct_field_access.gs.d.ts +0 -1
- package/dist/compliance/tests/struct_field_access/struct_field_access.gs.js +0 -19
- package/dist/compliance/tests/struct_field_access/struct_field_access.gs.js.map +0 -1
- package/dist/compliance/tests/struct_pointer_interface_fields/struct_pointer_interface_fields.gs.d.ts +0 -1
- package/dist/compliance/tests/struct_pointer_interface_fields/struct_pointer_interface_fields.gs.js +0 -26
- package/dist/compliance/tests/struct_pointer_interface_fields/struct_pointer_interface_fields.gs.js.map +0 -1
- package/dist/compliance/tests/struct_value_init_clone/struct_value_init_clone.gs.d.ts +0 -1
- package/dist/compliance/tests/struct_value_init_clone/struct_value_init_clone.gs.js +0 -30
- package/dist/compliance/tests/struct_value_init_clone/struct_value_init_clone.gs.js.map +0 -1
- package/dist/compliance/tests/switch_statement/switch_statement.gs.d.ts +0 -1
- package/dist/compliance/tests/switch_statement/switch_statement.gs.js +0 -76
- package/dist/compliance/tests/switch_statement/switch_statement.gs.js.map +0 -1
- package/dist/compliance/tests/value_type_copy_behavior/value_type_copy_behavior.gs.d.ts +0 -1
- package/dist/compliance/tests/value_type_copy_behavior/value_type_copy_behavior.gs.js +0 -31
- package/dist/compliance/tests/value_type_copy_behavior/value_type_copy_behavior.gs.js.map +0 -1
package/builtin/builtin.ts
CHANGED
|
@@ -1,44 +1,235 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* @param len The length of the slice.
|
|
4
|
-
* @param cap The capacity of the slice (optional).
|
|
5
|
-
* @returns A new TypeScript array representing the slice.
|
|
2
|
+
* GoSliceObject contains metadata for complex slice views
|
|
6
3
|
*/
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
slice.__capacity = cap !== undefined ? cap : len
|
|
13
|
-
return slice
|
|
4
|
+
interface GoSliceObject<T> {
|
|
5
|
+
backing: T[] // The backing array
|
|
6
|
+
offset: number // Offset into the backing array
|
|
7
|
+
length: number // Length of the slice
|
|
8
|
+
capacity: number // Capacity of the slice
|
|
14
9
|
}
|
|
15
10
|
|
|
16
11
|
/**
|
|
17
|
-
*
|
|
12
|
+
* SliceProxy is a proxy object for complex slices
|
|
13
|
+
*/
|
|
14
|
+
export type SliceProxy<T> = T[] & {
|
|
15
|
+
__meta__: GoSliceObject<T>
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Slice<T> is a union type that is either a plain array or a proxy
|
|
20
|
+
* null represents the nil state.
|
|
21
|
+
*/
|
|
22
|
+
export type Slice<T> = T[] | SliceProxy<T> | null
|
|
23
|
+
|
|
24
|
+
export function asArray<T>(slice: Slice<T>): T[] {
|
|
25
|
+
return slice as T[]
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* isComplexSlice checks if a slice is a complex slice (has __meta__ property)
|
|
30
|
+
*/
|
|
31
|
+
function isComplexSlice<T>(slice: Slice<T>): slice is SliceProxy<T> {
|
|
32
|
+
return (
|
|
33
|
+
slice !== null &&
|
|
34
|
+
slice !== undefined &&
|
|
35
|
+
'__meta__' in slice &&
|
|
36
|
+
slice.__meta__ !== undefined
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Creates a new slice with the specified length and capacity.
|
|
42
|
+
* @param length The length of the slice.
|
|
43
|
+
* @param capacity The capacity of the slice (optional).
|
|
44
|
+
* @returns A new slice.
|
|
45
|
+
*/
|
|
46
|
+
export const makeSlice = <T>(length: number, capacity?: number): Slice<T> => {
|
|
47
|
+
if (capacity === undefined) {
|
|
48
|
+
capacity = length
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (length < 0 || capacity < 0 || length > capacity) {
|
|
52
|
+
throw new Error(
|
|
53
|
+
`Invalid slice length (${length}) or capacity (${capacity})`,
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const arr = new Array<T>(length)
|
|
58
|
+
|
|
59
|
+
// Always create a complex slice with metadata to preserve capacity information
|
|
60
|
+
const proxy = arr as SliceProxy<T>
|
|
61
|
+
proxy.__meta__ = {
|
|
62
|
+
backing: new Array<T>(capacity),
|
|
63
|
+
offset: 0,
|
|
64
|
+
length: length,
|
|
65
|
+
capacity: capacity,
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
for (let i = 0; i < length; i++) {
|
|
69
|
+
proxy.__meta__.backing[i] = arr[i]
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return proxy
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* goSlice creates a slice from s[low:high:max]
|
|
18
77
|
* Arguments mirror Go semantics; omitted indices are undefined.
|
|
19
78
|
*
|
|
20
|
-
* @param
|
|
21
|
-
* @param low
|
|
22
|
-
* @param high Ending index (defaults to
|
|
23
|
-
* @param max
|
|
79
|
+
* @param s The original slice
|
|
80
|
+
* @param low Starting index (defaults to 0)
|
|
81
|
+
* @param high Ending index (defaults to s.length)
|
|
82
|
+
* @param max Capacity limit (defaults to original capacity)
|
|
24
83
|
*/
|
|
25
|
-
export const
|
|
26
|
-
|
|
84
|
+
export const goSlice = <T>(
|
|
85
|
+
s: Slice<T>,
|
|
27
86
|
low?: number,
|
|
28
87
|
high?: number,
|
|
29
88
|
max?: number,
|
|
30
|
-
):
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const
|
|
89
|
+
): Slice<T> => {
|
|
90
|
+
if (s === null || s === undefined) {
|
|
91
|
+
throw new Error('Cannot slice nil')
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const slen = len(s)
|
|
95
|
+
low = low ?? 0
|
|
96
|
+
high = high ?? slen
|
|
97
|
+
|
|
98
|
+
if (low < 0 || high < low) {
|
|
99
|
+
throw new Error(`Invalid slice indices: ${low}:${high}`)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// In Go, high can be up to capacity, not just length
|
|
103
|
+
const scap = cap(s)
|
|
104
|
+
if (high > scap) {
|
|
105
|
+
throw new Error(`Slice index out of range: ${high} > ${scap}`)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (
|
|
109
|
+
Array.isArray(s) &&
|
|
110
|
+
!isComplexSlice(s) &&
|
|
111
|
+
low === 0 &&
|
|
112
|
+
high === s.length &&
|
|
113
|
+
max === undefined
|
|
114
|
+
) {
|
|
115
|
+
return s
|
|
116
|
+
}
|
|
36
117
|
|
|
37
|
-
|
|
38
|
-
|
|
118
|
+
let backing: T[]
|
|
119
|
+
let oldOffset = 0
|
|
120
|
+
let oldCap = scap
|
|
121
|
+
|
|
122
|
+
// Get the backing array and offset
|
|
123
|
+
if (isComplexSlice(s)) {
|
|
124
|
+
backing = s.__meta__.backing
|
|
125
|
+
oldOffset = s.__meta__.offset
|
|
126
|
+
oldCap = s.__meta__.capacity
|
|
127
|
+
} else {
|
|
128
|
+
backing = s as T[]
|
|
39
129
|
}
|
|
40
|
-
|
|
41
|
-
|
|
130
|
+
|
|
131
|
+
let newCap
|
|
132
|
+
if (max !== undefined) {
|
|
133
|
+
if (max < high) {
|
|
134
|
+
throw new Error(`Invalid slice indices: ${low}:${high}:${max}`)
|
|
135
|
+
}
|
|
136
|
+
if (isComplexSlice(s) && max > oldOffset + oldCap) {
|
|
137
|
+
throw new Error(
|
|
138
|
+
`Slice index out of range: ${max} > ${oldOffset + oldCap}`,
|
|
139
|
+
)
|
|
140
|
+
}
|
|
141
|
+
if (!isComplexSlice(s) && max > s.length) {
|
|
142
|
+
throw new Error(`Slice index out of range: ${max} > ${s.length}`)
|
|
143
|
+
}
|
|
144
|
+
newCap = max - low
|
|
145
|
+
} else {
|
|
146
|
+
// For slices of slices, capacity should be the capacity of the original slice minus the low index
|
|
147
|
+
if (isComplexSlice(s)) {
|
|
148
|
+
newCap = oldCap - low
|
|
149
|
+
} else {
|
|
150
|
+
newCap = s.length - low
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const newLength = high - low
|
|
155
|
+
const newOffset = oldOffset + low
|
|
156
|
+
|
|
157
|
+
const target = {
|
|
158
|
+
__meta__: {
|
|
159
|
+
backing: backing,
|
|
160
|
+
offset: newOffset,
|
|
161
|
+
length: newLength,
|
|
162
|
+
capacity: newCap,
|
|
163
|
+
},
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const handler = {
|
|
167
|
+
get(target: any, prop: string | symbol): any {
|
|
168
|
+
if (typeof prop === 'string' && /^\d+$/.test(prop)) {
|
|
169
|
+
const index = Number(prop)
|
|
170
|
+
if (index >= 0 && index < target.__meta__.length) {
|
|
171
|
+
return target.__meta__.backing[target.__meta__.offset + index]
|
|
172
|
+
}
|
|
173
|
+
throw new Error(
|
|
174
|
+
`Slice index out of range: ${index} >= ${target.__meta__.length}`,
|
|
175
|
+
)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (prop === 'length') {
|
|
179
|
+
return target.__meta__.length
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (prop === '__meta__') {
|
|
183
|
+
return target.__meta__
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (
|
|
187
|
+
prop === 'slice' ||
|
|
188
|
+
prop === 'map' ||
|
|
189
|
+
prop === 'filter' ||
|
|
190
|
+
prop === 'reduce' ||
|
|
191
|
+
prop === 'forEach' ||
|
|
192
|
+
prop === Symbol.iterator
|
|
193
|
+
) {
|
|
194
|
+
const backingSlice = target.__meta__.backing.slice(
|
|
195
|
+
target.__meta__.offset,
|
|
196
|
+
target.__meta__.offset + target.__meta__.length,
|
|
197
|
+
)
|
|
198
|
+
return backingSlice[prop].bind(backingSlice)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return Reflect.get(target, prop)
|
|
202
|
+
},
|
|
203
|
+
|
|
204
|
+
set(target: any, prop: string | symbol, value: any): boolean {
|
|
205
|
+
if (typeof prop === 'string' && /^\d+$/.test(prop)) {
|
|
206
|
+
const index = Number(prop)
|
|
207
|
+
if (index >= 0 && index < target.__meta__.length) {
|
|
208
|
+
target.__meta__.backing[target.__meta__.offset + index] = value
|
|
209
|
+
return true
|
|
210
|
+
}
|
|
211
|
+
if (
|
|
212
|
+
index === target.__meta__.length &&
|
|
213
|
+
target.__meta__.length < target.__meta__.capacity
|
|
214
|
+
) {
|
|
215
|
+
target.__meta__.backing[target.__meta__.offset + index] = value
|
|
216
|
+
target.__meta__.length++
|
|
217
|
+
return true
|
|
218
|
+
}
|
|
219
|
+
throw new Error(
|
|
220
|
+
`Slice index out of range: ${index} >= ${target.__meta__.length}`,
|
|
221
|
+
)
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (prop === 'length' || prop === '__meta__') {
|
|
225
|
+
return false
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return Reflect.set(target, prop, value)
|
|
229
|
+
},
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return new Proxy(target, handler) as unknown as SliceProxy<T>
|
|
42
233
|
}
|
|
43
234
|
|
|
44
235
|
/**
|
|
@@ -50,28 +241,353 @@ export const makeMap = <K, V>(): Map<K, V> => {
|
|
|
50
241
|
}
|
|
51
242
|
|
|
52
243
|
/**
|
|
53
|
-
*
|
|
54
|
-
*
|
|
244
|
+
* Converts a JavaScript array to a Go slice.
|
|
245
|
+
* For multi-dimensional arrays, recursively converts nested arrays to slices.
|
|
246
|
+
* @param arr The JavaScript array to convert
|
|
247
|
+
* @param depth How many levels of nesting to convert (default: 1, use Infinity for all levels)
|
|
248
|
+
* @returns A Go slice containing the same elements
|
|
249
|
+
*/
|
|
250
|
+
export const arrayToSlice = <T>(
|
|
251
|
+
arr: T[] | null | undefined,
|
|
252
|
+
depth: number = 1,
|
|
253
|
+
): T[] => {
|
|
254
|
+
if (arr == null) return [] as T[]
|
|
255
|
+
|
|
256
|
+
if (arr.length === 0) return arr
|
|
257
|
+
|
|
258
|
+
const target = {
|
|
259
|
+
__meta__: {
|
|
260
|
+
backing: arr,
|
|
261
|
+
offset: 0,
|
|
262
|
+
length: arr.length,
|
|
263
|
+
capacity: arr.length,
|
|
264
|
+
},
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const handler = {
|
|
268
|
+
get(target: any, prop: string | symbol): any {
|
|
269
|
+
if (typeof prop === 'string' && /^\d+$/.test(prop)) {
|
|
270
|
+
const index = Number(prop)
|
|
271
|
+
if (index >= 0 && index < target.__meta__.length) {
|
|
272
|
+
return target.__meta__.backing[target.__meta__.offset + index]
|
|
273
|
+
}
|
|
274
|
+
throw new Error(
|
|
275
|
+
`Slice index out of range: ${index} >= ${target.__meta__.length}`,
|
|
276
|
+
)
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (prop === 'length') {
|
|
280
|
+
return target.__meta__.length
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (prop === '__meta__') {
|
|
284
|
+
return target.__meta__
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (
|
|
288
|
+
prop === 'slice' ||
|
|
289
|
+
prop === 'map' ||
|
|
290
|
+
prop === 'filter' ||
|
|
291
|
+
prop === 'reduce' ||
|
|
292
|
+
prop === 'forEach' ||
|
|
293
|
+
prop === Symbol.iterator
|
|
294
|
+
) {
|
|
295
|
+
const backingSlice = target.__meta__.backing.slice(
|
|
296
|
+
target.__meta__.offset,
|
|
297
|
+
target.__meta__.offset + target.__meta__.length,
|
|
298
|
+
)
|
|
299
|
+
return backingSlice[prop].bind(backingSlice)
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
return Reflect.get(target, prop)
|
|
303
|
+
},
|
|
304
|
+
|
|
305
|
+
set(target: any, prop: string | symbol, value: any): boolean {
|
|
306
|
+
if (typeof prop === 'string' && /^\d+$/.test(prop)) {
|
|
307
|
+
const index = Number(prop)
|
|
308
|
+
if (index >= 0 && index < target.__meta__.length) {
|
|
309
|
+
target.__meta__.backing[target.__meta__.offset + index] = value
|
|
310
|
+
return true
|
|
311
|
+
}
|
|
312
|
+
if (
|
|
313
|
+
index === target.__meta__.length &&
|
|
314
|
+
target.__meta__.length < target.__meta__.capacity
|
|
315
|
+
) {
|
|
316
|
+
target.__meta__.backing[target.__meta__.offset + index] = value
|
|
317
|
+
target.__meta__.length++
|
|
318
|
+
return true
|
|
319
|
+
}
|
|
320
|
+
throw new Error(
|
|
321
|
+
`Slice index out of range: ${index} >= ${target.__meta__.length}`,
|
|
322
|
+
)
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (prop === 'length' || prop === '__meta__') {
|
|
326
|
+
return false
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return Reflect.set(target, prop, value)
|
|
330
|
+
},
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Recursively convert nested arrays if depth > 1
|
|
334
|
+
if (depth > 1 && arr.length > 0) {
|
|
335
|
+
for (let i = 0; i < arr.length; i++) {
|
|
336
|
+
const item = arr[i]
|
|
337
|
+
if (isComplexSlice(item as any)) {
|
|
338
|
+
} else if (Array.isArray(item)) {
|
|
339
|
+
arr[i] = arrayToSlice(item as any[], depth - 1) as any
|
|
340
|
+
} else if (
|
|
341
|
+
item &&
|
|
342
|
+
typeof item === 'object' &&
|
|
343
|
+
isComplexSlice(item as any)
|
|
344
|
+
) {
|
|
345
|
+
// Preserve capacity information for complex slices
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
return new Proxy(target, handler) as unknown as SliceProxy<T>
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Returns the length of a collection (string, array, slice, map, or set).
|
|
355
|
+
* @param obj The collection to get the length of.
|
|
55
356
|
* @returns The length of the collection.
|
|
56
357
|
*/
|
|
57
|
-
export const len = <T>(
|
|
58
|
-
|
|
358
|
+
export const len = <T = unknown, V = unknown>(
|
|
359
|
+
obj: string | Array<T> | Slice<T> | Map<T, V> | Set<T> | null | undefined,
|
|
59
360
|
): number => {
|
|
60
|
-
if (
|
|
61
|
-
return
|
|
62
|
-
}
|
|
63
|
-
|
|
361
|
+
if (obj === null || obj === undefined) {
|
|
362
|
+
return 0
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if (typeof obj === 'string') {
|
|
366
|
+
return obj.length
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
if (obj instanceof Map || obj instanceof Set) {
|
|
370
|
+
return obj.size
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
if (isComplexSlice(obj)) {
|
|
374
|
+
return obj.__meta__.length
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
if (Array.isArray(obj)) {
|
|
378
|
+
return obj.length
|
|
64
379
|
}
|
|
380
|
+
|
|
65
381
|
return 0 // Default fallback
|
|
66
382
|
}
|
|
67
383
|
|
|
68
384
|
/**
|
|
69
|
-
* Returns the capacity of a slice
|
|
70
|
-
* @param
|
|
385
|
+
* Returns the capacity of a slice.
|
|
386
|
+
* @param obj The slice.
|
|
71
387
|
* @returns The capacity of the slice.
|
|
72
388
|
*/
|
|
73
|
-
export const cap = <T>(
|
|
74
|
-
|
|
389
|
+
export const cap = <T>(obj: Slice<T>): number => {
|
|
390
|
+
if (obj === null || obj === undefined) {
|
|
391
|
+
return 0
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
if (isComplexSlice(obj)) {
|
|
395
|
+
return obj.__meta__.capacity
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
if (Array.isArray(obj)) {
|
|
399
|
+
return obj.length
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
return 0
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Appends elements to a slice.
|
|
407
|
+
* Note: In Go, append can return a new slice if the underlying array is reallocated.
|
|
408
|
+
* This helper emulates that by returning the modified or new slice.
|
|
409
|
+
* @param slice The slice to append to.
|
|
410
|
+
* @param elements The elements to append.
|
|
411
|
+
* @returns The modified or new slice.
|
|
412
|
+
*/
|
|
413
|
+
export const append = <T>(slice: Slice<T>, ...elements: T[]): T[] => {
|
|
414
|
+
if (slice === null || slice === undefined) {
|
|
415
|
+
if (elements.length === 0) {
|
|
416
|
+
return [] as T[]
|
|
417
|
+
} else {
|
|
418
|
+
return elements.slice(0) as T[]
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
if (elements.length === 0) {
|
|
423
|
+
return slice
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
const oldLen = len(slice)
|
|
427
|
+
const oldCap = cap(slice)
|
|
428
|
+
const newLen = oldLen + elements.length
|
|
429
|
+
|
|
430
|
+
if (newLen <= oldCap) {
|
|
431
|
+
if (isComplexSlice(slice)) {
|
|
432
|
+
const offset = slice.__meta__.offset
|
|
433
|
+
const backing = slice.__meta__.backing
|
|
434
|
+
|
|
435
|
+
for (let i = 0; i < elements.length; i++) {
|
|
436
|
+
backing[offset + oldLen + i] = elements[i]
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
const result = new Array(newLen) as SliceProxy<T>
|
|
440
|
+
|
|
441
|
+
for (let i = 0; i < oldLen; i++) {
|
|
442
|
+
result[i] = backing[offset + i]
|
|
443
|
+
}
|
|
444
|
+
for (let i = 0; i < elements.length; i++) {
|
|
445
|
+
result[oldLen + i] = elements[i]
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
result.__meta__ = {
|
|
449
|
+
backing: backing,
|
|
450
|
+
offset: offset,
|
|
451
|
+
length: newLen,
|
|
452
|
+
capacity: oldCap,
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
return result
|
|
456
|
+
} else {
|
|
457
|
+
const result = new Array(newLen) as SliceProxy<T>
|
|
458
|
+
|
|
459
|
+
for (let i = 0; i < oldLen; i++) {
|
|
460
|
+
result[i] = slice[i]
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
for (let i = 0; i < elements.length; i++) {
|
|
464
|
+
result[i + oldLen] = elements[i]
|
|
465
|
+
|
|
466
|
+
if (i + oldLen < oldCap && Array.isArray(slice)) {
|
|
467
|
+
slice[i + oldLen] = elements[i]
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
result.__meta__ = {
|
|
472
|
+
backing: slice as any,
|
|
473
|
+
offset: 0,
|
|
474
|
+
length: newLen,
|
|
475
|
+
capacity: oldCap,
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
return result
|
|
479
|
+
}
|
|
480
|
+
} else {
|
|
481
|
+
let newCap = oldCap
|
|
482
|
+
if (newCap == 0) {
|
|
483
|
+
newCap = elements.length
|
|
484
|
+
} else {
|
|
485
|
+
if (newCap < 1024) {
|
|
486
|
+
newCap *= 2
|
|
487
|
+
} else {
|
|
488
|
+
newCap += newCap / 4
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// Ensure the new capacity fits all the elements
|
|
492
|
+
if (newCap < newLen) {
|
|
493
|
+
newCap = newLen
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
const newBacking = new Array<T>(newCap)
|
|
498
|
+
|
|
499
|
+
if (isComplexSlice(slice)) {
|
|
500
|
+
const offset = slice.__meta__.offset
|
|
501
|
+
const backing = slice.__meta__.backing
|
|
502
|
+
|
|
503
|
+
for (let i = 0; i < oldLen; i++) {
|
|
504
|
+
newBacking[i] = backing[offset + i]
|
|
505
|
+
}
|
|
506
|
+
} else {
|
|
507
|
+
for (let i = 0; i < oldLen; i++) {
|
|
508
|
+
newBacking[i] = slice[i]
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
for (let i = 0; i < elements.length; i++) {
|
|
513
|
+
newBacking[oldLen + i] = elements[i]
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
if (newLen === newCap) {
|
|
517
|
+
return newBacking.slice(0, newLen) as T[]
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
const result = new Array(newLen) as SliceProxy<T>
|
|
521
|
+
|
|
522
|
+
for (let i = 0; i < newLen; i++) {
|
|
523
|
+
result[i] = newBacking[i]
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
result.__meta__ = {
|
|
527
|
+
backing: newBacking,
|
|
528
|
+
offset: 0,
|
|
529
|
+
length: newLen,
|
|
530
|
+
capacity: newCap,
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
return result
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* Copies elements from src to dst.
|
|
539
|
+
* @param dst The destination slice.
|
|
540
|
+
* @param src The source slice.
|
|
541
|
+
* @returns The number of elements copied.
|
|
542
|
+
*/
|
|
543
|
+
export const copy = <T>(dst: Slice<T>, src: Slice<T>): number => {
|
|
544
|
+
if (dst === null || src === null) {
|
|
545
|
+
return 0
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
const dstLen = len(dst)
|
|
549
|
+
const srcLen = len(src)
|
|
550
|
+
|
|
551
|
+
const count = Math.min(dstLen, srcLen)
|
|
552
|
+
|
|
553
|
+
if (count === 0) {
|
|
554
|
+
return 0
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
if (isComplexSlice(dst)) {
|
|
558
|
+
const dstOffset = dst.__meta__.offset
|
|
559
|
+
const dstBacking = dst.__meta__.backing
|
|
560
|
+
|
|
561
|
+
if (isComplexSlice(src)) {
|
|
562
|
+
const srcOffset = src.__meta__.offset
|
|
563
|
+
const srcBacking = src.__meta__.backing
|
|
564
|
+
|
|
565
|
+
for (let i = 0; i < count; i++) {
|
|
566
|
+
dstBacking[dstOffset + i] = srcBacking[srcOffset + i]
|
|
567
|
+
dst[i] = srcBacking[srcOffset + i] // Update the proxy array
|
|
568
|
+
}
|
|
569
|
+
} else {
|
|
570
|
+
for (let i = 0; i < count; i++) {
|
|
571
|
+
dstBacking[dstOffset + i] = src[i]
|
|
572
|
+
dst[i] = src[i] // Update the proxy array
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
} else {
|
|
576
|
+
if (isComplexSlice(src)) {
|
|
577
|
+
const srcOffset = src.__meta__.offset
|
|
578
|
+
const srcBacking = src.__meta__.backing
|
|
579
|
+
|
|
580
|
+
for (let i = 0; i < count; i++) {
|
|
581
|
+
dst[i] = srcBacking[srcOffset + i]
|
|
582
|
+
}
|
|
583
|
+
} else {
|
|
584
|
+
for (let i = 0; i < count; i++) {
|
|
585
|
+
dst[i] = src[i]
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
return count
|
|
75
591
|
}
|
|
76
592
|
|
|
77
593
|
/**
|
|
@@ -95,9 +611,44 @@ export const stringToRunes = (str: string): number[] => {
|
|
|
95
611
|
* @param runes The input array of numbers representing Unicode code points.
|
|
96
612
|
* @returns The resulting string.
|
|
97
613
|
*/
|
|
98
|
-
export const runesToString = (runes: number
|
|
99
|
-
return String.fromCharCode(...runes)
|
|
100
|
-
}
|
|
614
|
+
export const runesToString = (runes: Slice<number>): string => {
|
|
615
|
+
return runes?.length ? String.fromCharCode(...runes) : ''
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
/**
|
|
619
|
+
* Converts a number to a byte (uint8) by truncating to the range 0-255.
|
|
620
|
+
* Equivalent to Go's byte() conversion.
|
|
621
|
+
* @param n The number to convert to a byte.
|
|
622
|
+
* @returns The byte value (0-255).
|
|
623
|
+
*/
|
|
624
|
+
export const byte = (n: number): number => {
|
|
625
|
+
return n & 0xff // Bitwise AND with 255 ensures we get a value in the range 0-255
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
/** Box represents a Go variable which can be referred to by other variables.
|
|
629
|
+
*
|
|
630
|
+
* For example:
|
|
631
|
+
* var myVariable int
|
|
632
|
+
*
|
|
633
|
+
*/
|
|
634
|
+
export type Box<T> = { value: T }
|
|
635
|
+
|
|
636
|
+
/** Wrap a non-null T in a pointer‐box. */
|
|
637
|
+
export function box<T>(v: T): Box<T> {
|
|
638
|
+
// We create a new object wrapper for every box call to ensure
|
|
639
|
+
// distinct pointer identity, crucial for pointer comparisons (p1 == p2).
|
|
640
|
+
return { value: v }
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
/** Dereference a pointer‐box, throws on null → simulates Go panic. */
|
|
644
|
+
export function unbox<T>(b: Box<T>): T {
|
|
645
|
+
if (b === null) {
|
|
646
|
+
throw new Error(
|
|
647
|
+
'runtime error: invalid memory address or nil pointer dereference',
|
|
648
|
+
)
|
|
649
|
+
}
|
|
650
|
+
return b.value
|
|
651
|
+
}
|
|
101
652
|
|
|
102
653
|
/**
|
|
103
654
|
* Gets a value from a map, with a default value if the key doesn't exist.
|
|
@@ -142,78 +693,213 @@ export const deleteMapEntry = <K, V>(map: Map<K, V>, key: K): void => {
|
|
|
142
693
|
export const mapHas = <K, V>(map: Map<K, V>, key: K): boolean => {
|
|
143
694
|
return map.has(key)
|
|
144
695
|
}
|
|
145
|
-
/**
|
|
146
|
-
* Appends elements to a slice (TypeScript array).
|
|
147
|
-
* Note: In Go, append can return a new slice if the underlying array is reallocated.
|
|
148
|
-
* This helper emulates that by returning the modified array.
|
|
149
|
-
* @param slice The slice (TypeScript array) to append to.
|
|
150
|
-
* @param elements The elements to append.
|
|
151
|
-
* @returns The modified slice (TypeScript array).
|
|
152
|
-
*/
|
|
153
|
-
export const append = <T>(
|
|
154
|
-
slice: Array<T> & { __capacity?: number },
|
|
155
|
-
...elements: T[]
|
|
156
|
-
): Array<T> & { __capacity?: number } => {
|
|
157
|
-
slice.push(...elements)
|
|
158
|
-
if (slice.__capacity !== undefined && slice.length > slice.__capacity) {
|
|
159
|
-
slice.__capacity = slice.length
|
|
160
|
-
}
|
|
161
|
-
return slice
|
|
162
|
-
}
|
|
163
696
|
|
|
164
697
|
/**
|
|
165
698
|
* Represents the kinds of Go types that can be registered at runtime.
|
|
166
699
|
*/
|
|
167
700
|
export enum TypeKind {
|
|
168
|
-
Struct = 'struct',
|
|
169
|
-
Interface = 'interface',
|
|
170
701
|
Basic = 'basic',
|
|
171
|
-
|
|
172
|
-
|
|
702
|
+
Interface = 'interface',
|
|
703
|
+
Struct = 'struct',
|
|
173
704
|
Map = 'map',
|
|
174
|
-
|
|
705
|
+
Slice = 'slice',
|
|
706
|
+
Array = 'array',
|
|
707
|
+
Pointer = 'pointer',
|
|
175
708
|
Function = 'function',
|
|
709
|
+
Channel = 'channel',
|
|
176
710
|
}
|
|
177
711
|
|
|
178
712
|
/**
|
|
179
|
-
*
|
|
713
|
+
* TypeInfo is used for runtime type checking.
|
|
714
|
+
* Can be a registered type (from typeRegistry) or an ad-hoc type description.
|
|
715
|
+
* When used as input to typeAssert, it can be a string (type name) or a structured description.
|
|
180
716
|
*/
|
|
181
|
-
|
|
182
|
-
|
|
717
|
+
|
|
718
|
+
/**
|
|
719
|
+
* Base type information shared by all type kinds
|
|
720
|
+
*/
|
|
721
|
+
export interface BaseTypeInfo {
|
|
722
|
+
name?: string
|
|
183
723
|
kind: TypeKind
|
|
184
|
-
zeroValue
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
724
|
+
zeroValue?: any
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
/**
|
|
728
|
+
* Type information for struct types
|
|
729
|
+
*/
|
|
730
|
+
export interface StructTypeInfo extends BaseTypeInfo {
|
|
731
|
+
kind: TypeKind.Struct
|
|
732
|
+
methods: Set<string>
|
|
733
|
+
ctor?: new (...args: any[]) => any
|
|
734
|
+
fields?: Set<string> // Field names for struct types
|
|
735
|
+
fieldTypes?: Record<string, TypeInfo | string> // Types for struct fields
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
/**
|
|
739
|
+
* Type information for interface types
|
|
740
|
+
*/
|
|
741
|
+
export interface InterfaceTypeInfo extends BaseTypeInfo {
|
|
742
|
+
kind: TypeKind.Interface
|
|
743
|
+
methods: Set<string>
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
/**
|
|
747
|
+
* Type information for basic types (string, number, boolean)
|
|
748
|
+
*/
|
|
749
|
+
export interface BasicTypeInfo extends BaseTypeInfo {
|
|
750
|
+
kind: TypeKind.Basic
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
/**
|
|
754
|
+
* Type information for map types
|
|
755
|
+
*/
|
|
756
|
+
export interface MapTypeInfo extends BaseTypeInfo {
|
|
757
|
+
kind: TypeKind.Map
|
|
758
|
+
keyType?: string | TypeInfo
|
|
759
|
+
elemType?: string | TypeInfo
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
/**
|
|
763
|
+
* Type information for slice types
|
|
764
|
+
*/
|
|
765
|
+
export interface SliceTypeInfo extends BaseTypeInfo {
|
|
766
|
+
kind: TypeKind.Slice
|
|
767
|
+
elemType?: string | TypeInfo
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
/**
|
|
771
|
+
* Type information for array types
|
|
772
|
+
*/
|
|
773
|
+
export interface ArrayTypeInfo extends BaseTypeInfo {
|
|
774
|
+
kind: TypeKind.Array
|
|
775
|
+
elemType?: string | TypeInfo
|
|
776
|
+
length: number
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
/**
|
|
780
|
+
* Type information for pointer types
|
|
781
|
+
*/
|
|
782
|
+
export interface PointerTypeInfo extends BaseTypeInfo {
|
|
783
|
+
kind: TypeKind.Pointer
|
|
784
|
+
elemType?: string | TypeInfo
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
/**
|
|
788
|
+
* Type information for function types
|
|
789
|
+
*/
|
|
790
|
+
export interface FunctionTypeInfo extends BaseTypeInfo {
|
|
791
|
+
kind: TypeKind.Function
|
|
792
|
+
params?: (string | TypeInfo)[]
|
|
793
|
+
results?: (string | TypeInfo)[]
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
/**
|
|
797
|
+
* Type information for channel types
|
|
798
|
+
*/
|
|
799
|
+
export interface ChannelTypeInfo extends BaseTypeInfo {
|
|
800
|
+
kind: TypeKind.Channel
|
|
801
|
+
elemType?: string | TypeInfo
|
|
802
|
+
direction?: 'send' | 'receive' | 'both'
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
/**
|
|
806
|
+
* Union type representing all possible TypeInfo variants
|
|
807
|
+
*/
|
|
808
|
+
export type TypeInfo =
|
|
809
|
+
| StructTypeInfo
|
|
810
|
+
| InterfaceTypeInfo
|
|
811
|
+
| BasicTypeInfo
|
|
812
|
+
| MapTypeInfo
|
|
813
|
+
| SliceTypeInfo
|
|
814
|
+
| ArrayTypeInfo
|
|
815
|
+
| PointerTypeInfo
|
|
816
|
+
| FunctionTypeInfo
|
|
817
|
+
| ChannelTypeInfo
|
|
818
|
+
|
|
819
|
+
// Type guard functions for TypeInfo variants
|
|
820
|
+
export function isStructTypeInfo(info: TypeInfo): info is StructTypeInfo {
|
|
821
|
+
return info.kind === TypeKind.Struct
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
export function isInterfaceTypeInfo(info: TypeInfo): info is InterfaceTypeInfo {
|
|
825
|
+
return info.kind === TypeKind.Interface
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
export function isBasicTypeInfo(info: TypeInfo): info is BasicTypeInfo {
|
|
829
|
+
return info.kind === TypeKind.Basic
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
export function isMapTypeInfo(info: TypeInfo): info is MapTypeInfo {
|
|
833
|
+
return info.kind === TypeKind.Map
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
export function isSliceTypeInfo(info: TypeInfo): info is SliceTypeInfo {
|
|
837
|
+
return info.kind === TypeKind.Slice
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
export function isArrayTypeInfo(info: TypeInfo): info is ArrayTypeInfo {
|
|
841
|
+
return info.kind === TypeKind.Array
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
export function isPointerTypeInfo(info: TypeInfo): info is PointerTypeInfo {
|
|
845
|
+
return info.kind === TypeKind.Pointer
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
export function isFunctionTypeInfo(info: TypeInfo): info is FunctionTypeInfo {
|
|
849
|
+
return info.kind === TypeKind.Function
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
export function isChannelTypeInfo(info: TypeInfo): info is ChannelTypeInfo {
|
|
853
|
+
return info.kind === TypeKind.Channel
|
|
189
854
|
}
|
|
190
855
|
|
|
191
856
|
// Registry to store runtime type information
|
|
192
857
|
const typeRegistry = new Map<string, TypeInfo>()
|
|
193
858
|
|
|
194
859
|
/**
|
|
195
|
-
* Registers a type with the runtime type system.
|
|
860
|
+
* Registers a struct type with the runtime type system.
|
|
196
861
|
*
|
|
197
862
|
* @param name The name of the type.
|
|
198
|
-
* @param kind The kind of the type.
|
|
199
863
|
* @param zeroValue The zero value for the type.
|
|
200
|
-
* @param methods
|
|
201
|
-
* @param
|
|
202
|
-
* @returns The type information object
|
|
864
|
+
* @param methods Set of method names for the struct.
|
|
865
|
+
* @param ctor Constructor for the struct.
|
|
866
|
+
* @returns The struct type information object.
|
|
203
867
|
*/
|
|
204
|
-
export const
|
|
868
|
+
export const registerStructType = (
|
|
205
869
|
name: string,
|
|
206
|
-
kind: TypeKind,
|
|
207
870
|
zeroValue: any,
|
|
208
|
-
methods
|
|
209
|
-
|
|
210
|
-
):
|
|
211
|
-
const typeInfo:
|
|
871
|
+
methods: Set<string>,
|
|
872
|
+
ctor: new (...args: any[]) => any,
|
|
873
|
+
): StructTypeInfo => {
|
|
874
|
+
const typeInfo: StructTypeInfo = {
|
|
212
875
|
name,
|
|
213
|
-
kind,
|
|
876
|
+
kind: TypeKind.Struct,
|
|
877
|
+
zeroValue,
|
|
878
|
+
methods,
|
|
879
|
+
ctor: ctor,
|
|
880
|
+
}
|
|
881
|
+
typeRegistry.set(name, typeInfo)
|
|
882
|
+
return typeInfo
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
/**
|
|
886
|
+
* Registers an interface type with the runtime type system.
|
|
887
|
+
*
|
|
888
|
+
* @param name The name of the type.
|
|
889
|
+
* @param zeroValue The zero value for the type (usually null).
|
|
890
|
+
* @param methods Set of method names the interface requires.
|
|
891
|
+
* @returns The interface type information object.
|
|
892
|
+
*/
|
|
893
|
+
export const registerInterfaceType = (
|
|
894
|
+
name: string,
|
|
895
|
+
zeroValue: any,
|
|
896
|
+
methods: Set<string>,
|
|
897
|
+
): InterfaceTypeInfo => {
|
|
898
|
+
const typeInfo: InterfaceTypeInfo = {
|
|
899
|
+
name,
|
|
900
|
+
kind: TypeKind.Interface,
|
|
214
901
|
zeroValue,
|
|
215
902
|
methods,
|
|
216
|
-
constructor,
|
|
217
903
|
}
|
|
218
904
|
typeRegistry.set(name, typeInfo)
|
|
219
905
|
return typeInfo
|
|
@@ -228,115 +914,410 @@ export interface TypeAssertResult<T> {
|
|
|
228
914
|
}
|
|
229
915
|
|
|
230
916
|
/**
|
|
231
|
-
*
|
|
917
|
+
* Normalizes a type info to a structured TypeInfo object.
|
|
232
918
|
*
|
|
233
|
-
* @param
|
|
234
|
-
* @
|
|
235
|
-
* @returns An object with the asserted value and whether the assertion succeeded.
|
|
919
|
+
* @param info The type info or name.
|
|
920
|
+
* @returns A normalized TypeInfo object.
|
|
236
921
|
*/
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
)
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
922
|
+
function normalizeTypeInfo(info: string | TypeInfo): TypeInfo {
|
|
923
|
+
if (typeof info === 'string') {
|
|
924
|
+
const typeInfo = typeRegistry.get(info)
|
|
925
|
+
if (typeInfo) {
|
|
926
|
+
return typeInfo
|
|
927
|
+
}
|
|
928
|
+
return {
|
|
929
|
+
kind: TypeKind.Basic,
|
|
930
|
+
name: info,
|
|
931
|
+
}
|
|
246
932
|
}
|
|
247
933
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
return { value: typeInfo.zeroValue as T, ok: false }
|
|
251
|
-
}
|
|
934
|
+
return info
|
|
935
|
+
}
|
|
252
936
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
937
|
+
/**
|
|
938
|
+
* Validates that a map key matches the expected type info.
|
|
939
|
+
*
|
|
940
|
+
* @param key The key to validate
|
|
941
|
+
* @param keyTypeInfo The normalized type info for the key
|
|
942
|
+
* @returns True if the key matches the type info, false otherwise
|
|
943
|
+
*/
|
|
944
|
+
function validateMapKey(key: any, keyTypeInfo: TypeInfo): boolean {
|
|
945
|
+
if (keyTypeInfo.kind === TypeKind.Basic) {
|
|
946
|
+
// For string keys
|
|
947
|
+
if (keyTypeInfo.name === 'string') {
|
|
948
|
+
return typeof key === 'string'
|
|
949
|
+
} else if (
|
|
950
|
+
keyTypeInfo.name === 'int' ||
|
|
951
|
+
keyTypeInfo.name === 'float64' ||
|
|
952
|
+
keyTypeInfo.name === 'number'
|
|
953
|
+
) {
|
|
954
|
+
if (typeof key === 'string') {
|
|
955
|
+
return /^-?\d+(\.\d+)?$/.test(key)
|
|
956
|
+
} else {
|
|
957
|
+
return typeof key === 'number'
|
|
259
958
|
}
|
|
260
|
-
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
return false
|
|
962
|
+
}
|
|
261
963
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
964
|
+
/**
|
|
965
|
+
* Checks if a value matches a basic type info.
|
|
966
|
+
*
|
|
967
|
+
* @param value The value to check.
|
|
968
|
+
* @param info The basic type info to match against.
|
|
969
|
+
* @returns True if the value matches the basic type, false otherwise.
|
|
970
|
+
*/
|
|
971
|
+
function matchesBasicType(value: any, info: TypeInfo): boolean {
|
|
972
|
+
if (info.name === 'string') return typeof value === 'string'
|
|
973
|
+
if (info.name === 'number' || info.name === 'int' || info.name === 'float64')
|
|
974
|
+
return typeof value === 'number'
|
|
975
|
+
if (info.name === 'boolean' || info.name === 'bool')
|
|
976
|
+
return typeof value === 'boolean'
|
|
977
|
+
return false
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
/**
|
|
981
|
+
* Checks if a value matches a struct type info.
|
|
982
|
+
*
|
|
983
|
+
* @param value The value to check.
|
|
984
|
+
* @param info The struct type info to match against.
|
|
985
|
+
* @returns True if the value matches the struct type, false otherwise.
|
|
986
|
+
*/
|
|
987
|
+
function matchesStructType(value: any, info: TypeInfo): boolean {
|
|
988
|
+
if (!isStructTypeInfo(info)) return false
|
|
989
|
+
|
|
990
|
+
// For structs, use instanceof with the constructor
|
|
991
|
+
if (info.ctor && value instanceof info.ctor) {
|
|
992
|
+
return true
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
if (info.fields && typeof value === 'object') {
|
|
996
|
+
// For struct type assertions, we need to check that the value has exactly
|
|
997
|
+
const descFields = Array.from(info.fields as Set<string>)
|
|
998
|
+
const valueFields = Object.keys(value)
|
|
999
|
+
|
|
1000
|
+
const condition1 = descFields.every((field) => field in value)
|
|
1001
|
+
const condition2 = valueFields.length === descFields.length
|
|
1002
|
+
const condition3 = valueFields.every((field) => descFields.includes(field))
|
|
1003
|
+
|
|
1004
|
+
console.log('Struct field matching debug:', {
|
|
1005
|
+
descFields,
|
|
1006
|
+
valueFields,
|
|
1007
|
+
allDescFieldsInValue: condition1,
|
|
1008
|
+
sameFieldCount: condition2,
|
|
1009
|
+
allValueFieldsInDesc: condition3,
|
|
1010
|
+
})
|
|
1011
|
+
|
|
1012
|
+
return condition1 && condition2 && condition3
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
return false
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
/**
|
|
1019
|
+
* Checks if a value matches an interface type info.
|
|
1020
|
+
*
|
|
1021
|
+
* @param value The value to check.
|
|
1022
|
+
* @param info The interface type info to match against.
|
|
1023
|
+
* @returns True if the value matches the interface type, false otherwise.
|
|
1024
|
+
*/
|
|
1025
|
+
function matchesInterfaceType(value: any, info: TypeInfo): boolean {
|
|
1026
|
+
// For interfaces, check if the value has all the required methods
|
|
1027
|
+
if (
|
|
1028
|
+
isInterfaceTypeInfo(info) &&
|
|
1029
|
+
info.methods &&
|
|
1030
|
+
typeof value === 'object' &&
|
|
1031
|
+
value !== null
|
|
1032
|
+
) {
|
|
1033
|
+
return Array.from(info.methods).every(
|
|
1034
|
+
(method) => typeof (value as any)[method] === 'function',
|
|
1035
|
+
)
|
|
1036
|
+
}
|
|
1037
|
+
return false
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
/**
|
|
1041
|
+
* Checks if a value matches a map type info.
|
|
1042
|
+
*
|
|
1043
|
+
* @param value The value to check.
|
|
1044
|
+
* @param info The map type info to match against.
|
|
1045
|
+
* @returns True if the value matches the map type, false otherwise.
|
|
1046
|
+
*/
|
|
1047
|
+
function matchesMapType(value: any, info: TypeInfo): boolean {
|
|
1048
|
+
if (typeof value !== 'object' || value === null) return false
|
|
1049
|
+
if (!isMapTypeInfo(info)) return false
|
|
1050
|
+
|
|
1051
|
+
if (info.keyType || info.elemType) {
|
|
1052
|
+
let entries: [any, any][] = []
|
|
1053
|
+
|
|
1054
|
+
if (value instanceof Map) {
|
|
1055
|
+
entries = Array.from(value.entries())
|
|
1056
|
+
} else {
|
|
1057
|
+
entries = Object.entries(value)
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
if (entries.length === 0) return true // Empty map matches any map type
|
|
1061
|
+
|
|
1062
|
+
const sampleSize = Math.min(5, entries.length)
|
|
1063
|
+
for (let i = 0; i < sampleSize; i++) {
|
|
1064
|
+
const [k, v] = entries[i]
|
|
1065
|
+
|
|
1066
|
+
if (info.keyType) {
|
|
1067
|
+
if (
|
|
1068
|
+
!validateMapKey(
|
|
1069
|
+
k,
|
|
1070
|
+
normalizeTypeInfo(info.keyType as string | TypeInfo),
|
|
1071
|
+
)
|
|
1072
|
+
) {
|
|
1073
|
+
return false
|
|
270
1074
|
}
|
|
271
1075
|
}
|
|
272
|
-
break
|
|
273
1076
|
|
|
274
|
-
case TypeKind.Basic:
|
|
275
|
-
// For basic types, check if the value matches the expected JavaScript type
|
|
276
|
-
// This is a simple check for common basic types
|
|
277
|
-
const basicType = typeof value
|
|
278
1077
|
if (
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
basicType === 'boolean'
|
|
1078
|
+
info.elemType &&
|
|
1079
|
+
!matchesType(v, normalizeTypeInfo(info.elemType as string | TypeInfo))
|
|
282
1080
|
) {
|
|
283
|
-
return
|
|
1081
|
+
return false
|
|
284
1082
|
}
|
|
285
|
-
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
286
1085
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
// In Go, pointers can be nil which we represent as null/undefined in TS
|
|
290
|
-
if (value !== null && value !== undefined) {
|
|
291
|
-
return { value: value as T, ok: true }
|
|
292
|
-
}
|
|
293
|
-
break
|
|
1086
|
+
return true
|
|
1087
|
+
}
|
|
294
1088
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
1089
|
+
/**
|
|
1090
|
+
* Checks if a value matches an array or slice type info.
|
|
1091
|
+
*
|
|
1092
|
+
* @param value The value to check.
|
|
1093
|
+
* @param info The array or slice type info to match against.
|
|
1094
|
+
* @returns True if the value matches the array or slice type, false otherwise.
|
|
1095
|
+
*/
|
|
1096
|
+
function matchesArrayOrSliceType(value: any, info: TypeInfo): boolean {
|
|
1097
|
+
// For slices and arrays, check if the value is an array and sample element types
|
|
1098
|
+
if (!Array.isArray(value)) return false
|
|
1099
|
+
if (!isArrayTypeInfo(info) && !isSliceTypeInfo(info)) return false
|
|
301
1100
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
return { value: value as T, ok: true }
|
|
306
|
-
}
|
|
307
|
-
break
|
|
1101
|
+
if (info.elemType) {
|
|
1102
|
+
const arr = value as any[]
|
|
1103
|
+
if (arr.length === 0) return true // Empty array matches any array type
|
|
308
1104
|
|
|
309
|
-
|
|
310
|
-
|
|
1105
|
+
const sampleSize = Math.min(5, arr.length)
|
|
1106
|
+
for (let i = 0; i < sampleSize; i++) {
|
|
311
1107
|
if (
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
'close' in value &&
|
|
317
|
-
typeof value.send === 'function' &&
|
|
318
|
-
typeof value.receive === 'function' &&
|
|
319
|
-
typeof value.close === 'function'
|
|
1108
|
+
!matchesType(
|
|
1109
|
+
arr[i],
|
|
1110
|
+
normalizeTypeInfo(info.elemType as string | TypeInfo),
|
|
1111
|
+
)
|
|
320
1112
|
) {
|
|
321
|
-
return
|
|
1113
|
+
return false
|
|
322
1114
|
}
|
|
323
|
-
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
return true
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
/**
|
|
1122
|
+
* Checks if a value matches a pointer type info.
|
|
1123
|
+
*
|
|
1124
|
+
* @param value The value to check.
|
|
1125
|
+
* @param info The pointer type info to match against.
|
|
1126
|
+
* @returns True if the value matches the pointer type, false otherwise.
|
|
1127
|
+
*/
|
|
1128
|
+
function matchesPointerType(value: any, info: TypeInfo): boolean {
|
|
1129
|
+
// For pointers, check if value is a Box (has a 'value' property)
|
|
1130
|
+
if (value === null || value === undefined) {
|
|
1131
|
+
return false
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
// Check if the value is a Box (has a 'value' property)
|
|
1135
|
+
if (typeof value !== 'object' || !('value' in value)) {
|
|
1136
|
+
return false
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
if (!isPointerTypeInfo(info)) return false
|
|
1140
|
+
|
|
1141
|
+
if (info.elemType) {
|
|
1142
|
+
const elemTypeInfo = normalizeTypeInfo(info.elemType as string | TypeInfo)
|
|
1143
|
+
return matchesType(value.value, elemTypeInfo)
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
return true
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
/**
|
|
1150
|
+
* Checks if a value matches a function type info.
|
|
1151
|
+
*
|
|
1152
|
+
* @param value The value to check.
|
|
1153
|
+
* @param info The function type info to match against.
|
|
1154
|
+
* @returns True if the value matches the function type, false otherwise.
|
|
1155
|
+
*/
|
|
1156
|
+
function matchesFunctionType(value: any, info: TypeInfo): boolean {
|
|
1157
|
+
// For functions, check if the value is a function
|
|
1158
|
+
return typeof value === 'function'
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
/**
|
|
1162
|
+
* Checks if a value matches a channel type info.
|
|
1163
|
+
*
|
|
1164
|
+
* @param value The value to check.
|
|
1165
|
+
* @param info The channel type info to match against.
|
|
1166
|
+
* @returns True if the value matches the channel type, false otherwise.
|
|
1167
|
+
*/
|
|
1168
|
+
function matchesChannelType(value: any, info: TypeInfo): boolean {
|
|
1169
|
+
return (
|
|
1170
|
+
typeof value === 'object' &&
|
|
1171
|
+
value !== null &&
|
|
1172
|
+
'send' in value &&
|
|
1173
|
+
'receive' in value &&
|
|
1174
|
+
'close' in value &&
|
|
1175
|
+
typeof value.send === 'function' &&
|
|
1176
|
+
typeof value.receive === 'function' &&
|
|
1177
|
+
typeof value.close === 'function'
|
|
1178
|
+
)
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
/**
|
|
1182
|
+
* Checks if a value matches a type info.
|
|
1183
|
+
*
|
|
1184
|
+
* @param value The value to check.
|
|
1185
|
+
* @param info The type info to match against.
|
|
1186
|
+
* @returns True if the value matches the type info, false otherwise.
|
|
1187
|
+
*/
|
|
1188
|
+
function matchesType(value: any, info: TypeInfo): boolean {
|
|
1189
|
+
if (value === null || value === undefined) {
|
|
1190
|
+
return false
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
switch (info.kind) {
|
|
1194
|
+
case TypeKind.Basic:
|
|
1195
|
+
return matchesBasicType(value, info)
|
|
1196
|
+
|
|
1197
|
+
case TypeKind.Struct:
|
|
1198
|
+
return matchesStructType(value, info)
|
|
1199
|
+
|
|
1200
|
+
case TypeKind.Interface:
|
|
1201
|
+
return matchesInterfaceType(value, info)
|
|
1202
|
+
|
|
1203
|
+
case TypeKind.Map:
|
|
1204
|
+
return matchesMapType(value, info)
|
|
1205
|
+
|
|
1206
|
+
case TypeKind.Slice:
|
|
1207
|
+
case TypeKind.Array:
|
|
1208
|
+
return matchesArrayOrSliceType(value, info)
|
|
1209
|
+
|
|
1210
|
+
case TypeKind.Pointer:
|
|
1211
|
+
return matchesPointerType(value, info)
|
|
324
1212
|
|
|
325
1213
|
case TypeKind.Function:
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
break
|
|
1214
|
+
return matchesFunctionType(value, info)
|
|
1215
|
+
|
|
1216
|
+
case TypeKind.Channel:
|
|
1217
|
+
return matchesChannelType(value, info)
|
|
331
1218
|
|
|
332
1219
|
default:
|
|
333
1220
|
console.warn(
|
|
334
|
-
`Type
|
|
1221
|
+
`Type matching for kind '${(info as TypeInfo).kind}' not implemented.`,
|
|
335
1222
|
)
|
|
1223
|
+
return false
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
export function typeAssert<T>(
|
|
1228
|
+
value: any,
|
|
1229
|
+
typeInfo: string | TypeInfo,
|
|
1230
|
+
): TypeAssertResult<T> {
|
|
1231
|
+
const normalizedType = normalizeTypeInfo(typeInfo)
|
|
1232
|
+
|
|
1233
|
+
if (
|
|
1234
|
+
isStructTypeInfo(normalizedType) &&
|
|
1235
|
+
normalizedType.fields &&
|
|
1236
|
+
typeof value === 'object' &&
|
|
1237
|
+
value !== null
|
|
1238
|
+
) {
|
|
1239
|
+
const descFields = Array.from(normalizedType.fields as Set<string>)
|
|
1240
|
+
const valueFields = Object.keys(value)
|
|
1241
|
+
|
|
1242
|
+
// For struct type assertions, we need exact field matching
|
|
1243
|
+
const structMatch =
|
|
1244
|
+
descFields.length === valueFields.length &&
|
|
1245
|
+
descFields.every((field: string) => field in value) &&
|
|
1246
|
+
valueFields.every((field) => descFields.includes(field))
|
|
1247
|
+
|
|
1248
|
+
if (structMatch) {
|
|
1249
|
+
return { value: value as T, ok: true }
|
|
1250
|
+
} else {
|
|
1251
|
+
return { value: null as unknown as T, ok: false }
|
|
1252
|
+
}
|
|
336
1253
|
}
|
|
337
1254
|
|
|
338
|
-
|
|
339
|
-
|
|
1255
|
+
if (
|
|
1256
|
+
isMapTypeInfo(normalizedType) &&
|
|
1257
|
+
typeof value === 'object' &&
|
|
1258
|
+
value !== null
|
|
1259
|
+
) {
|
|
1260
|
+
if (normalizedType.keyType || normalizedType.elemType) {
|
|
1261
|
+
let entries: [any, any][] = []
|
|
1262
|
+
|
|
1263
|
+
if (value instanceof Map) {
|
|
1264
|
+
entries = Array.from(value.entries())
|
|
1265
|
+
} else {
|
|
1266
|
+
entries = Object.entries(value)
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
if (entries.length === 0) {
|
|
1270
|
+
return { value: value as T, ok: true }
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
const sampleSize = Math.min(5, entries.length)
|
|
1274
|
+
for (let i = 0; i < sampleSize; i++) {
|
|
1275
|
+
const [k, v] = entries[i]
|
|
1276
|
+
|
|
1277
|
+
if (normalizedType.keyType) {
|
|
1278
|
+
if (
|
|
1279
|
+
!validateMapKey(
|
|
1280
|
+
k,
|
|
1281
|
+
normalizeTypeInfo(normalizedType.keyType as string | TypeInfo),
|
|
1282
|
+
)
|
|
1283
|
+
) {
|
|
1284
|
+
return { value: null as unknown as T, ok: false }
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
if (normalizedType.elemType) {
|
|
1289
|
+
const elemTypeInfo = normalizeTypeInfo(
|
|
1290
|
+
normalizedType.elemType as string | TypeInfo,
|
|
1291
|
+
)
|
|
1292
|
+
if (!matchesType(v, elemTypeInfo)) {
|
|
1293
|
+
return { value: null as unknown as T, ok: false }
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
// If we get here, the map type assertion passes
|
|
1299
|
+
return { value: value as T, ok: true }
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
const matches = matchesType(value, normalizedType)
|
|
1304
|
+
|
|
1305
|
+
if (matches) {
|
|
1306
|
+
return { value: value as T, ok: true }
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
// If we get here, the assertion failed
|
|
1310
|
+
// For registered types, use the zero value from the registry
|
|
1311
|
+
if (typeof typeInfo === 'string') {
|
|
1312
|
+
const registeredType = typeRegistry.get(typeInfo)
|
|
1313
|
+
if (registeredType && registeredType.zeroValue !== undefined) {
|
|
1314
|
+
return { value: registeredType.zeroValue as T, ok: false }
|
|
1315
|
+
}
|
|
1316
|
+
} else if (normalizedType.zeroValue !== undefined) {
|
|
1317
|
+
return { value: normalizedType.zeroValue as T, ok: false }
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
return { value: null as unknown as T, ok: false }
|
|
340
1321
|
}
|
|
341
1322
|
|
|
342
1323
|
/**
|
|
@@ -770,13 +1751,15 @@ export const makeChannel = <T>(
|
|
|
770
1751
|
* Implements the `Disposable` interface for use with `using` declarations.
|
|
771
1752
|
*/
|
|
772
1753
|
export class DisposableStack implements Disposable {
|
|
773
|
-
|
|
1754
|
+
private stack: (() => void)[] = []
|
|
774
1755
|
|
|
775
1756
|
/**
|
|
776
1757
|
* Adds a function to be executed when the stack is disposed.
|
|
777
1758
|
* @param fn The function to defer.
|
|
778
1759
|
*/
|
|
779
|
-
defer(fn: () => void): void {
|
|
1760
|
+
defer(fn: () => void): void {
|
|
1761
|
+
this.stack.push(fn)
|
|
1762
|
+
}
|
|
780
1763
|
|
|
781
1764
|
/**
|
|
782
1765
|
* Disposes of the resources in the stack by executing the deferred functions
|
|
@@ -786,9 +1769,9 @@ export class DisposableStack implements Disposable {
|
|
|
786
1769
|
*/
|
|
787
1770
|
[Symbol.dispose](): void {
|
|
788
1771
|
// Emulate Go: if a deferred throws, stop and rethrow
|
|
789
|
-
while (this
|
|
790
|
-
const fn = this
|
|
791
|
-
fn()
|
|
1772
|
+
while (this.stack.length) {
|
|
1773
|
+
const fn = this.stack.pop()!
|
|
1774
|
+
fn()
|
|
792
1775
|
}
|
|
793
1776
|
}
|
|
794
1777
|
}
|
|
@@ -799,14 +1782,14 @@ export class DisposableStack implements Disposable {
|
|
|
799
1782
|
* Implements the `AsyncDisposable` interface for use with `await using` declarations.
|
|
800
1783
|
*/
|
|
801
1784
|
export class AsyncDisposableStack implements AsyncDisposable {
|
|
802
|
-
|
|
1785
|
+
private stack: (() => Promise<void> | void)[] = []
|
|
803
1786
|
|
|
804
1787
|
/**
|
|
805
1788
|
* Adds a synchronous or asynchronous function to be executed when the stack is disposed.
|
|
806
1789
|
* @param fn The function to defer. Can return void or a Promise<void>.
|
|
807
1790
|
*/
|
|
808
1791
|
defer(fn: () => Promise<void> | void): void {
|
|
809
|
-
this
|
|
1792
|
+
this.stack.push(fn)
|
|
810
1793
|
}
|
|
811
1794
|
|
|
812
1795
|
/**
|
|
@@ -815,8 +1798,16 @@ export class AsyncDisposableStack implements AsyncDisposable {
|
|
|
815
1798
|
*/
|
|
816
1799
|
async [Symbol.asyncDispose](): Promise<void> {
|
|
817
1800
|
// Execute in LIFO order, awaiting each potentially async function
|
|
818
|
-
for (let i = this
|
|
819
|
-
await this
|
|
1801
|
+
for (let i = this.stack.length - 1; i >= 0; --i) {
|
|
1802
|
+
await this.stack[i]()
|
|
820
1803
|
}
|
|
821
1804
|
}
|
|
822
|
-
}
|
|
1805
|
+
}
|
|
1806
|
+
|
|
1807
|
+
/**
|
|
1808
|
+
* Implementation of Go's built-in println function
|
|
1809
|
+
* @param args Arguments to print
|
|
1810
|
+
*/
|
|
1811
|
+
export function println(...args: any[]): void {
|
|
1812
|
+
console.log(...args)
|
|
1813
|
+
}
|