From 51fddcb044a18b291674721c792b7477923eb816 Mon Sep 17 00:00:00 2001 From: Chris O'Hara Date: Sat, 18 Nov 2023 08:26:46 +1000 Subject: [PATCH 1/3] Add a Function message --- gen/proto/go/coroutine/v1/coroutine.pb.go | 96 ++++-- .../go/coroutine/v1/coroutine_vtproto.pb.go | 52 +++ gen/proto/go/coroutine/v1/function.pb.go | 177 ++++++++++ .../go/coroutine/v1/function_vtproto.pb.go | 313 ++++++++++++++++++ gen/proto/go/coroutine/v1/type_vtproto.pb.go | 103 ------ proto/coroutine/v1/coroutine.proto | 12 + proto/coroutine/v1/function.proto | 16 + 7 files changed, 630 insertions(+), 139 deletions(-) create mode 100644 gen/proto/go/coroutine/v1/function.pb.go create mode 100644 gen/proto/go/coroutine/v1/function_vtproto.pb.go create mode 100644 proto/coroutine/v1/function.proto diff --git a/gen/proto/go/coroutine/v1/coroutine.pb.go b/gen/proto/go/coroutine/v1/coroutine.pb.go index 561aec0..5046d97 100644 --- a/gen/proto/go/coroutine/v1/coroutine.pb.go +++ b/gen/proto/go/coroutine/v1/coroutine.pb.go @@ -26,9 +26,17 @@ type State struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Build *Build `protobuf:"bytes,1,opt,name=build,proto3" json:"build,omitempty"` - State []byte `protobuf:"bytes,2,opt,name=state,proto3" json:"state,omitempty"` + // Build is information about the build in which the coruotine state + // was generated. + Build *Build `protobuf:"bytes,1,opt,name=build,proto3" json:"build,omitempty"` + // State is a serialized representation of the objects in the program + // that are reachable from the coroutine stack. + State []byte `protobuf:"bytes,2,opt,name=state,proto3" json:"state,omitempty"` + // Types is the set of types used by the object graph. Types []*Type `protobuf:"bytes,3,rep,name=types,proto3" json:"types,omitempty"` + // Functions is the set of functions, methods and closures referenced + // by the object graph. + Functions []*Function `protobuf:"bytes,4,rep,name=functions,proto3" json:"functions,omitempty"` } func (x *State) Reset() { @@ -84,6 +92,13 @@ func (x *State) GetTypes() []*Type { return nil } +func (x *State) GetFunctions() []*Function { + if x != nil { + return x.Functions + } + return nil +} + // Build is info about the build in which a durable coroutine // is/was running. type Build struct { @@ -154,32 +169,38 @@ var File_coroutine_v1_coroutine_proto protoreflect.FileDescriptor var file_coroutine_v1_coroutine_proto_rawDesc = []byte{ 0x0a, 0x1c, 0x63, 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, - 0x63, 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x1a, 0x17, 0x63, 0x6f, - 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x72, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x29, - 0x0a, 0x05, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, - 0x63, 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x69, - 0x6c, 0x64, 0x52, 0x05, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, - 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, - 0x28, 0x0a, 0x05, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, - 0x2e, 0x63, 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x79, - 0x70, 0x65, 0x52, 0x05, 0x74, 0x79, 0x70, 0x65, 0x73, 0x22, 0x3b, 0x0a, 0x05, 0x42, 0x75, 0x69, - 0x6c, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, - 0x69, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x6f, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, - 0x6f, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x72, 0x63, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x61, 0x72, 0x63, 0x68, 0x42, 0xbd, 0x01, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x63, - 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x42, 0x0e, 0x43, 0x6f, 0x72, - 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x48, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x74, 0x65, 0x61, 0x6c, 0x74, - 0x68, 0x72, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2f, 0x63, 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, - 0x65, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x63, - 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x63, 0x6f, 0x72, 0x6f, - 0x75, 0x74, 0x69, 0x6e, 0x65, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x43, 0x58, 0x58, 0xaa, 0x02, 0x0c, - 0x43, 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0c, 0x43, - 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x18, 0x43, 0x6f, - 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0d, 0x43, 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, - 0x6e, 0x65, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x63, 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x1a, 0x1b, 0x63, 0x6f, + 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x17, 0x63, 0x6f, 0x72, 0x6f, 0x75, + 0x74, 0x69, 0x6e, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x22, 0xa8, 0x01, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x29, 0x0a, 0x05, + 0x62, 0x75, 0x69, 0x6c, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x6f, + 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, + 0x52, 0x05, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x28, 0x0a, + 0x05, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x63, + 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x79, 0x70, 0x65, + 0x52, 0x05, 0x74, 0x79, 0x70, 0x65, 0x73, 0x12, 0x34, 0x0a, 0x09, 0x66, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x6f, 0x72, + 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x09, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x3b, 0x0a, + 0x05, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x6f, 0x73, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x02, 0x6f, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x72, 0x63, 0x68, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x72, 0x63, 0x68, 0x42, 0xbd, 0x01, 0x0a, 0x10, 0x63, + 0x6f, 0x6d, 0x2e, 0x63, 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x42, + 0x0e, 0x43, 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, + 0x01, 0x5a, 0x48, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x74, + 0x65, 0x61, 0x6c, 0x74, 0x68, 0x72, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2f, 0x63, 0x6f, 0x72, 0x6f, + 0x75, 0x74, 0x69, 0x6e, 0x65, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, + 0x67, 0x6f, 0x2f, 0x63, 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x2f, 0x76, 0x31, 0x3b, + 0x63, 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x43, 0x58, + 0x58, 0xaa, 0x02, 0x0c, 0x43, 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x2e, 0x56, 0x31, + 0xca, 0x02, 0x0c, 0x43, 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x5c, 0x56, 0x31, 0xe2, + 0x02, 0x18, 0x43, 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x5c, 0x56, 0x31, 0x5c, 0x47, + 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0d, 0x43, 0x6f, 0x72, + 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( @@ -196,18 +217,20 @@ func file_coroutine_v1_coroutine_proto_rawDescGZIP() []byte { var file_coroutine_v1_coroutine_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_coroutine_v1_coroutine_proto_goTypes = []interface{}{ - (*State)(nil), // 0: coroutine.v1.State - (*Build)(nil), // 1: coroutine.v1.Build - (*Type)(nil), // 2: coroutine.v1.Type + (*State)(nil), // 0: coroutine.v1.State + (*Build)(nil), // 1: coroutine.v1.Build + (*Type)(nil), // 2: coroutine.v1.Type + (*Function)(nil), // 3: coroutine.v1.Function } var file_coroutine_v1_coroutine_proto_depIdxs = []int32{ 1, // 0: coroutine.v1.State.build:type_name -> coroutine.v1.Build 2, // 1: coroutine.v1.State.types:type_name -> coroutine.v1.Type - 2, // [2:2] is the sub-list for method output_type - 2, // [2:2] is the sub-list for method input_type - 2, // [2:2] is the sub-list for extension type_name - 2, // [2:2] is the sub-list for extension extendee - 0, // [0:2] is the sub-list for field type_name + 3, // 2: coroutine.v1.State.functions:type_name -> coroutine.v1.Function + 3, // [3:3] is the sub-list for method output_type + 3, // [3:3] is the sub-list for method input_type + 3, // [3:3] is the sub-list for extension type_name + 3, // [3:3] is the sub-list for extension extendee + 0, // [0:3] is the sub-list for field type_name } func init() { file_coroutine_v1_coroutine_proto_init() } @@ -215,6 +238,7 @@ func file_coroutine_v1_coroutine_proto_init() { if File_coroutine_v1_coroutine_proto != nil { return } + file_coroutine_v1_function_proto_init() file_coroutine_v1_type_proto_init() if !protoimpl.UnsafeEnabled { file_coroutine_v1_coroutine_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { diff --git a/gen/proto/go/coroutine/v1/coroutine_vtproto.pb.go b/gen/proto/go/coroutine/v1/coroutine_vtproto.pb.go index 5c102b4..985cb08 100644 --- a/gen/proto/go/coroutine/v1/coroutine_vtproto.pb.go +++ b/gen/proto/go/coroutine/v1/coroutine_vtproto.pb.go @@ -47,6 +47,18 @@ func (m *State) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } + if len(m.Functions) > 0 { + for iNdEx := len(m.Functions) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.Functions[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x22 + } + } if len(m.Types) > 0 { for iNdEx := len(m.Types) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Types[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) @@ -153,6 +165,12 @@ func (m *State) SizeVT() (n int) { n += 1 + l + sov(uint64(l)) } } + if len(m.Functions) > 0 { + for _, e := range m.Functions { + l = e.SizeVT() + n += 1 + l + sov(uint64(l)) + } + } n += len(m.unknownFields) return n } @@ -312,6 +330,40 @@ func (m *State) UnmarshalVT(dAtA []byte) error { return err } iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Functions", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Functions = append(m.Functions, &Function{}) + if err := m.Functions[len(m.Functions)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skip(dAtA[iNdEx:]) diff --git a/gen/proto/go/coroutine/v1/function.pb.go b/gen/proto/go/coroutine/v1/function.pb.go new file mode 100644 index 0000000..a070939 --- /dev/null +++ b/gen/proto/go/coroutine/v1/function.pb.go @@ -0,0 +1,177 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.31.0 +// protoc (unknown) +// source: coroutine/v1/function.proto + +package coroutinev1 + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// Function represents a function, method or closure in the program. +type Function struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Name is the name of the function. + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // Type is an identifier for the function's type. + Type int32 `protobuf:"varint,2,opt,name=type,proto3" json:"type,omitempty"` + // Closure is an identifier for a struct type that represents the + // memory layout of the closure. + Closure int32 `protobuf:"varint,3,opt,name=closure,proto3" json:"closure,omitempty"` +} + +func (x *Function) Reset() { + *x = Function{} + if protoimpl.UnsafeEnabled { + mi := &file_coroutine_v1_function_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Function) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Function) ProtoMessage() {} + +func (x *Function) ProtoReflect() protoreflect.Message { + mi := &file_coroutine_v1_function_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Function.ProtoReflect.Descriptor instead. +func (*Function) Descriptor() ([]byte, []int) { + return file_coroutine_v1_function_proto_rawDescGZIP(), []int{0} +} + +func (x *Function) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Function) GetType() int32 { + if x != nil { + return x.Type + } + return 0 +} + +func (x *Function) GetClosure() int32 { + if x != nil { + return x.Closure + } + return 0 +} + +var File_coroutine_v1_function_proto protoreflect.FileDescriptor + +var file_coroutine_v1_function_proto_rawDesc = []byte{ + 0x0a, 0x1b, 0x63, 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, 0x63, + 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x22, 0x4c, 0x0a, 0x08, 0x46, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, + 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, + 0x18, 0x0a, 0x07, 0x63, 0x6c, 0x6f, 0x73, 0x75, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x07, 0x63, 0x6c, 0x6f, 0x73, 0x75, 0x72, 0x65, 0x42, 0xbc, 0x01, 0x0a, 0x10, 0x63, 0x6f, + 0x6d, 0x2e, 0x63, 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x42, 0x0d, + 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, + 0x48, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x74, 0x65, 0x61, + 0x6c, 0x74, 0x68, 0x72, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x2f, 0x63, 0x6f, 0x72, 0x6f, 0x75, 0x74, + 0x69, 0x6e, 0x65, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, + 0x2f, 0x63, 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x63, 0x6f, + 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x43, 0x58, 0x58, 0xaa, + 0x02, 0x0c, 0x43, 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x2e, 0x56, 0x31, 0xca, 0x02, + 0x0c, 0x43, 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x18, + 0x43, 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, + 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0d, 0x43, 0x6f, 0x72, 0x6f, 0x75, + 0x74, 0x69, 0x6e, 0x65, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_coroutine_v1_function_proto_rawDescOnce sync.Once + file_coroutine_v1_function_proto_rawDescData = file_coroutine_v1_function_proto_rawDesc +) + +func file_coroutine_v1_function_proto_rawDescGZIP() []byte { + file_coroutine_v1_function_proto_rawDescOnce.Do(func() { + file_coroutine_v1_function_proto_rawDescData = protoimpl.X.CompressGZIP(file_coroutine_v1_function_proto_rawDescData) + }) + return file_coroutine_v1_function_proto_rawDescData +} + +var file_coroutine_v1_function_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_coroutine_v1_function_proto_goTypes = []interface{}{ + (*Function)(nil), // 0: coroutine.v1.Function +} +var file_coroutine_v1_function_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_coroutine_v1_function_proto_init() } +func file_coroutine_v1_function_proto_init() { + if File_coroutine_v1_function_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_coroutine_v1_function_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Function); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_coroutine_v1_function_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_coroutine_v1_function_proto_goTypes, + DependencyIndexes: file_coroutine_v1_function_proto_depIdxs, + MessageInfos: file_coroutine_v1_function_proto_msgTypes, + }.Build() + File_coroutine_v1_function_proto = out.File + file_coroutine_v1_function_proto_rawDesc = nil + file_coroutine_v1_function_proto_goTypes = nil + file_coroutine_v1_function_proto_depIdxs = nil +} diff --git a/gen/proto/go/coroutine/v1/function_vtproto.pb.go b/gen/proto/go/coroutine/v1/function_vtproto.pb.go new file mode 100644 index 0000000..74346ae --- /dev/null +++ b/gen/proto/go/coroutine/v1/function_vtproto.pb.go @@ -0,0 +1,313 @@ +// Code generated by protoc-gen-go-vtproto. DO NOT EDIT. +// protoc-gen-go-vtproto version: v0.5.0 +// source: coroutine/v1/function.proto + +package coroutinev1 + +import ( + fmt "fmt" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + io "io" + bits "math/bits" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +func (m *Function) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Function) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Function) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Closure != 0 { + i = encodeVarint(dAtA, i, uint64(m.Closure)) + i-- + dAtA[i] = 0x18 + } + if m.Type != 0 { + i = encodeVarint(dAtA, i, uint64(m.Type)) + i-- + dAtA[i] = 0x10 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarint(dAtA []byte, offset int, v uint64) int { + offset -= sov(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Function) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sov(uint64(l)) + } + if m.Type != 0 { + n += 1 + sov(uint64(m.Type)) + } + if m.Closure != 0 { + n += 1 + sov(uint64(m.Closure)) + } + n += len(m.unknownFields) + return n +} + +func sov(x uint64) (n int) { + return (bits.Len64(x|1) + 6) / 7 +} +func soz(x uint64) (n int) { + return sov(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Function) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Function: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Function: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + m.Type = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Type |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Closure", wireType) + } + m.Closure = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Closure |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} + +func skip(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflow + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflow + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflow + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLength + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroup + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLength + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLength = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflow = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroup = fmt.Errorf("proto: unexpected end of group") +) diff --git a/gen/proto/go/coroutine/v1/type_vtproto.pb.go b/gen/proto/go/coroutine/v1/type_vtproto.pb.go index 3e7de17..bdbb2d1 100644 --- a/gen/proto/go/coroutine/v1/type_vtproto.pb.go +++ b/gen/proto/go/coroutine/v1/type_vtproto.pb.go @@ -8,7 +8,6 @@ import ( fmt "fmt" protoimpl "google.golang.org/protobuf/runtime/protoimpl" io "io" - bits "math/bits" ) const ( @@ -264,17 +263,6 @@ func (m *Field) MarshalToSizedBufferVT(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func encodeVarint(dAtA []byte, offset int, v uint64) int { - offset -= sov(v) - base := offset - for v >= 1<<7 { - dAtA[offset] = uint8(v&0x7f | 0x80) - v >>= 7 - offset++ - } - dAtA[offset] = uint8(v) - return base -} func (m *Type) SizeVT() (n int) { if m == nil { return 0 @@ -375,12 +363,6 @@ func (m *Field) SizeVT() (n int) { return n } -func sov(x uint64) (n int) { - return (bits.Len64(x|1) + 6) / 7 -} -func soz(x uint64) (n int) { - return sov(uint64((x << 1) ^ uint64((int64(x) >> 63)))) -} func (m *Type) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -1117,88 +1099,3 @@ func (m *Field) UnmarshalVT(dAtA []byte) error { } return nil } - -func skip(dAtA []byte) (n int, err error) { - l := len(dAtA) - iNdEx := 0 - depth := 0 - for iNdEx < l { - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflow - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - wireType := int(wire & 0x7) - switch wireType { - case 0: - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflow - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - iNdEx++ - if dAtA[iNdEx-1] < 0x80 { - break - } - } - case 1: - iNdEx += 8 - case 2: - var length int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflow - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - length |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if length < 0 { - return 0, ErrInvalidLength - } - iNdEx += length - case 3: - depth++ - case 4: - if depth == 0 { - return 0, ErrUnexpectedEndOfGroup - } - depth-- - case 5: - iNdEx += 4 - default: - return 0, fmt.Errorf("proto: illegal wireType %d", wireType) - } - if iNdEx < 0 { - return 0, ErrInvalidLength - } - if depth == 0 { - return iNdEx, nil - } - } - return 0, io.ErrUnexpectedEOF -} - -var ( - ErrInvalidLength = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflow = fmt.Errorf("proto: integer overflow") - ErrUnexpectedEndOfGroup = fmt.Errorf("proto: unexpected end of group") -) diff --git a/proto/coroutine/v1/coroutine.proto b/proto/coroutine/v1/coroutine.proto index 5f8d1c8..d4d6cad 100644 --- a/proto/coroutine/v1/coroutine.proto +++ b/proto/coroutine/v1/coroutine.proto @@ -2,13 +2,25 @@ syntax = "proto3"; package coroutine.v1; +import "coroutine/v1/function.proto"; import "coroutine/v1/type.proto"; // State is durable coroutine state. message State { + // Build is information about the build in which the coruotine state + // was generated. Build build = 1; + + // State is a serialized representation of the objects in the program + // that are reachable from the coroutine stack. bytes state = 2; + + // Types is the set of types used by the object graph. repeated Type types = 3; + + // Functions is the set of functions, methods and closures referenced + // by the object graph. + repeated Function functions = 4; } // Build is info about the build in which a durable coroutine diff --git a/proto/coroutine/v1/function.proto b/proto/coroutine/v1/function.proto new file mode 100644 index 0000000..4b6aba7 --- /dev/null +++ b/proto/coroutine/v1/function.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; + +package coroutine.v1; + +// Function represents a function, method or closure in the program. +message Function { + // Name is the name of the function. + string name = 1; + + // Type is an identifier for the function's type. + int32 type = 2; + + // Closure is an identifier for a struct type that represents the + // memory layout of the closure. + int32 closure = 3; +} From 9776dd21a0d7a141a8150d52f3828bed7d4dca31 Mon Sep 17 00:00:00 2001 From: Chris O'Hara Date: Sat, 18 Nov 2023 08:45:08 +1000 Subject: [PATCH 2/3] Store the set of functions, along with type info, separate from object graph --- types/reflect.go | 38 +++++++++++----------------- types/serde.go | 22 +++++++++++------ types/types.go | 64 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+), 31 deletions(-) diff --git a/types/reflect.go b/types/reflect.go index f178193..504624c 100644 --- a/types/reflect.go +++ b/types/reflect.go @@ -614,47 +614,37 @@ func serializeFunc(s *Serializer, t reflect.Type, p unsafe.Pointer) { // memory location starting with the address of the function, hence the // double indirection here. p = *(*unsafe.Pointer)(p) - if p == nil { // nil function value - serializeBool(s, false) + if p == nil { + // Function IDs start at 1; use 0 to represent nil ptr. + serializeVarint(s, 0) return } - serializeBool(s, true) addr := *(*uintptr)(p) - fn := FuncByAddr(addr) - if fn == nil { - panic(fmt.Sprintf("function not found at address %v", addr)) - } - serializeString(s, &fn.Name) + id, closure := s.funcs.RegisterAddr(addr) + serializeVarint(s, int(id)) - if fn.Closure != nil { - t := fn.Closure - serializeStructFields(s, p, t.NumField()-1, func(i int) reflect.StructField { - return t.Field(i + 1) + if closure != nil { + // Skip the first field, which is the function ptr. + serializeStructFields(s, p, closure.NumField()-1, func(i int) reflect.StructField { + return closure.Field(i + 1) }) } } func deserializeFunc(d *Deserializer, t reflect.Type, p unsafe.Pointer) { - var ok bool - deserializeBool(d, &ok) - if !ok { + id := deserializeVarint(d) + if id == 0 { *(**Func)(p) = nil return } - var name string - deserializeString(d, &name) - - fn := FuncByName(name) - if fn == nil { - panic(name + ": function symbol not found in the program") - } + fn := d.funcs.ToFunc(funcid(id)) if fn.Type == nil { - panic(name + ": function type is missing") + panic(fn.Name + ": function type is missing") } if !t.AssignableTo(fn.Type) { - panic(name + ": function type mismatch: " + fn.Type.String() + " != " + t.String()) + panic(fn.Name + ": function type mismatch: " + fn.Type.String() + " != " + t.String()) } if fn.Closure != nil { diff --git a/types/serde.go b/types/serde.go index 2a6d86f..e3adfdb 100644 --- a/types/serde.go +++ b/types/serde.go @@ -53,9 +53,10 @@ func Serialize(x any) ([]byte, error) { serializeAny(s, t, p) state := &coroutinev1.State{ - State: s.b, - Build: buildInfo, - Types: s.types.types, + State: s.b, + Build: buildInfo, + Types: s.types.types, + Functions: s.funcs.funcs, } return state.MarshalVT() } @@ -70,7 +71,7 @@ func Deserialize(b []byte) (interface{}, error) { return nil, fmt.Errorf("%w: got %v, expect %v", ErrBuildIDMismatch, state.Build.Id, buildInfo.Id) } - d := newDeserializer(state.State, state.Types) + d := newDeserializer(state.State, state.Types, state.Functions) var x interface{} px := &x t := reflect.TypeOf(px).Elem() @@ -85,6 +86,7 @@ func Deserialize(b []byte) (interface{}, error) { type Deserializer struct { serdes *serdemap types *typemap + funcs *funcmap // TODO: make it a slice since pointer ids is the sequence of integers // starting at 1. @@ -94,10 +96,12 @@ type Deserializer struct { b []byte } -func newDeserializer(b []byte, types []*coroutinev1.Type) *Deserializer { +func newDeserializer(b []byte, ctypes []*coroutinev1.Type, cfuncs []*coroutinev1.Function) *Deserializer { + types := newTypeMap(serdes, ctypes) return &Deserializer{ serdes: serdes, - types: newTypeMap(serdes, types), + types: types, + funcs: newFuncMap(types, cfuncs), ptrs: make(map[sID]unsafe.Pointer), b: b, } @@ -156,6 +160,7 @@ func (d *Deserializer) store(i sID, p unsafe.Pointer) { type Serializer struct { serdes *serdemap types *typemap + funcs *funcmap ptrs map[unsafe.Pointer]sID containers containers @@ -167,9 +172,12 @@ type Serializer struct { } func newSerializer() *Serializer { + types := newTypeMap(serdes, nil) + return &Serializer{ serdes: serdes, - types: newTypeMap(serdes, nil), + types: types, + funcs: newFuncMap(types, nil), ptrs: make(map[unsafe.Pointer]sID), scanptrs: make(map[reflect.Value]struct{}), b: make([]byte, 0, 128), diff --git a/types/types.go b/types/types.go index 0f24cea..1d42da0 100644 --- a/types/types.go +++ b/types/types.go @@ -336,6 +336,70 @@ func (m *typemap) ToType(t reflect.Type) typeid { return id } +type funcid = uint32 + +type funcmap struct { + types *typemap + + funcs []*coroutinev1.Function + cache doublemap[typeid, *Func] +} + +func newFuncMap(types *typemap, funcs []*coroutinev1.Function) *funcmap { + return &funcmap{ + types: types, + funcs: funcs, + } +} + +func (m *funcmap) register(f *coroutinev1.Function) typeid { + m.funcs = append(m.funcs, f) + id := funcid(len(m.funcs)) // note that IDs start at 1 + return id +} + +func (m *funcmap) lookup(id funcid) *coroutinev1.Function { + if id == 0 || id > uint32(len(m.funcs)) { + return nil + } + return m.funcs[id-1] +} + +func (m *funcmap) ToFunc(id funcid) *Func { + if x, ok := m.cache.getK(id); ok { + return x + } + cf := m.lookup(id) + if cf == nil { + panic(fmt.Sprintf("function ID %d not found", id)) + } + f := FuncByName(cf.Name) + if f == nil { + panic(fmt.Sprintf("function %s not found", cf.Name)) + } + return f +} + +func (m *funcmap) RegisterAddr(addr uintptr) (id funcid, closureType reflect.Type) { + f := FuncByAddr(addr) + if f == nil { + panic(fmt.Sprintf("function not found at address %v", addr)) + } + + var closureTypeID typeid + if f.Closure != nil { + closureTypeID = m.types.ToType(f.Closure) + } + + id = m.register(&coroutinev1.Function{ + Name: f.Name, + Type: int32(m.types.ToType(f.Type)), + Closure: int32(closureTypeID), + }) + + return id, f.Closure +} + type doublemap[K, V comparable] struct { fromK map[K]V fromV map[V]K From b819413e743de5402fdf0bbbed55428b1eb5dc7b Mon Sep 17 00:00:00 2001 From: Chris O'Hara Date: Sat, 18 Nov 2023 08:57:15 +1000 Subject: [PATCH 3/3] Make the casting/indirection clearer --- types/reflect.go | 18 +++++++++--------- types/types.go | 4 ++-- types/unsafe.go | 6 ++++++ 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/types/reflect.go b/types/reflect.go index 504624c..58ebde3 100644 --- a/types/reflect.go +++ b/types/reflect.go @@ -610,21 +610,18 @@ func deserializeStructFields(d *Deserializer, p unsafe.Pointer, n int, field fun } func serializeFunc(s *Serializer, t reflect.Type, p unsafe.Pointer) { - // p is a pointer to a function value, function values are pointers to a - // memory location starting with the address of the function, hence the - // double indirection here. - p = *(*unsafe.Pointer)(p) - if p == nil { + fn := *(**function)(p) + if fn == nil { // Function IDs start at 1; use 0 to represent nil ptr. serializeVarint(s, 0) return } - addr := *(*uintptr)(p) - id, closure := s.funcs.RegisterAddr(addr) + id, closure := s.funcs.RegisterAddr(fn.addr) serializeVarint(s, int(id)) if closure != nil { + p = unsafe.Pointer(fn) // Skip the first field, which is the function ptr. serializeStructFields(s, p, closure.NumField()-1, func(i int) reflect.StructField { return closure.Field(i + 1) @@ -635,7 +632,7 @@ func serializeFunc(s *Serializer, t reflect.Type, p unsafe.Pointer) { func deserializeFunc(d *Deserializer, t reflect.Type, p unsafe.Pointer) { id := deserializeVarint(d) if id == 0 { - *(**Func)(p) = nil + *(**function)(p) = nil return } @@ -660,7 +657,10 @@ func deserializeFunc(d *Deserializer, t reflect.Type, p unsafe.Pointer) { *(*unsafe.Pointer)(p) = closure } else { - *(**Func)(p) = fn + // Avoid an allocation by storing a pointer to the immutable Func. + // This works because the addr is the first element in both the Func + // and function structs. + *(**function)(p) = (*function)(unsafe.Pointer(fn)) } } diff --git a/types/types.go b/types/types.go index 1d42da0..3f29b31 100644 --- a/types/types.go +++ b/types/types.go @@ -380,8 +380,8 @@ func (m *funcmap) ToFunc(id funcid) *Func { return f } -func (m *funcmap) RegisterAddr(addr uintptr) (id funcid, closureType reflect.Type) { - f := FuncByAddr(addr) +func (m *funcmap) RegisterAddr(addr unsafe.Pointer) (id funcid, closureType reflect.Type) { + f := FuncByAddr(uintptr(addr)) if f == nil { panic(fmt.Sprintf("function not found at address %v", addr)) } diff --git a/types/unsafe.go b/types/unsafe.go index 3213809..b100a5b 100644 --- a/types/unsafe.go +++ b/types/unsafe.go @@ -19,6 +19,12 @@ type slice struct { cap int } +// Used for unsafe access to a function pointer and closure vars. +type function struct { + addr unsafe.Pointer + // closure vars follow... +} + // returns true iff type t would be inlined in an interface. func inlined(t reflect.Type) bool { switch t.Kind() {