From fd02618c2f7e4454ad5cfb593d0bb7cc07dd94f1 Mon Sep 17 00:00:00 2001 From: Egor Aristov Date: Sat, 6 Dec 2025 11:16:38 +0300 Subject: [PATCH] continue work on supporting buffers --- example/golang/main.go | 14 ++++++++++++-- example/golang/remote.go | 20 +++++++++++++++++--- example/ts/src/index.ts | 13 +++++++++++++ example/ts/src/remote.ts | 7 ++++++- kitcom/internal/golang/gogen.go | 12 ++++++++++-- kitcom/internal/golang/gogen.tmpl | 2 +- kitcom/internal/ts/tsgen.tmpl | 13 +++++++++---- lib/golang/lib.go | 7 ++++--- lib/ts/src/lib.ts | 10 +++++++--- 9 files changed, 79 insertions(+), 19 deletions(-) diff --git a/example/golang/main.go b/example/golang/main.go index 6c4a12a..1934a2e 100644 --- a/example/golang/main.go +++ b/example/golang/main.go @@ -6,6 +6,7 @@ import ( "os" "os/exec" "path" + "slices" "time" kittenipc "efprojects.com/kitten-ipc" @@ -56,11 +57,20 @@ func main() { } remoteApi := TsIpcApi{Ipc: ipc} - res, err := remoteApi.Div(10, 2) + resDiv, err := remoteApi.Div(10, 2) if err != nil { log.Panic(err) } - log.Printf("call result go->ts Div = %v", res) + log.Printf("call result go->ts Div = %v", resDiv) + + data1 := slices.Repeat([]byte{0b10101010}, 10) + data2 := slices.Repeat([]byte{0b11110000}, 10) + + resXor, err := remoteApi.XorData(data1, data2) + if err != nil { + log.Panic(err) + } + log.Printf("call result go->ts XorData = %v", resXor) if err := ipc.Wait(1 * time.Second); err != nil { log.Panic(err) diff --git a/example/golang/remote.go b/example/golang/remote.go index d929578..3dc744e 100644 --- a/example/golang/remote.go +++ b/example/golang/remote.go @@ -5,21 +5,35 @@ package main import ( kittenipc "efprojects.com/kitten-ipc" "fmt" + "reflect" ) type TsIpcApi struct { - Ipc kittenipc.Callable + Ipc kittenipc.IpcCommon } -func (t *TsIpcApi) Div( +func (self *TsIpcApi) Div( a int, b int, ) ( int, error, ) { - results, err := t.Ipc.Call("TsIpcApi.Div", a, b) + results, err := self.Ipc.Call("TsIpcApi.Div", a, b) if err != nil { return 0, fmt.Errorf("call to TsIpcApi.Div failed: %w", err) } _ = results return int(results[0].(float64)), nil } + +func (self *TsIpcApi) XorData( + data1 []byte, data2 []byte, +) ( + []byte, error, +) { + results, err := self.Ipc.Call("TsIpcApi.XorData", data1, data2) + if err != nil { + return []byte{}, fmt.Errorf("call to TsIpcApi.XorData failed: %w", err) + } + _ = results + return self.Ipc.ConvType(reflect.TypeOf([]byte{}), reflect.TypeOf(""), results[0]).([]byte), nil +} diff --git a/example/ts/src/index.ts b/example/ts/src/index.ts index c8f93cb..055b2aa 100644 --- a/example/ts/src/index.ts +++ b/example/ts/src/index.ts @@ -11,6 +11,19 @@ class TsIpcApi { } return a / b; } + XorData(data1: Buffer, data2: Buffer): Buffer { + if (data1.length === 0 || data2.length === 0) { + throw new Error('empty input data'); + } + if (data1.length !== data2.length) { + throw new Error('input data length mismatch'); + } + const result = Buffer.alloc(data1.length); + for (let i = 0; i < data1.length; i++) { + result[i] = data1[i]! ^ data2[i]!; + } + return result; + } } async function main() { diff --git a/example/ts/src/remote.ts b/example/ts/src/remote.ts index 11cbc8e..4a499de 100644 --- a/example/ts/src/remote.ts +++ b/example/ts/src/remote.ts @@ -10,13 +10,18 @@ export default class GoIpcApi { } async Div(a: number, b: number): Promise { + a = this.ipc.convType(a, "number"); + b = this.ipc.convType(b, "number"); const results = await this.ipc.call("GoIpcApi.Div", a, b); + results[0] = this.ipc.convType(results[0], "number"); return results[0] as number; } async XorData(data1: Buffer, data2: Buffer): Promise { + data1 = this.ipc.convType(data1, "Buffer"); + data2 = this.ipc.convType(data2, "Buffer"); const results = await this.ipc.call("GoIpcApi.XorData", data1, data2); - results[0] = Buffer.from(results[0], "base64"); + results[0] = this.ipc.convType(results[0], "Buffer"); return results[0] as Buffer; } } diff --git a/kitcom/internal/golang/gogen.go b/kitcom/internal/golang/gogen.go index beda26f..c66ffed 100644 --- a/kitcom/internal/golang/gogen.go +++ b/kitcom/internal/golang/gogen.go @@ -5,7 +5,6 @@ import ( "fmt" "go/format" "os" - "strings" "text/template" _ "embed" @@ -34,16 +33,19 @@ func (g *GoApiGenerator) Generate(apis *api.Api, destFile string) error { Api: apis, } + const defaultReceiver = "self" + tpl := template.New("gogen") tpl = tpl.Funcs(map[string]any{ "receiver": func(name string) string { - return strings.ToLower(name)[0:1] + return defaultReceiver }, "typedef": func(t api.ValType) (string, error) { td, ok := map[api.ValType]string{ api.TInt: "int", api.TString: "string", api.TBool: "bool", + api.TBlob: "[]byte", }[t] if !ok { return "", fmt.Errorf("cannot generate type %v", t) @@ -55,6 +57,11 @@ func (g *GoApiGenerator) Generate(apis *api.Api, destFile string) error { api.TInt: fmt.Sprintf("int(%s.(float64))", valDef), api.TString: fmt.Sprintf("%s.(string)", valDef), api.TBool: fmt.Sprintf("%s.(bool)", valDef), + api.TBlob: fmt.Sprintf( + "%s.Ipc.ConvType(reflect.TypeOf([]byte{}), reflect.TypeOf(\"\"), %s).([]byte)", + defaultReceiver, + valDef, + ), }[t] if !ok { return "", fmt.Errorf("cannot convert type %v for val %s", t, valDef) @@ -66,6 +73,7 @@ func (g *GoApiGenerator) Generate(apis *api.Api, destFile string) error { api.TInt: "0", api.TString: `""`, api.TBool: "false", + api.TBlob: "[]byte{}", }[t] if !ok { return "", fmt.Errorf("cannot generate zero value for type %v", t) diff --git a/kitcom/internal/golang/gogen.tmpl b/kitcom/internal/golang/gogen.tmpl index f055c3f..1247c57 100644 --- a/kitcom/internal/golang/gogen.tmpl +++ b/kitcom/internal/golang/gogen.tmpl @@ -12,7 +12,7 @@ import ( {{ range $e := .Api.Endpoints }} type {{ .Name }} struct { - Ipc kittenipc.Callable + Ipc kittenipc.IpcCommon } {{ range $mtd := $e.Methods }} diff --git a/kitcom/internal/ts/tsgen.tmpl b/kitcom/internal/ts/tsgen.tmpl index 61abca7..8e09604 100644 --- a/kitcom/internal/ts/tsgen.tmpl +++ b/kitcom/internal/ts/tsgen.tmpl @@ -14,17 +14,22 @@ export default class {{ $e.Name }} { {{ range $mtd := $e.Methods }} async {{ $mtd.Name }}( - {{ range $par := $mtd.Params }}{{ $par.Name }}: {{ $par.Type | typedef }}, {{ end }} + {{ range $par := $mtd.Params }}{{ $par.Name }}: {{ $par.Type | typedef }}, {{ end }} ): Promise<{{ if len $mtd.Ret }}{{ (index $mtd.Ret 0).Type | typedef }}{{ else }}void{{ end }}> { + + {{ range $par := $mtd.Params -}} + {{ $par.Name }} = this.ipc.convType({{ $par.Name }}, '{{ $par.Type | typedef }}'); + {{- end -}} + const results = await this.ipc.call('{{ $e.Name }}.{{ $mtd.Name }}', {{ range $par := $mtd.Params }}{{ $par.Name }}, {{ end }} ); - {{- if eq (index $mtd.Ret 0).Type.String "blob" -}} - results[0] = Buffer.from(results[0], 'base64'); + {{- range $i, $ret := $mtd.Ret -}} + results[{{ $i }}] = this.ipc.convType(results[{{ $i }}], '{{ $ret.Type | typedef }}'); {{- end -}} - return {{ range $i, $ret := $mtd.Ret }}{{ if $i }}, {{ end }}results[{{ $i }}] as {{ $ret.Type | typedef }}{{ end }} + return {{ range $i, $ret := $mtd.Ret }}{{ if $i }}, {{ end }}results[{{ $i }}] as {{ $ret.Type | typedef }}{{ end }} } {{ end }} } diff --git a/lib/golang/lib.go b/lib/golang/lib.go index ae4081c..5696cd9 100644 --- a/lib/golang/lib.go +++ b/lib/golang/lib.go @@ -45,8 +45,9 @@ type Message struct { Error string `json:"error"` } -type Callable interface { +type IpcCommon interface { Call(method string, params ...any) (Vals, error) + ConvType(needType reflect.Type, gotType reflect.Type, arg any) any } type pendingCall struct { @@ -138,7 +139,7 @@ func (ipc *ipcCommon) handleCall(msg Message) { for i, arg := range msg.Args { paramType := method.Type().In(i) argType := reflect.TypeOf(arg) - arg = ipc.convType(paramType, argType, arg) + arg = ipc.ConvType(paramType, argType, arg) args = append(args, reflect.ValueOf(arg)) } @@ -159,7 +160,7 @@ func (ipc *ipcCommon) handleCall(msg Message) { ipc.sendResponse(msg.Id, results, resErr) } -func (ipc *ipcCommon) convType(needType reflect.Type, gotType reflect.Type, arg any) any { +func (ipc *ipcCommon) ConvType(needType reflect.Type, gotType reflect.Type, arg any) any { switch needType.Kind() { case reflect.Int: // JSON decodes any number to float64. If we need int, we should check and convert diff --git a/lib/ts/src/lib.ts b/lib/ts/src/lib.ts index 907ec30..4cd305d 100644 --- a/lib/ts/src/lib.ts +++ b/lib/ts/src/lib.ts @@ -124,7 +124,7 @@ abstract class IPCCommon { this.sendMsg({type: MsgType.Response, id: msg.id, error: `endpoint not found: ${ endpointName }`}); return; } - const method = endpoint[methodName]; + const method: Function = endpoint[methodName]; if (!method || typeof method !== 'function') { this.sendMsg({type: MsgType.Response, id: msg.id, error: `method not found: ${ msg.method }`}); return; @@ -183,7 +183,7 @@ abstract class IPCCommon { } }; try { - this.sendMsg({type: MsgType.Call, id, method, args: args.map(this.convType)}); + this.sendMsg({type: MsgType.Call, id, method, args: args.map(arg => this.convType(arg))}); } catch (e) { delete this.pendingCalls[id]; reject(new Error(`send call: ${ e }`)); @@ -191,10 +191,14 @@ abstract class IPCCommon { }); } - private convType(arg: any): JSONSerializable { + public convType(arg: any, toType?: string): any { // noinspection FallThroughInSwitchStatementJS switch (typeof arg) { case 'string': + if(toType === 'Buffer') { + return Buffer.from(arg, 'base64'); + } + return arg; case 'boolean': case 'number': return arg;