goscript 0.0.20 → 0.0.22
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/builtin/builtin.ts +390 -119
- package/compiler/assignment.go +407 -0
- package/compiler/compiler.go +203 -5879
- package/compiler/compiler_test.go +4 -0
- package/compiler/composite-lit.go +537 -0
- package/compiler/config.go +3 -0
- package/compiler/decl.go +223 -0
- package/compiler/expr-call.go +423 -0
- package/compiler/expr-selector.go +105 -0
- package/compiler/expr-star.go +90 -0
- package/compiler/expr-type.go +182 -0
- package/compiler/expr-value.go +119 -0
- package/compiler/expr.go +356 -0
- package/compiler/field.go +169 -0
- package/compiler/lit.go +99 -0
- package/compiler/output.go +1 -1
- package/compiler/primitive.go +142 -0
- package/compiler/{write-type-spec.go → spec-struct.go} +79 -203
- package/compiler/spec-value.go +194 -0
- package/compiler/spec.go +263 -0
- package/compiler/stmt-assign.go +411 -0
- package/compiler/stmt-for.go +178 -0
- package/compiler/stmt-range.go +237 -0
- package/compiler/stmt.go +907 -0
- package/compiler/type-assert.go +463 -0
- package/compiler/type-info.go +141 -0
- package/compiler/type.go +556 -0
- package/dist/builtin/builtin.d.ts +24 -6
- package/dist/builtin/builtin.js +215 -19
- package/dist/builtin/builtin.js.map +1 -1
- package/go.mod +2 -1
- package/go.sum +4 -2
- package/package.json +2 -2
- /package/compiler/{writer.go → code-writer.go} +0 -0
package/builtin/builtin.ts
CHANGED
|
@@ -724,12 +724,29 @@ export interface BaseTypeInfo {
|
|
|
724
724
|
zeroValue?: any
|
|
725
725
|
}
|
|
726
726
|
|
|
727
|
+
/**
|
|
728
|
+
* Represents an argument or a return value of a method.
|
|
729
|
+
*/
|
|
730
|
+
export interface MethodArg {
|
|
731
|
+
name?: string; // Name of the argument/return value, if available
|
|
732
|
+
type: TypeInfo | string; // TypeInfo object or string name of the type
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
/**
|
|
736
|
+
* Represents the signature of a method, including its name, arguments, and return types.
|
|
737
|
+
*/
|
|
738
|
+
export interface MethodSignature {
|
|
739
|
+
name: string;
|
|
740
|
+
args: MethodArg[];
|
|
741
|
+
returns: MethodArg[];
|
|
742
|
+
}
|
|
743
|
+
|
|
727
744
|
/**
|
|
728
745
|
* Type information for struct types
|
|
729
746
|
*/
|
|
730
747
|
export interface StructTypeInfo extends BaseTypeInfo {
|
|
731
748
|
kind: TypeKind.Struct
|
|
732
|
-
methods:
|
|
749
|
+
methods: MethodSignature[] // Array of method signatures
|
|
733
750
|
ctor?: new (...args: any[]) => any
|
|
734
751
|
fields: Record<string, TypeInfo | string> // Field names and types for struct fields
|
|
735
752
|
}
|
|
@@ -739,7 +756,7 @@ export interface StructTypeInfo extends BaseTypeInfo {
|
|
|
739
756
|
*/
|
|
740
757
|
export interface InterfaceTypeInfo extends BaseTypeInfo {
|
|
741
758
|
kind: TypeKind.Interface
|
|
742
|
-
methods:
|
|
759
|
+
methods: MethodSignature[] // Array of method signatures
|
|
743
760
|
}
|
|
744
761
|
|
|
745
762
|
/**
|
|
@@ -790,6 +807,7 @@ export interface FunctionTypeInfo extends BaseTypeInfo {
|
|
|
790
807
|
kind: TypeKind.Function
|
|
791
808
|
params?: (string | TypeInfo)[]
|
|
792
809
|
results?: (string | TypeInfo)[]
|
|
810
|
+
isVariadic?: boolean // True if the function is variadic (e.g., ...T)
|
|
793
811
|
}
|
|
794
812
|
|
|
795
813
|
/**
|
|
@@ -860,14 +878,15 @@ const typeRegistry = new Map<string, TypeInfo>()
|
|
|
860
878
|
*
|
|
861
879
|
* @param name The name of the type.
|
|
862
880
|
* @param zeroValue The zero value for the type.
|
|
863
|
-
* @param methods
|
|
881
|
+
* @param methods Array of method signatures for the struct.
|
|
864
882
|
* @param ctor Constructor for the struct.
|
|
883
|
+
* @param fields Record of field names and their types.
|
|
865
884
|
* @returns The struct type information object.
|
|
866
885
|
*/
|
|
867
886
|
export const registerStructType = (
|
|
868
887
|
name: string,
|
|
869
888
|
zeroValue: any,
|
|
870
|
-
methods:
|
|
889
|
+
methods: MethodSignature[],
|
|
871
890
|
ctor: new (...args: any[]) => any,
|
|
872
891
|
fields: Record<string, TypeInfo | string> = {},
|
|
873
892
|
): StructTypeInfo => {
|
|
@@ -888,13 +907,13 @@ export const registerStructType = (
|
|
|
888
907
|
*
|
|
889
908
|
* @param name The name of the type.
|
|
890
909
|
* @param zeroValue The zero value for the type (usually null).
|
|
891
|
-
* @param methods
|
|
910
|
+
* @param methods Array of method signatures for the interface.
|
|
892
911
|
* @returns The interface type information object.
|
|
893
912
|
*/
|
|
894
913
|
export const registerInterfaceType = (
|
|
895
914
|
name: string,
|
|
896
915
|
zeroValue: any,
|
|
897
|
-
methods:
|
|
916
|
+
methods: MethodSignature[],
|
|
898
917
|
): InterfaceTypeInfo => {
|
|
899
918
|
const typeInfo: InterfaceTypeInfo = {
|
|
900
919
|
name,
|
|
@@ -935,6 +954,147 @@ function normalizeTypeInfo(info: string | TypeInfo): TypeInfo {
|
|
|
935
954
|
return info
|
|
936
955
|
}
|
|
937
956
|
|
|
957
|
+
function compareOptionalTypeInfo(
|
|
958
|
+
type1?: string | TypeInfo,
|
|
959
|
+
type2?: string | TypeInfo,
|
|
960
|
+
): boolean {
|
|
961
|
+
if (type1 === undefined && type2 === undefined) return true
|
|
962
|
+
if (type1 === undefined || type2 === undefined) return false
|
|
963
|
+
// Assuming areTypeInfosIdentical will handle normalization if needed,
|
|
964
|
+
// but type1 and type2 here are expected to be direct fields from TypeInfo objects.
|
|
965
|
+
return areTypeInfosIdentical(type1, type2)
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
function areFuncParamOrResultArraysIdentical(
|
|
969
|
+
arr1?: (string | TypeInfo)[],
|
|
970
|
+
arr2?: (string | TypeInfo)[]
|
|
971
|
+
): boolean {
|
|
972
|
+
if (arr1 === undefined && arr2 === undefined) return true
|
|
973
|
+
if (arr1 === undefined || arr2 === undefined) return false
|
|
974
|
+
if (arr1.length !== arr2.length) return false
|
|
975
|
+
for (let i = 0; i < arr1.length; i++) {
|
|
976
|
+
if (!areTypeInfosIdentical(arr1[i], arr2[i])) {
|
|
977
|
+
return false
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
return true
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
function areFuncSignaturesIdentical(
|
|
984
|
+
func1: FunctionTypeInfo,
|
|
985
|
+
func2: FunctionTypeInfo,
|
|
986
|
+
): boolean {
|
|
987
|
+
if ((func1.isVariadic || false) !== (func2.isVariadic || false)) {
|
|
988
|
+
return false
|
|
989
|
+
}
|
|
990
|
+
return (
|
|
991
|
+
areFuncParamOrResultArraysIdentical(func1.params, func2.params) &&
|
|
992
|
+
areFuncParamOrResultArraysIdentical(func1.results, func2.results)
|
|
993
|
+
)
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
function areMethodArgsArraysIdentical(
|
|
997
|
+
args1?: MethodArg[],
|
|
998
|
+
args2?: MethodArg[],
|
|
999
|
+
): boolean {
|
|
1000
|
+
if (args1 === undefined && args2 === undefined) return true
|
|
1001
|
+
if (args1 === undefined || args2 === undefined) return false
|
|
1002
|
+
if (args1.length !== args2.length) return false
|
|
1003
|
+
for (let i = 0; i < args1.length; i++) {
|
|
1004
|
+
// Compare based on type only, names of args/results don't affect signature identity here.
|
|
1005
|
+
if (!areTypeInfosIdentical(args1[i].type, args2[i].type)) {
|
|
1006
|
+
return false
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
return true
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
export function areTypeInfosIdentical(
|
|
1013
|
+
type1InfoOrName: string | TypeInfo,
|
|
1014
|
+
type2InfoOrName: string | TypeInfo,
|
|
1015
|
+
): boolean {
|
|
1016
|
+
const t1Norm = normalizeTypeInfo(type1InfoOrName)
|
|
1017
|
+
const t2Norm = normalizeTypeInfo(type2InfoOrName)
|
|
1018
|
+
|
|
1019
|
+
if (t1Norm === t2Norm) return true // Object identity
|
|
1020
|
+
if (t1Norm.kind !== t2Norm.kind) return false
|
|
1021
|
+
|
|
1022
|
+
// If types have names, the names must match for identity.
|
|
1023
|
+
// If one has a name and the other doesn't, they are not identical.
|
|
1024
|
+
if (t1Norm.name !== t2Norm.name) return false
|
|
1025
|
+
|
|
1026
|
+
// If both are named and names match, for Basic, Struct, Interface, this is sufficient for identity.
|
|
1027
|
+
if (t1Norm.name !== undefined /* && t2Norm.name is also defined and equal */) {
|
|
1028
|
+
if (
|
|
1029
|
+
t1Norm.kind === TypeKind.Basic ||
|
|
1030
|
+
t1Norm.kind === TypeKind.Struct ||
|
|
1031
|
+
t1Norm.kind === TypeKind.Interface
|
|
1032
|
+
) {
|
|
1033
|
+
return true
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
// For other types (Pointer, Slice, etc.), or if both are anonymous (name is undefined),
|
|
1037
|
+
// structural comparison is needed.
|
|
1038
|
+
|
|
1039
|
+
switch (t1Norm.kind) {
|
|
1040
|
+
case TypeKind.Basic:
|
|
1041
|
+
// Names matched if they were defined, or both undefined (which means true by t1Norm.name !== t2Norm.name being false)
|
|
1042
|
+
return true
|
|
1043
|
+
case TypeKind.Pointer:
|
|
1044
|
+
return compareOptionalTypeInfo(
|
|
1045
|
+
(t1Norm as PointerTypeInfo).elemType,
|
|
1046
|
+
(t2Norm as PointerTypeInfo).elemType,
|
|
1047
|
+
)
|
|
1048
|
+
case TypeKind.Slice:
|
|
1049
|
+
return compareOptionalTypeInfo(
|
|
1050
|
+
(t1Norm as SliceTypeInfo).elemType,
|
|
1051
|
+
(t2Norm as SliceTypeInfo).elemType,
|
|
1052
|
+
)
|
|
1053
|
+
case TypeKind.Array:
|
|
1054
|
+
return (
|
|
1055
|
+
(t1Norm as ArrayTypeInfo).length === (t2Norm as ArrayTypeInfo).length &&
|
|
1056
|
+
compareOptionalTypeInfo(
|
|
1057
|
+
(t1Norm as ArrayTypeInfo).elemType,
|
|
1058
|
+
(t2Norm as ArrayTypeInfo).elemType,
|
|
1059
|
+
)
|
|
1060
|
+
)
|
|
1061
|
+
case TypeKind.Map:
|
|
1062
|
+
return (
|
|
1063
|
+
compareOptionalTypeInfo(
|
|
1064
|
+
(t1Norm as MapTypeInfo).keyType,
|
|
1065
|
+
(t2Norm as MapTypeInfo).keyType,
|
|
1066
|
+
) &&
|
|
1067
|
+
compareOptionalTypeInfo(
|
|
1068
|
+
(t1Norm as MapTypeInfo).elemType,
|
|
1069
|
+
(t2Norm as MapTypeInfo).elemType,
|
|
1070
|
+
)
|
|
1071
|
+
)
|
|
1072
|
+
case TypeKind.Channel:
|
|
1073
|
+
return (
|
|
1074
|
+
// Ensure direction property exists before comparing, or handle undefined if it can be
|
|
1075
|
+
((t1Norm as ChannelTypeInfo).direction || 'both') === ((t2Norm as ChannelTypeInfo).direction || 'both') &&
|
|
1076
|
+
compareOptionalTypeInfo(
|
|
1077
|
+
(t1Norm as ChannelTypeInfo).elemType,
|
|
1078
|
+
(t2Norm as ChannelTypeInfo).elemType,
|
|
1079
|
+
)
|
|
1080
|
+
)
|
|
1081
|
+
case TypeKind.Function:
|
|
1082
|
+
return areFuncSignaturesIdentical(
|
|
1083
|
+
t1Norm as FunctionTypeInfo,
|
|
1084
|
+
t2Norm as FunctionTypeInfo,
|
|
1085
|
+
)
|
|
1086
|
+
case TypeKind.Struct:
|
|
1087
|
+
case TypeKind.Interface:
|
|
1088
|
+
// If we reach here, names were undefined (both anonymous) or names matched but was not Basic/Struct/Interface.
|
|
1089
|
+
// For anonymous Struct/Interface, strict identity means full structural comparison.
|
|
1090
|
+
// For now, we consider anonymous types not identical unless they are the same object (caught above).
|
|
1091
|
+
// If they were named and matched, 'return true' was hit earlier for these kinds.
|
|
1092
|
+
return false
|
|
1093
|
+
default:
|
|
1094
|
+
return false
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
|
|
938
1098
|
/**
|
|
939
1099
|
* Validates that a map key matches the expected type info.
|
|
940
1100
|
*
|
|
@@ -993,16 +1153,20 @@ function matchesStructType(value: any, info: TypeInfo): boolean {
|
|
|
993
1153
|
return true
|
|
994
1154
|
}
|
|
995
1155
|
|
|
1156
|
+
// Check if the value has all methods defined in the struct's TypeInfo
|
|
1157
|
+
// This is a structural check, not a signature check here.
|
|
1158
|
+
// Signature checks are more relevant for interface satisfaction.
|
|
996
1159
|
if (info.methods && typeof value === 'object' && value !== null) {
|
|
997
|
-
const
|
|
998
|
-
(
|
|
1160
|
+
const allMethodsExist = info.methods.every(
|
|
1161
|
+
(methodSig) => typeof (value as any)[methodSig.name] === 'function',
|
|
999
1162
|
)
|
|
1000
|
-
if (
|
|
1001
|
-
return
|
|
1163
|
+
if (!allMethodsExist) {
|
|
1164
|
+
return false
|
|
1002
1165
|
}
|
|
1166
|
+
// Further signature checking could be added here if needed for struct-to-struct assignability
|
|
1003
1167
|
}
|
|
1004
1168
|
|
|
1005
|
-
if (typeof value === 'object' && value !== null) {
|
|
1169
|
+
if (typeof value === 'object' && value !== null && info.fields) {
|
|
1006
1170
|
const fieldNames = Object.keys(info.fields || {})
|
|
1007
1171
|
const valueFields = Object.keys(value)
|
|
1008
1172
|
|
|
@@ -1034,19 +1198,96 @@ function matchesStructType(value: any, info: TypeInfo): boolean {
|
|
|
1034
1198
|
* @param info The interface type info to match against.
|
|
1035
1199
|
* @returns True if the value matches the interface type, false otherwise.
|
|
1036
1200
|
*/
|
|
1201
|
+
/**
|
|
1202
|
+
* Checks if a value matches an interface type info by verifying it implements
|
|
1203
|
+
* all required methods with compatible signatures.
|
|
1204
|
+
*
|
|
1205
|
+
* @param value The value to check.
|
|
1206
|
+
* @param info The interface type info to match against.
|
|
1207
|
+
* @returns True if the value matches the interface type, false otherwise.
|
|
1208
|
+
*/
|
|
1037
1209
|
function matchesInterfaceType(value: any, info: TypeInfo): boolean {
|
|
1038
|
-
//
|
|
1039
|
-
if (
|
|
1040
|
-
|
|
1041
|
-
info.methods &&
|
|
1042
|
-
typeof value === 'object' &&
|
|
1043
|
-
value !== null
|
|
1044
|
-
) {
|
|
1045
|
-
return Array.from(info.methods).every(
|
|
1046
|
-
(method) => typeof (value as any)[method] === 'function',
|
|
1047
|
-
)
|
|
1210
|
+
// Check basic conditions first
|
|
1211
|
+
if (!isInterfaceTypeInfo(info) || typeof value !== 'object' || value === null) {
|
|
1212
|
+
return false
|
|
1048
1213
|
}
|
|
1049
|
-
|
|
1214
|
+
|
|
1215
|
+
// For interfaces, check if the value has all the required methods with compatible signatures
|
|
1216
|
+
return info.methods.every((requiredMethodSig) => {
|
|
1217
|
+
const actualMethod = (value as any)[requiredMethodSig.name]
|
|
1218
|
+
|
|
1219
|
+
// Method must exist and be a function
|
|
1220
|
+
if (typeof actualMethod !== 'function') {
|
|
1221
|
+
return false
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
// Check parameter count (basic arity check)
|
|
1225
|
+
// Note: This is a simplified check as JavaScript functions can have optional/rest parameters
|
|
1226
|
+
const declaredParamCount = actualMethod.length
|
|
1227
|
+
const requiredParamCount = requiredMethodSig.args.length
|
|
1228
|
+
|
|
1229
|
+
// Strict arity checking can be problematic in JS, so we'll be lenient
|
|
1230
|
+
// A method with fewer params than required is definitely incompatible
|
|
1231
|
+
if (declaredParamCount < requiredParamCount) {
|
|
1232
|
+
return false
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
// Check return types if we can determine them
|
|
1236
|
+
// This is challenging in JavaScript without runtime type information
|
|
1237
|
+
|
|
1238
|
+
// If the value has a __goTypeName property, it might be a registered type
|
|
1239
|
+
// with more type information available
|
|
1240
|
+
if (value.__goTypeName) {
|
|
1241
|
+
const valueTypeInfo = typeRegistry.get(value.__goTypeName)
|
|
1242
|
+
if (valueTypeInfo && isStructTypeInfo(valueTypeInfo)) {
|
|
1243
|
+
// Find the matching method in the value's type info
|
|
1244
|
+
const valueMethodSig = valueTypeInfo.methods.find(
|
|
1245
|
+
m => m.name === requiredMethodSig.name
|
|
1246
|
+
)
|
|
1247
|
+
|
|
1248
|
+
if (valueMethodSig) {
|
|
1249
|
+
// Compare return types
|
|
1250
|
+
if (valueMethodSig.returns.length !== requiredMethodSig.returns.length) {
|
|
1251
|
+
return false
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
// Compare each return type for compatibility
|
|
1255
|
+
for (let i = 0; i < requiredMethodSig.returns.length; i++) {
|
|
1256
|
+
const requiredReturnType = normalizeTypeInfo(
|
|
1257
|
+
requiredMethodSig.returns[i].type
|
|
1258
|
+
)
|
|
1259
|
+
const valueReturnType = normalizeTypeInfo(
|
|
1260
|
+
valueMethodSig.returns[i].type
|
|
1261
|
+
)
|
|
1262
|
+
|
|
1263
|
+
// For interface return types, we need to check if the value's return type
|
|
1264
|
+
// implements the required interface
|
|
1265
|
+
if (isInterfaceTypeInfo(requiredReturnType)) {
|
|
1266
|
+
// This would be a recursive check, but we'll simplify for now
|
|
1267
|
+
// by just checking if the types are the same or if the value type
|
|
1268
|
+
// is registered as implementing the interface
|
|
1269
|
+
if (requiredReturnType.name !== valueReturnType.name) {
|
|
1270
|
+
// Check if valueReturnType implements requiredReturnType
|
|
1271
|
+
// This would require additional implementation tracking
|
|
1272
|
+
return false
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
// For non-interface types, check direct type compatibility
|
|
1276
|
+
else if (requiredReturnType.name !== valueReturnType.name) {
|
|
1277
|
+
return false
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
// Similarly, we could check parameter types for compatibility
|
|
1282
|
+
// but we'll skip that for brevity
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
// If we can't determine detailed type information, we'll accept the method
|
|
1288
|
+
// as long as it exists with a compatible arity
|
|
1289
|
+
return true
|
|
1290
|
+
})
|
|
1050
1291
|
}
|
|
1051
1292
|
|
|
1052
1293
|
/**
|
|
@@ -1297,22 +1538,46 @@ export function typeAssert<T>(
|
|
|
1297
1538
|
|
|
1298
1539
|
if (
|
|
1299
1540
|
isStructTypeInfo(normalizedType) &&
|
|
1300
|
-
normalizedType.methods &&
|
|
1541
|
+
normalizedType.methods && normalizedType.methods.length > 0 &&
|
|
1301
1542
|
typeof value === 'object' &&
|
|
1302
1543
|
value !== null
|
|
1303
1544
|
) {
|
|
1304
|
-
// Check if the value implements all methods of the struct type
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
)
|
|
1545
|
+
// Check if the value implements all methods of the struct type with compatible signatures.
|
|
1546
|
+
// This is more for interface satisfaction by a struct.
|
|
1547
|
+
// For struct-to-struct assertion, usually instanceof or field checks are primary.
|
|
1548
|
+
const allMethodsMatch = normalizedType.methods.every((requiredMethodSig) => {
|
|
1549
|
+
const actualMethod = (value as any)[requiredMethodSig.name];
|
|
1550
|
+
if (typeof actualMethod !== 'function') {
|
|
1551
|
+
return false;
|
|
1552
|
+
}
|
|
1553
|
+
const valueTypeInfoVal = (value as any).$typeInfo
|
|
1554
|
+
if (valueTypeInfoVal) {
|
|
1555
|
+
const normalizedValueType = normalizeTypeInfo(valueTypeInfoVal)
|
|
1556
|
+
if (isStructTypeInfo(normalizedValueType) || isInterfaceTypeInfo(normalizedValueType)) {
|
|
1557
|
+
const actualValueMethodSig = normalizedValueType.methods.find(m => m.name === requiredMethodSig.name)
|
|
1558
|
+
if (actualValueMethodSig) {
|
|
1559
|
+
// Perform full signature comparison using MethodSignatures
|
|
1560
|
+
const paramsMatch = areMethodArgsArraysIdentical(requiredMethodSig.args, actualValueMethodSig.args)
|
|
1561
|
+
const resultsMatch = areMethodArgsArraysIdentical(requiredMethodSig.returns, actualValueMethodSig.returns)
|
|
1562
|
+
return paramsMatch && resultsMatch
|
|
1563
|
+
} else {
|
|
1564
|
+
// Value has TypeInfo listing methods, but this specific method isn't listed.
|
|
1565
|
+
// This implies a mismatch for strict signature check based on TypeInfo.
|
|
1566
|
+
return false
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1308
1570
|
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1571
|
+
// Fallback: Original behavior if value has no TypeInfo that lists methods,
|
|
1572
|
+
// or if the method wasn't found in its TypeInfo (covered by 'else' returning false above).
|
|
1573
|
+
// The original comment was: "For now, presence and function type is checked by matchesStructType/matchesInterfaceType"
|
|
1574
|
+
// This 'return true' implies that if we couldn't do a full signature check via TypeInfo,
|
|
1575
|
+
// we still consider it a match if the function simply exists on the object.
|
|
1576
|
+
return true;
|
|
1577
|
+
});
|
|
1312
1578
|
|
|
1313
|
-
if (allMethodsMatch
|
|
1314
|
-
|
|
1315
|
-
return { value: value as T, ok: true }
|
|
1579
|
+
if (allMethodsMatch) {
|
|
1580
|
+
return { value: value as T, ok: true };
|
|
1316
1581
|
}
|
|
1317
1582
|
}
|
|
1318
1583
|
|
|
@@ -1498,182 +1763,182 @@ export interface Channel<T> {
|
|
|
1498
1763
|
|
|
1499
1764
|
// A simple implementation of buffered channels
|
|
1500
1765
|
class BufferedChannel<T> implements Channel<T> {
|
|
1501
|
-
private buffer: T[] = []
|
|
1502
|
-
private closed: boolean = false
|
|
1503
|
-
private capacity: number
|
|
1504
|
-
public zeroValue: T
|
|
1766
|
+
private buffer: T[] = []
|
|
1767
|
+
private closed: boolean = false
|
|
1768
|
+
private capacity: number
|
|
1769
|
+
public zeroValue: T // Made public for access by ChannelRef or for type inference
|
|
1505
1770
|
|
|
1506
1771
|
// Senders queue: stores { value, resolve for send, reject for send }
|
|
1507
1772
|
private senders: Array<{
|
|
1508
|
-
value: T
|
|
1509
|
-
resolveSend: () => void
|
|
1510
|
-
rejectSend: (e: Error) => void
|
|
1511
|
-
}> = []
|
|
1773
|
+
value: T
|
|
1774
|
+
resolveSend: () => void
|
|
1775
|
+
rejectSend: (e: Error) => void
|
|
1776
|
+
}> = []
|
|
1512
1777
|
|
|
1513
1778
|
// Receivers queue for receive(): stores { resolve for receive, reject for receive }
|
|
1514
1779
|
private receivers: Array<{
|
|
1515
|
-
resolveReceive: (value: T) => void
|
|
1516
|
-
rejectReceive: (e: Error) => void
|
|
1517
|
-
}> = []
|
|
1780
|
+
resolveReceive: (value: T) => void
|
|
1781
|
+
rejectReceive: (e: Error) => void
|
|
1782
|
+
}> = []
|
|
1518
1783
|
|
|
1519
1784
|
// Receivers queue for receiveWithOk(): stores { resolve for receiveWithOk }
|
|
1520
1785
|
private receiversWithOk: Array<{
|
|
1521
|
-
resolveReceive: (result: ChannelReceiveResult<T>) => void
|
|
1522
|
-
}> = []
|
|
1786
|
+
resolveReceive: (result: ChannelReceiveResult<T>) => void
|
|
1787
|
+
}> = []
|
|
1523
1788
|
|
|
1524
1789
|
constructor(capacity: number, zeroValue: T) {
|
|
1525
1790
|
if (capacity < 0) {
|
|
1526
|
-
throw new Error('Channel capacity cannot be negative')
|
|
1791
|
+
throw new Error('Channel capacity cannot be negative')
|
|
1527
1792
|
}
|
|
1528
|
-
this.capacity = capacity
|
|
1529
|
-
this.zeroValue = zeroValue
|
|
1793
|
+
this.capacity = capacity
|
|
1794
|
+
this.zeroValue = zeroValue
|
|
1530
1795
|
}
|
|
1531
1796
|
|
|
1532
1797
|
async send(value: T): Promise<void> {
|
|
1533
1798
|
if (this.closed) {
|
|
1534
|
-
throw new Error('send on closed channel')
|
|
1799
|
+
throw new Error('send on closed channel')
|
|
1535
1800
|
}
|
|
1536
1801
|
|
|
1537
1802
|
// Attempt to hand off to a waiting receiver (rendezvous)
|
|
1538
1803
|
if (this.receivers.length > 0) {
|
|
1539
|
-
const receiverTask = this.receivers.shift()
|
|
1540
|
-
queueMicrotask(() => receiverTask.resolveReceive(value))
|
|
1541
|
-
return
|
|
1804
|
+
const receiverTask = this.receivers.shift()!
|
|
1805
|
+
queueMicrotask(() => receiverTask.resolveReceive(value))
|
|
1806
|
+
return
|
|
1542
1807
|
}
|
|
1543
1808
|
if (this.receiversWithOk.length > 0) {
|
|
1544
|
-
const receiverTask = this.receiversWithOk.shift()
|
|
1545
|
-
queueMicrotask(() => receiverTask.resolveReceive({ value, ok: true }))
|
|
1546
|
-
return
|
|
1809
|
+
const receiverTask = this.receiversWithOk.shift()!
|
|
1810
|
+
queueMicrotask(() => receiverTask.resolveReceive({ value, ok: true }))
|
|
1811
|
+
return
|
|
1547
1812
|
}
|
|
1548
1813
|
|
|
1549
1814
|
// If no waiting receivers, try to buffer if space is available
|
|
1550
1815
|
if (this.buffer.length < this.capacity) {
|
|
1551
|
-
this.buffer.push(value)
|
|
1552
|
-
return
|
|
1816
|
+
this.buffer.push(value)
|
|
1817
|
+
return
|
|
1553
1818
|
}
|
|
1554
1819
|
|
|
1555
1820
|
// Buffer is full (or capacity is 0 and no receivers are waiting). Sender must block.
|
|
1556
1821
|
return new Promise<void>((resolve, reject) => {
|
|
1557
|
-
this.senders.push({ value, resolveSend: resolve, rejectSend: reject })
|
|
1558
|
-
})
|
|
1822
|
+
this.senders.push({ value, resolveSend: resolve, rejectSend: reject })
|
|
1823
|
+
})
|
|
1559
1824
|
}
|
|
1560
1825
|
|
|
1561
1826
|
async receive(): Promise<T> {
|
|
1562
1827
|
// Attempt to get from buffer first
|
|
1563
1828
|
if (this.buffer.length > 0) {
|
|
1564
|
-
const value = this.buffer.shift()
|
|
1829
|
+
const value = this.buffer.shift()!
|
|
1565
1830
|
// If a sender was waiting because the buffer was full, unblock it.
|
|
1566
1831
|
if (this.senders.length > 0) {
|
|
1567
|
-
const senderTask = this.senders.shift()
|
|
1568
|
-
this.buffer.push(senderTask.value)
|
|
1569
|
-
queueMicrotask(() => senderTask.resolveSend())
|
|
1832
|
+
const senderTask = this.senders.shift()!
|
|
1833
|
+
this.buffer.push(senderTask.value) // Sender's value now goes into buffer
|
|
1834
|
+
queueMicrotask(() => senderTask.resolveSend()) // Unblock sender
|
|
1570
1835
|
}
|
|
1571
|
-
return value
|
|
1836
|
+
return value
|
|
1572
1837
|
}
|
|
1573
1838
|
|
|
1574
1839
|
// Buffer is empty.
|
|
1575
1840
|
// If channel is closed (and buffer is empty), subsequent receives panic.
|
|
1576
1841
|
if (this.closed) {
|
|
1577
|
-
throw new Error('receive on closed channel')
|
|
1842
|
+
throw new Error('receive on closed channel')
|
|
1578
1843
|
}
|
|
1579
1844
|
|
|
1580
1845
|
// Buffer is empty, channel is open.
|
|
1581
1846
|
// Attempt to rendezvous with a waiting sender.
|
|
1582
1847
|
if (this.senders.length > 0) {
|
|
1583
|
-
const senderTask = this.senders.shift()
|
|
1584
|
-
queueMicrotask(() => senderTask.resolveSend())
|
|
1585
|
-
return senderTask.value
|
|
1848
|
+
const senderTask = this.senders.shift()!
|
|
1849
|
+
queueMicrotask(() => senderTask.resolveSend()) // Unblock the sender
|
|
1850
|
+
return senderTask.value // Return the value from sender
|
|
1586
1851
|
}
|
|
1587
1852
|
|
|
1588
1853
|
// Buffer is empty, channel is open, no waiting senders. Receiver must block.
|
|
1589
1854
|
return new Promise<T>((resolve, reject) => {
|
|
1590
|
-
this.receivers.push({ resolveReceive: resolve, rejectReceive: reject })
|
|
1591
|
-
})
|
|
1855
|
+
this.receivers.push({ resolveReceive: resolve, rejectReceive: reject })
|
|
1856
|
+
})
|
|
1592
1857
|
}
|
|
1593
1858
|
|
|
1594
1859
|
async receiveWithOk(): Promise<ChannelReceiveResult<T>> {
|
|
1595
1860
|
// Attempt to get from buffer first
|
|
1596
1861
|
if (this.buffer.length > 0) {
|
|
1597
|
-
const value = this.buffer.shift()
|
|
1862
|
+
const value = this.buffer.shift()!
|
|
1598
1863
|
if (this.senders.length > 0) {
|
|
1599
|
-
const senderTask = this.senders.shift()
|
|
1600
|
-
this.buffer.push(senderTask.value)
|
|
1601
|
-
queueMicrotask(() => senderTask.resolveSend())
|
|
1864
|
+
const senderTask = this.senders.shift()!
|
|
1865
|
+
this.buffer.push(senderTask.value)
|
|
1866
|
+
queueMicrotask(() => senderTask.resolveSend())
|
|
1602
1867
|
}
|
|
1603
|
-
return { value, ok: true }
|
|
1868
|
+
return { value, ok: true }
|
|
1604
1869
|
}
|
|
1605
1870
|
|
|
1606
1871
|
// Buffer is empty.
|
|
1607
1872
|
// Attempt to rendezvous with a waiting sender.
|
|
1608
1873
|
if (this.senders.length > 0) {
|
|
1609
|
-
const senderTask = this.senders.shift()
|
|
1610
|
-
queueMicrotask(() => senderTask.resolveSend())
|
|
1611
|
-
return { value: senderTask.value, ok: true }
|
|
1874
|
+
const senderTask = this.senders.shift()!
|
|
1875
|
+
queueMicrotask(() => senderTask.resolveSend())
|
|
1876
|
+
return { value: senderTask.value, ok: true }
|
|
1612
1877
|
}
|
|
1613
1878
|
|
|
1614
1879
|
// Buffer is empty, no waiting senders.
|
|
1615
1880
|
// If channel is closed, return zero value with ok: false.
|
|
1616
1881
|
if (this.closed) {
|
|
1617
|
-
return { value: this.zeroValue, ok: false }
|
|
1882
|
+
return { value: this.zeroValue, ok: false }
|
|
1618
1883
|
}
|
|
1619
1884
|
|
|
1620
1885
|
// Buffer is empty, channel is open, no waiting senders. Receiver must block.
|
|
1621
1886
|
return new Promise<ChannelReceiveResult<T>>((resolve) => {
|
|
1622
|
-
this.receiversWithOk.push({ resolveReceive: resolve })
|
|
1623
|
-
})
|
|
1887
|
+
this.receiversWithOk.push({ resolveReceive: resolve })
|
|
1888
|
+
})
|
|
1624
1889
|
}
|
|
1625
1890
|
|
|
1626
1891
|
async selectReceive(id: number): Promise<SelectResult<T>> {
|
|
1627
1892
|
if (this.buffer.length > 0) {
|
|
1628
|
-
const value = this.buffer.shift()
|
|
1893
|
+
const value = this.buffer.shift()!
|
|
1629
1894
|
if (this.senders.length > 0) {
|
|
1630
|
-
const senderTask = this.senders.shift()
|
|
1631
|
-
this.buffer.push(senderTask.value)
|
|
1632
|
-
queueMicrotask(() => senderTask.resolveSend())
|
|
1895
|
+
const senderTask = this.senders.shift()!
|
|
1896
|
+
this.buffer.push(senderTask.value)
|
|
1897
|
+
queueMicrotask(() => senderTask.resolveSend())
|
|
1633
1898
|
}
|
|
1634
|
-
return { value, ok: true, id }
|
|
1899
|
+
return { value, ok: true, id }
|
|
1635
1900
|
}
|
|
1636
1901
|
|
|
1637
1902
|
if (this.senders.length > 0) {
|
|
1638
|
-
const senderTask = this.senders.shift()
|
|
1639
|
-
queueMicrotask(() => senderTask.resolveSend())
|
|
1640
|
-
return { value: senderTask.value, ok: true, id }
|
|
1903
|
+
const senderTask = this.senders.shift()!
|
|
1904
|
+
queueMicrotask(() => senderTask.resolveSend())
|
|
1905
|
+
return { value: senderTask.value, ok: true, id }
|
|
1641
1906
|
}
|
|
1642
1907
|
|
|
1643
1908
|
if (this.closed) {
|
|
1644
|
-
return { value: this.zeroValue, ok: false, id }
|
|
1909
|
+
return { value: this.zeroValue, ok: false, id }
|
|
1645
1910
|
}
|
|
1646
1911
|
|
|
1647
1912
|
return new Promise<SelectResult<T>>((resolve) => {
|
|
1648
1913
|
this.receiversWithOk.push({
|
|
1649
1914
|
resolveReceive: (result: ChannelReceiveResult<T>) => {
|
|
1650
|
-
resolve({ ...result, id })
|
|
1915
|
+
resolve({ ...result, id })
|
|
1651
1916
|
},
|
|
1652
|
-
})
|
|
1653
|
-
})
|
|
1917
|
+
})
|
|
1918
|
+
})
|
|
1654
1919
|
}
|
|
1655
1920
|
|
|
1656
1921
|
async selectSend(value: T, id: number): Promise<SelectResult<boolean>> {
|
|
1657
1922
|
if (this.closed) {
|
|
1658
1923
|
// A select case sending on a closed channel panics in Go.
|
|
1659
1924
|
// This will cause Promise.race in selectStatement to reject.
|
|
1660
|
-
throw new Error('send on closed channel')
|
|
1925
|
+
throw new Error('send on closed channel')
|
|
1661
1926
|
}
|
|
1662
1927
|
|
|
1663
1928
|
if (this.receivers.length > 0) {
|
|
1664
|
-
const receiverTask = this.receivers.shift()
|
|
1665
|
-
queueMicrotask(() => receiverTask.resolveReceive(value))
|
|
1666
|
-
return { value: true, ok: true, id }
|
|
1929
|
+
const receiverTask = this.receivers.shift()!
|
|
1930
|
+
queueMicrotask(() => receiverTask.resolveReceive(value))
|
|
1931
|
+
return { value: true, ok: true, id }
|
|
1667
1932
|
}
|
|
1668
1933
|
if (this.receiversWithOk.length > 0) {
|
|
1669
|
-
const receiverTask = this.receiversWithOk.shift()
|
|
1670
|
-
queueMicrotask(() => receiverTask.resolveReceive({ value, ok: true }))
|
|
1671
|
-
return { value: true, ok: true, id }
|
|
1934
|
+
const receiverTask = this.receiversWithOk.shift()!
|
|
1935
|
+
queueMicrotask(() => receiverTask.resolveReceive({ value, ok: true }))
|
|
1936
|
+
return { value: true, ok: true, id }
|
|
1672
1937
|
}
|
|
1673
1938
|
|
|
1674
1939
|
if (this.buffer.length < this.capacity) {
|
|
1675
|
-
this.buffer.push(value)
|
|
1676
|
-
return { value: true, ok: true, id }
|
|
1940
|
+
this.buffer.push(value)
|
|
1941
|
+
return { value: true, ok: true, id }
|
|
1677
1942
|
}
|
|
1678
1943
|
|
|
1679
1944
|
return new Promise<SelectResult<boolean>>((resolve, reject) => {
|
|
@@ -1681,48 +1946,54 @@ class BufferedChannel<T> implements Channel<T> {
|
|
|
1681
1946
|
value,
|
|
1682
1947
|
resolveSend: () => resolve({ value: true, ok: true, id }),
|
|
1683
1948
|
rejectSend: (e) => reject(e), // Propagate error if channel closes
|
|
1684
|
-
})
|
|
1685
|
-
})
|
|
1949
|
+
})
|
|
1950
|
+
})
|
|
1686
1951
|
}
|
|
1687
1952
|
|
|
1688
1953
|
close(): void {
|
|
1689
1954
|
if (this.closed) {
|
|
1690
|
-
throw new Error('close of closed channel')
|
|
1955
|
+
throw new Error('close of closed channel')
|
|
1691
1956
|
}
|
|
1692
|
-
this.closed = true
|
|
1957
|
+
this.closed = true
|
|
1693
1958
|
|
|
1694
|
-
const sendersToNotify = [...this.senders]
|
|
1695
|
-
this.senders = []
|
|
1959
|
+
const sendersToNotify = [...this.senders] // Shallow copy for iteration
|
|
1960
|
+
this.senders = []
|
|
1696
1961
|
for (const senderTask of sendersToNotify) {
|
|
1697
|
-
queueMicrotask(() =>
|
|
1962
|
+
queueMicrotask(() =>
|
|
1963
|
+
senderTask.rejectSend(new Error('send on closed channel')),
|
|
1964
|
+
)
|
|
1698
1965
|
}
|
|
1699
1966
|
|
|
1700
|
-
const receiversToNotify = [...this.receivers]
|
|
1701
|
-
this.receivers = []
|
|
1967
|
+
const receiversToNotify = [...this.receivers]
|
|
1968
|
+
this.receivers = []
|
|
1702
1969
|
for (const receiverTask of receiversToNotify) {
|
|
1703
|
-
queueMicrotask(() =>
|
|
1970
|
+
queueMicrotask(() =>
|
|
1971
|
+
receiverTask.rejectReceive(new Error('receive on closed channel')),
|
|
1972
|
+
)
|
|
1704
1973
|
}
|
|
1705
1974
|
|
|
1706
|
-
const receiversWithOkToNotify = [...this.receiversWithOk]
|
|
1707
|
-
this.receiversWithOk = []
|
|
1975
|
+
const receiversWithOkToNotify = [...this.receiversWithOk]
|
|
1976
|
+
this.receiversWithOk = []
|
|
1708
1977
|
for (const receiverTask of receiversWithOkToNotify) {
|
|
1709
|
-
queueMicrotask(() =>
|
|
1978
|
+
queueMicrotask(() =>
|
|
1979
|
+
receiverTask.resolveReceive({ value: this.zeroValue, ok: false }),
|
|
1980
|
+
)
|
|
1710
1981
|
}
|
|
1711
1982
|
}
|
|
1712
1983
|
|
|
1713
1984
|
canReceiveNonBlocking(): boolean {
|
|
1714
|
-
return this.buffer.length > 0 || this.senders.length > 0 || this.closed
|
|
1985
|
+
return this.buffer.length > 0 || this.senders.length > 0 || this.closed
|
|
1715
1986
|
}
|
|
1716
1987
|
|
|
1717
1988
|
canSendNonBlocking(): boolean {
|
|
1718
1989
|
if (this.closed) {
|
|
1719
|
-
return true
|
|
1990
|
+
return true // Ready to panic
|
|
1720
1991
|
}
|
|
1721
1992
|
return (
|
|
1722
1993
|
this.buffer.length < this.capacity ||
|
|
1723
1994
|
this.receivers.length > 0 ||
|
|
1724
1995
|
this.receiversWithOk.length > 0
|
|
1725
|
-
)
|
|
1996
|
+
)
|
|
1726
1997
|
}
|
|
1727
1998
|
}
|
|
1728
1999
|
|