├── examples ├── wasi │ ├── testdata │ │ └── hello.txt │ ├── main_test.go │ ├── cat │ │ ├── cat.proto │ │ ├── cat_options.pb.go │ │ ├── cat_plugin.pb.go │ │ └── cat.pb.go │ ├── plugin │ │ └── plugin.go │ ├── main.go │ └── README.md ├── host-functions │ ├── main_test.go │ ├── plugin │ │ └── plugin.go │ ├── greeting │ │ ├── greet.proto │ │ ├── greet_options.pb.go │ │ ├── greet_plugin.pb.go │ │ └── greet.pb.go │ ├── main.go │ └── README.md ├── helloworld │ ├── main_test.go │ ├── greeting │ │ ├── greet.proto │ │ ├── greet_options.pb.go │ │ ├── greet_plugin.pb.go │ │ └── greet.pb.go │ ├── README.md │ ├── plugin-morning │ │ └── morning.go │ ├── plugin-evening │ │ └── evening.go │ └── main.go ├── host-function-library │ ├── main_test.go │ ├── library │ │ └── json-parser │ │ │ ├── export │ │ │ ├── library.proto │ │ │ ├── library_plugin.pb.go │ │ │ ├── library_host.pb.go │ │ │ └── library.pb.go │ │ │ └── impl │ │ │ └── parser.go │ ├── proto │ │ ├── greet.proto │ │ ├── greet_options.pb.go │ │ ├── greet_plugin.pb.go │ │ └── greet.pb.go │ ├── plugin │ │ └── plugin.go │ ├── main.go │ └── README.md └── known-types │ ├── main_test.go │ ├── known │ ├── known.proto │ ├── known_options.pb.go │ ├── known_plugin.pb.go │ └── known.pb.go │ ├── plugin │ └── plugin.go │ ├── main.go │ └── README.md ├── version └── version.go ├── imgs ├── icon.png └── architecture.png ├── .clang-format ├── tests ├── import │ ├── proto │ │ ├── bar │ │ │ ├── bar.proto │ │ │ ├── bar_plugin.pb.go │ │ │ ├── bar_options.pb.go │ │ │ └── bar.pb.go │ │ └── foo │ │ │ ├── foo.proto │ │ │ ├── foo.pb.go │ │ │ ├── foo_plugin.pb.go │ │ │ └── foo_options.pb.go │ ├── plugin │ │ └── plugin.go │ └── import_test.go ├── stdout.go ├── host-functions │ ├── plugin-empty │ │ └── plugin.go │ ├── proto │ │ ├── host.proto │ │ ├── host_options.pb.go │ │ ├── host_plugin.pb.go │ │ └── host.pb.go │ ├── plugin │ │ └── plugin.go │ ├── bench_test.go │ └── host_functions_test.go ├── fields │ ├── bench_test.go │ ├── proto │ │ ├── fields_options.pb.go │ │ ├── fields.proto │ │ └── fields_plugin.pb.go │ ├── plugin │ │ └── plugin.go │ └── fields_test.go └── well-known │ ├── proto │ ├── known_options.pb.go │ ├── known.proto │ └── known_plugin.pb.go │ ├── plugin │ └── plugin.go │ └── well_known_test.go ├── .gitignore ├── go.mod ├── genid ├── doc.go ├── wrappers.go ├── map_entry.go ├── empty_gen.go ├── goname.go ├── field_mask_gen.go ├── source_context_gen.go ├── any_gen.go ├── duration_gen.go ├── timestamp_gen.go ├── struct_gen.go └── api_gen.go ├── cmd └── protoc-gen-go-plugin │ └── main.go ├── types └── known │ ├── emptypb │ ├── empty.proto │ ├── empty.pb.go │ └── empty_vtproto.pb.go │ ├── sourcecontextpb │ ├── source_context.proto │ └── source_context.pb.go │ ├── timestamppb │ ├── timestamp.proto │ ├── timestamp.pb.go │ └── timestamp.go │ ├── wrapperspb │ ├── wrappers.go │ └── wrappers.proto │ ├── structpb │ └── struct.proto │ ├── durationpb │ ├── duration.go │ ├── duration.proto │ └── duration.pb.go │ ├── anypb │ ├── any.proto │ └── any.pb.go │ └── typepb │ └── type.proto ├── .github └── workflows │ ├── release.yaml │ └── test.yaml ├── wasm ├── plugin.go └── host.go ├── gen ├── vtproto.go ├── options.go ├── reflect.go └── plugin.go ├── LICENSE ├── goreleaser.yaml ├── Makefile ├── go.sum └── encoding ├── defval └── default.go └── tag └── tag.go /examples/wasi/testdata/hello.txt: -------------------------------------------------------------------------------- 1 | Hello WASI! -------------------------------------------------------------------------------- /version/version.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | var Version = "v0.1.0" 4 | -------------------------------------------------------------------------------- /imgs/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ice3man543/go-plugin/main/imgs/icon.png -------------------------------------------------------------------------------- /imgs/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ice3man543/go-plugin/main/imgs/architecture.png -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Proto 3 | BasedOnStyle: Google 4 | AlignConsecutiveAssignments: true 5 | AlignConsecutiveDeclarations: true 6 | -------------------------------------------------------------------------------- /examples/host-functions/main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/knqyf263/go-plugin/tests" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func Test_main(t *testing.T) { 11 | got := tests.TestStdout(t, main) 12 | want := `Hello, go-plugin from` 13 | assert.Contains(t, got, want) 14 | } 15 | -------------------------------------------------------------------------------- /examples/wasi/main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/knqyf263/go-plugin/tests" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func Test_main(t *testing.T) { 11 | got := tests.TestStdout(t, main) 12 | want := `File loading... 13 | Hello WASI!` 14 | assert.Equal(t, want, got) 15 | } 16 | -------------------------------------------------------------------------------- /examples/helloworld/main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/knqyf263/go-plugin/tests" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func Test_main(t *testing.T) { 11 | got := tests.TestStdout(t, main) 12 | want := `Good morning, go-plugin 13 | Good evening, go-plugin` 14 | assert.Equal(t, want, got) 15 | } 16 | -------------------------------------------------------------------------------- /examples/host-function-library/main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/knqyf263/go-plugin/tests" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func Test_main(t *testing.T) { 11 | got := tests.TestStdout(t, main) 12 | want := "Hello, Sato. This is Yamada-san (age 20)." 13 | assert.Equal(t, want, got) 14 | } 15 | -------------------------------------------------------------------------------- /examples/known-types/main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/knqyf263/go-plugin/tests" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func Test_main(t *testing.T) { 11 | got := tests.TestStdout(t, main) 12 | want := `I love Sushi 13 | I love Tempura 14 | Duration: 1h0m0s` 15 | assert.Equal(t, want, got) 16 | } 17 | -------------------------------------------------------------------------------- /tests/import/proto/bar/bar.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package bar; 3 | 4 | option go_package = "github.com/knqyf263/go-plugin/tests/import/proto/bar"; 5 | 6 | // go:plugin type=plugin version=1 7 | service Bar { 8 | rpc Hello(Request) returns (Reply) {} 9 | } 10 | 11 | message Request { 12 | string a = 1; 13 | } 14 | 15 | message Reply { 16 | string a = 1; 17 | } 18 | -------------------------------------------------------------------------------- /tests/import/proto/foo/foo.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package foo; 3 | 4 | import "tests/import/proto/bar/bar.proto"; 5 | 6 | option go_package = "github.com/knqyf263/go-plugin/tests/import/proto/foo"; 7 | 8 | // go:plugin type=plugin version=1 9 | service Foo { 10 | rpc Hello(Request) returns (bar.Reply) {} 11 | } 12 | 13 | message Request { 14 | string a = 1; 15 | } 16 | -------------------------------------------------------------------------------- /examples/wasi/cat/cat.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package fs; 3 | 4 | option go_package = "github.com/knqyf263/go-plugin/examples/wasi/cat"; 5 | 6 | // go:plugin type=plugin 7 | service FileCat { 8 | rpc Cat(FileCatRequest) returns (FileCatReply) {} 9 | } 10 | 11 | message FileCatRequest { 12 | string file_path = 1; 13 | } 14 | 15 | message FileCatReply { 16 | string content = 1; 17 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | 17 | .idea 18 | .DS_Store 19 | *.wasm 20 | dist/ 21 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/knqyf263/go-plugin 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/planetscale/vtprotobuf v0.3.0 7 | github.com/stretchr/testify v1.7.1 8 | github.com/tetratelabs/wazero v1.0.2 9 | google.golang.org/protobuf v1.28.1 10 | ) 11 | 12 | require ( 13 | github.com/davecgh/go-spew v1.1.0 // indirect 14 | github.com/pmezard/go-difflib v1.0.0 // indirect 15 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect 16 | ) 17 | -------------------------------------------------------------------------------- /genid/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package genid contains constants for declarations in descriptor.proto 6 | // and the well-known types. 7 | package genid 8 | 9 | import protoreflect "google.golang.org/protobuf/reflect/protoreflect" 10 | 11 | const GoogleProtobuf_package protoreflect.FullName = "google.protobuf" 12 | -------------------------------------------------------------------------------- /genid/wrappers.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package genid 6 | 7 | import protoreflect "google.golang.org/protobuf/reflect/protoreflect" 8 | 9 | // Generic field name and number for messages in wrappers.proto. 10 | const ( 11 | WrapperValue_Value_field_name protoreflect.Name = "value" 12 | WrapperValue_Value_field_number protoreflect.FieldNumber = 1 13 | ) 14 | -------------------------------------------------------------------------------- /tests/stdout.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "bytes" 5 | "os" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func TestStdout(t *testing.T, fn func()) string { 12 | t.Helper() 13 | old := os.Stdout 14 | t.Cleanup(func() { 15 | os.Stdout = old 16 | }) 17 | r, w, err := os.Pipe() 18 | require.NoError(t, err) 19 | 20 | os.Stdout = w 21 | fn() 22 | w.Close() 23 | 24 | var buffer bytes.Buffer 25 | _, err = buffer.ReadFrom(r) 26 | require.NoError(t, err) 27 | 28 | s := buffer.String() 29 | return s[:len(s)-1] 30 | } 31 | -------------------------------------------------------------------------------- /examples/helloworld/greeting/greet.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package greeting; 3 | 4 | option go_package = "github.com/knqyf263/go-plugin/examples/helloworld/greeting"; 5 | 6 | // The greeting service definition. 7 | // go:plugin type=plugin version=1 8 | service Greeter { 9 | // Sends a greeting 10 | rpc Greet(GreetRequest) returns (GreetReply) {} 11 | } 12 | 13 | // The request message containing the user's name. 14 | message GreetRequest { 15 | string name = 1; 16 | } 17 | 18 | // The response message containing the greetings 19 | message GreetReply { 20 | string message = 1; 21 | } -------------------------------------------------------------------------------- /examples/helloworld/README.md: -------------------------------------------------------------------------------- 1 | # Hello World Example 2 | This example shows how to define plugin interfaces, generate Go code and use it. 3 | 4 | ## Generate Go code 5 | A proto file is under `./greeting`. 6 | 7 | ```shell 8 | $ protoc --go-plugin_out=. --go-plugin_opt=paths=source_relative greeting/greet.proto 9 | ``` 10 | 11 | ## Compile a plugin 12 | Use TinyGo to compile the plugin to Wasm. 13 | 14 | ```shell 15 | $ go generate main.go 16 | ``` 17 | 18 | ## Run 19 | `main.go` loads the above two plugins. 20 | 21 | ```shell 22 | $ go run main.go 23 | Good morning, go-plugin 24 | Good evening, go-plugin 25 | ``` -------------------------------------------------------------------------------- /tests/import/plugin/plugin.go: -------------------------------------------------------------------------------- 1 | //go:build tinygo.wasm 2 | 3 | package main 4 | 5 | import ( 6 | "context" 7 | 8 | "github.com/knqyf263/go-plugin/tests/import/proto/bar" 9 | "github.com/knqyf263/go-plugin/tests/import/proto/foo" 10 | ) 11 | 12 | // main is required for TinyGo to compile to Wasm. 13 | func main() { 14 | foo.RegisterFoo(TestPlugin{}) 15 | } 16 | 17 | type TestPlugin struct{} 18 | 19 | var _ foo.Foo = (*TestPlugin)(nil) 20 | 21 | func (p TestPlugin) Hello(_ context.Context, request *foo.Request) (*bar.Reply, error) { 22 | return &bar.Reply{ 23 | A: request.GetA() + ", bar", 24 | }, nil 25 | } 26 | -------------------------------------------------------------------------------- /genid/map_entry.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package genid 6 | 7 | import protoreflect "google.golang.org/protobuf/reflect/protoreflect" 8 | 9 | // Generic field names and numbers for synthetic map entry messages. 10 | const ( 11 | MapEntry_Key_field_name protoreflect.Name = "key" 12 | MapEntry_Value_field_name protoreflect.Name = "value" 13 | 14 | MapEntry_Key_field_number protoreflect.FieldNumber = 1 15 | MapEntry_Value_field_number protoreflect.FieldNumber = 2 16 | ) 17 | -------------------------------------------------------------------------------- /genid/empty_gen.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Code generated by generate-protos. DO NOT EDIT. 6 | 7 | package genid 8 | 9 | import ( 10 | protoreflect "google.golang.org/protobuf/reflect/protoreflect" 11 | ) 12 | 13 | const File_google_protobuf_empty_proto = "google/protobuf/empty.proto" 14 | 15 | // Names for google.protobuf.Empty. 16 | const ( 17 | Empty_message_name protoreflect.Name = "Empty" 18 | Empty_message_fullname protoreflect.FullName = "google.protobuf.Empty" 19 | ) 20 | -------------------------------------------------------------------------------- /examples/helloworld/plugin-morning/morning.go: -------------------------------------------------------------------------------- 1 | //go:build tinygo.wasm 2 | 3 | package main 4 | 5 | import ( 6 | "context" 7 | 8 | "github.com/knqyf263/go-plugin/examples/helloworld/greeting" 9 | ) 10 | 11 | // main is required for TinyGo to compile to Wasm. 12 | func main() { 13 | greeting.RegisterGreeter(GoodMorning{}) 14 | } 15 | 16 | type GoodMorning struct{} 17 | 18 | var _ greeting.Greeter = (*GoodMorning)(nil) 19 | 20 | func (m GoodMorning) Greet(_ context.Context, request *greeting.GreetRequest) (*greeting.GreetReply, error) { 21 | return &greeting.GreetReply{ 22 | Message: "Good morning, " + request.GetName(), 23 | }, nil 24 | } 25 | -------------------------------------------------------------------------------- /examples/host-function-library/library/json-parser/export/library.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package host; 3 | 4 | option go_package = "github.com/knqyf263/go-plugin/examples/host-functions-library/library/json-parser/export"; 5 | 6 | // Distributing host functions without plugin code 7 | // go:plugin type=host module=json-parser 8 | service ParserLibrary { 9 | rpc ParseJson(ParseJsonRequest) returns (ParseJsonResponse) {} 10 | } 11 | 12 | message ParseJsonRequest { 13 | bytes content = 1; 14 | } 15 | 16 | message ParseJsonResponse { 17 | Person response = 1; 18 | } 19 | 20 | message Person { 21 | string name = 1; 22 | int64 age = 2; 23 | } 24 | -------------------------------------------------------------------------------- /examples/helloworld/plugin-evening/evening.go: -------------------------------------------------------------------------------- 1 | //go:build tinygo.wasm 2 | 3 | package main 4 | 5 | import ( 6 | "context" 7 | "fmt" 8 | 9 | "github.com/knqyf263/go-plugin/examples/helloworld/greeting" 10 | ) 11 | 12 | // main is required for TinyGo to compile to Wasm. 13 | func main() { 14 | greeting.RegisterGreeter(GoodEvening{}) 15 | } 16 | 17 | type GoodEvening struct{} 18 | 19 | var _ greeting.Greeter = (*GoodEvening)(nil) 20 | 21 | func (m GoodEvening) Greet(_ context.Context, request *greeting.GreetRequest) (*greeting.GreetReply, error) { 22 | return &greeting.GreetReply{ 23 | Message: fmt.Sprintf("Good evening, %s", request.GetName()), 24 | }, nil 25 | } 26 | -------------------------------------------------------------------------------- /examples/known-types/known/known.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package greeting; 3 | 4 | import "google/protobuf/struct.proto"; 5 | import "google/protobuf/timestamp.proto"; 6 | import "google/protobuf/duration.proto"; 7 | 8 | option go_package = "github.com/knqyf263/go-plugin/examples/well-known/known"; 9 | 10 | // The greeting service definition. 11 | // go:plugin type=plugin 12 | service WellKnown { 13 | rpc Diff(DiffRequest) returns (DiffReply) {} 14 | } 15 | 16 | message DiffRequest { 17 | google.protobuf.Value value = 1; 18 | google.protobuf.Timestamp start = 2; 19 | google.protobuf.Timestamp end = 3; 20 | } 21 | 22 | message DiffReply { 23 | google.protobuf.Duration duration = 1; 24 | } -------------------------------------------------------------------------------- /cmd/protoc-gen-go-plugin/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "google.golang.org/protobuf/compiler/protogen" 6 | "google.golang.org/protobuf/types/pluginpb" 7 | 8 | "github.com/knqyf263/go-plugin/gen" 9 | ) 10 | 11 | func main() { 12 | var flags flag.FlagSet 13 | protogen.Options{ParamFunc: flags.Set}.Run(func(plugin *protogen.Plugin) error { 14 | g, err := gen.NewGenerator(plugin) 15 | if err != nil { 16 | return err 17 | } 18 | 19 | for _, f := range plugin.Files { 20 | if !f.Generate { 21 | continue 22 | } 23 | g.GenerateFiles(f) 24 | } 25 | 26 | plugin.SupportedFeatures = uint64(pluginpb.CodeGeneratorResponse_FEATURE_PROTO3_OPTIONAL) 27 | 28 | return nil 29 | }) 30 | } 31 | -------------------------------------------------------------------------------- /tests/import/import_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | "github.com/stretchr/testify/require" 9 | 10 | "github.com/knqyf263/go-plugin/tests/import/proto/bar" 11 | "github.com/knqyf263/go-plugin/tests/import/proto/foo" 12 | ) 13 | 14 | func TestImport(t *testing.T) { 15 | ctx := context.Background() 16 | p, err := foo.NewFooPlugin(ctx) 17 | require.NoError(t, err) 18 | 19 | plugin, err := p.Load(ctx, "plugin/plugin.wasm") 20 | require.NoError(t, err) 21 | defer plugin.Close(ctx) 22 | 23 | got, err := plugin.Hello(ctx, &foo.Request{ 24 | A: "Hi", 25 | }) 26 | 27 | want := &bar.Reply{ 28 | A: "Hi, bar", 29 | } 30 | assert.Equal(t, want, got) 31 | } 32 | -------------------------------------------------------------------------------- /types/known/emptypb/empty.proto: -------------------------------------------------------------------------------- 1 | // Protocol Buffers - Google's data interchange format 2 | // Copyright 2008 Google Inc. All rights reserved. 3 | // Copyright 2022 Teppei Fukuda. All rights reserved. 4 | // https://developers.google.com/protocol-buffers/ 5 | 6 | syntax = "proto3"; 7 | 8 | package emptyppb; 9 | 10 | option go_package = "github.com/knqyf263/go-plugin/types/known/emptypb"; 11 | 12 | // A generic empty message that you can re-use to avoid defining duplicated 13 | // empty messages in your APIs. A typical example is to use it as the request 14 | // or the response type of an API method. For instance: 15 | // 16 | // service Foo { 17 | // rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); 18 | // } 19 | // 20 | message Empty {} 21 | -------------------------------------------------------------------------------- /types/known/sourcecontextpb/source_context.proto: -------------------------------------------------------------------------------- 1 | // Protocol Buffers - Google's data interchange format 2 | // Copyright 2008 Google Inc. All rights reserved. 3 | // Copyright 2022 Teppei Fukuda. All rights reserved. 4 | // https://developers.google.com/protocol-buffers/ 5 | 6 | syntax = "proto3"; 7 | 8 | package google.protobuf; 9 | 10 | option go_package = "github.com/knqyf263/go-plugin/types/known/sourcecontextpb"; 11 | 12 | // `SourceContext` represents information about the source of a 13 | // protobuf element, like the file in which it is defined. 14 | message SourceContext { 15 | // The path-qualified name of the .proto file that contained the associated 16 | // protobuf element. For example: `"google/protobuf/source_context.proto"`. 17 | string file_name = 1; 18 | } 19 | -------------------------------------------------------------------------------- /examples/wasi/plugin/plugin.go: -------------------------------------------------------------------------------- 1 | //go:build tinygo.wasm 2 | 3 | package main 4 | 5 | import ( 6 | "context" 7 | "fmt" 8 | "os" 9 | 10 | "github.com/knqyf263/go-plugin/examples/wasi/cat" 11 | ) 12 | 13 | // main is required for TinyGo to compile to Wasm. 14 | func main() { 15 | cat.RegisterFileCat(CatPlugin{}) 16 | } 17 | 18 | type CatPlugin struct{} 19 | 20 | var _ cat.FileCat = (*CatPlugin)(nil) 21 | 22 | func (CatPlugin) Cat(_ context.Context, request *cat.FileCatRequest) (*cat.FileCatReply, error) { 23 | // The message is shown in stdout as os.Stdout is attached. 24 | fmt.Println("File loading...") 25 | b, err := os.ReadFile(request.GetFilePath()) 26 | if err != nil { 27 | return nil, err 28 | } 29 | return &cat.FileCatReply{ 30 | Content: string(b), 31 | }, nil 32 | } 33 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | push: 4 | tags: 5 | - "v*" 6 | jobs: 7 | release: 8 | name: Release 9 | runs-on: ubuntu-latest 10 | env: 11 | DOCKER_CLI_EXPERIMENTAL: "enabled" 12 | steps: 13 | - name: Checkout code 14 | uses: actions/checkout@v3 15 | with: 16 | fetch-depth: 0 17 | 18 | - name: Setup Go 19 | uses: actions/setup-go@v3 20 | with: 21 | go-version-file: go.mod 22 | 23 | - name: GoReleaser 24 | uses: goreleaser/goreleaser-action@v4 25 | with: 26 | distribution: goreleaser 27 | version: v1.16.1 28 | args: release --clean --timeout 30m 29 | env: 30 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 31 | -------------------------------------------------------------------------------- /genid/goname.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package genid 6 | 7 | // Go names of implementation-specific struct fields in generated messages. 8 | const ( 9 | State_goname = "state" 10 | 11 | SizeCache_goname = "sizeCache" 12 | SizeCacheA_goname = "XXX_sizecache" 13 | 14 | WeakFields_goname = "weakFields" 15 | WeakFieldsA_goname = "XXX_weak" 16 | 17 | UnknownFields_goname = "unknownFields" 18 | UnknownFieldsA_goname = "XXX_unrecognized" 19 | 20 | ExtensionFields_goname = "extensionFields" 21 | ExtensionFieldsA_goname = "XXX_InternalExtensions" 22 | ExtensionFieldsB_goname = "XXX_extensions" 23 | 24 | WeakFieldPrefix_goname = "XXX_weak_" 25 | ) 26 | -------------------------------------------------------------------------------- /examples/host-function-library/library/json-parser/impl/parser.go: -------------------------------------------------------------------------------- 1 | package impl 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "github.com/knqyf263/go-plugin/examples/host-function-library/library/json-parser/export" 7 | ) 8 | 9 | var _ export.ParserLibrary = (*ParserLibraryImpl)(nil) 10 | 11 | // ParserLibraryImpl implements export.ParserLibrary functions 12 | type ParserLibraryImpl struct{} 13 | 14 | // ParseJson is embedded into the plugin and can be called by the plugin. 15 | func (ParserLibraryImpl) ParseJson(_ context.Context, request *export.ParseJsonRequest) (*export.ParseJsonResponse, error) { 16 | var person export.Person 17 | if err := json.Unmarshal(request.GetContent(), &person); err != nil { 18 | return nil, err 19 | } 20 | 21 | return &export.ParseJsonResponse{Response: &person}, nil 22 | } 23 | -------------------------------------------------------------------------------- /wasm/plugin.go: -------------------------------------------------------------------------------- 1 | //go:build tinygo.wasm 2 | 3 | // This file is designed to be imported by plugins. 4 | 5 | package wasm 6 | 7 | // #include 8 | import "C" 9 | 10 | import ( 11 | "reflect" 12 | "unsafe" 13 | ) 14 | 15 | func PtrToByte(ptr, size uint32) []byte { 16 | var b []byte 17 | s := (*reflect.SliceHeader)(unsafe.Pointer(&b)) 18 | s.Len = uintptr(size) 19 | s.Cap = uintptr(size) 20 | s.Data = uintptr(ptr) 21 | return b 22 | } 23 | 24 | func ByteToPtr(buf []byte) (uint32, uint32) { 25 | if len(buf) == 0 { 26 | return 0, 0 27 | } 28 | 29 | size := C.ulong(len(buf)) 30 | ptr := unsafe.Pointer(C.malloc(size)) 31 | 32 | copy(unsafe.Slice((*byte)(ptr), size), buf) 33 | 34 | return uint32(uintptr(ptr)), uint32(len(buf)) 35 | } 36 | 37 | func FreePtr(ptr uint32) { 38 | C.free(unsafe.Pointer(uintptr(ptr))) 39 | } 40 | -------------------------------------------------------------------------------- /examples/host-function-library/proto/greet.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package host; 3 | 4 | option go_package = "github.com/knqyf263/go-plugin/tests/import-host-functions/proto"; 5 | 6 | // The greeting service definition. 7 | // go:plugin type=plugin version=1 8 | service Greeter { 9 | // Sends a greeting 10 | rpc Greet(GreetRequest) returns (GreetReply) {} 11 | } 12 | 13 | // The request message containing the user's name. 14 | message GreetRequest { 15 | string name = 1; 16 | } 17 | 18 | // The response message containing the greetings 19 | message GreetReply { 20 | string message = 1; 21 | } 22 | 23 | // The host functions embedded into the plugin 24 | // go:plugin type=host 25 | service HostFunctions { 26 | rpc San(SanRequest) returns (SanResponse) {} 27 | } 28 | 29 | message SanRequest { 30 | string message = 1; 31 | } 32 | 33 | message SanResponse { 34 | string message = 1; 35 | } 36 | -------------------------------------------------------------------------------- /types/known/timestamppb/timestamp.proto: -------------------------------------------------------------------------------- 1 | // Protocol Buffers - Google's data interchange format 2 | // Copyright 2008 Google Inc. All rights reserved. 3 | // Copyright 2022 Teppei Fukuda. All rights reserved. 4 | // https://developers.google.com/protocol-buffers/ 5 | 6 | syntax = "proto3"; 7 | 8 | package google.protobuf; 9 | 10 | option go_package = "github.com/knqyf263/go-plugin/types/known/timestamppb"; 11 | 12 | message Timestamp { 13 | // Represents seconds of UTC time since Unix epoch 14 | // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to 15 | // 9999-12-31T23:59:59Z inclusive. 16 | int64 seconds = 1; 17 | 18 | // Non-negative fractions of a second at nanosecond resolution. Negative 19 | // second values with fractions must still have non-negative nanos values 20 | // that count forward in time. Must be from 0 to 999,999,999 21 | // inclusive. 22 | int32 nanos = 2; 23 | } 24 | -------------------------------------------------------------------------------- /examples/known-types/plugin/plugin.go: -------------------------------------------------------------------------------- 1 | //go:build tinygo.wasm 2 | 3 | package main 4 | 5 | import ( 6 | "context" 7 | "fmt" 8 | 9 | "github.com/knqyf263/go-plugin/examples/known-types/known" 10 | "github.com/knqyf263/go-plugin/types/known/durationpb" 11 | ) 12 | 13 | // main is required for TinyGo to compile to Wasm. 14 | func main() { 15 | known.RegisterWellKnown(WellKnownPlugin{}) 16 | } 17 | 18 | type WellKnownPlugin struct{} 19 | 20 | var _ known.WellKnown = (*WellKnownPlugin)(nil) 21 | 22 | func (p WellKnownPlugin) Diff(_ context.Context, request *known.DiffRequest) (*known.DiffReply, error) { 23 | value := request.GetValue().AsInterface() 24 | if m, ok := value.(map[string]interface{}); ok { 25 | fmt.Printf("I love %s\n", m["A"]) 26 | fmt.Printf("I love %s\n", m["B"]) 27 | } 28 | return &known.DiffReply{ 29 | Duration: durationpb.New(request.GetEnd().AsTime().Sub(request.GetStart().AsTime())), 30 | }, nil 31 | } 32 | -------------------------------------------------------------------------------- /tests/host-functions/plugin-empty/plugin.go: -------------------------------------------------------------------------------- 1 | //go:build tinygo.wasm 2 | 3 | package main 4 | 5 | import ( 6 | "context" 7 | "fmt" 8 | 9 | "github.com/knqyf263/go-plugin/tests/host-functions/proto" 10 | ) 11 | 12 | // main is required for TinyGo to compile to Wasm. 13 | func main() { 14 | proto.RegisterGreeter(TestPlugin{}) 15 | } 16 | 17 | type TestPlugin struct{} 18 | 19 | var _ proto.Greeter = (*TestPlugin)(nil) 20 | 21 | func (p TestPlugin) Greet(ctx context.Context, request *proto.GreetRequest) (*proto.GreetReply, error) { 22 | hostFunctions := proto.NewHostFunctions() 23 | 24 | // Call the host function with nil request 25 | resp, err := hostFunctions.ParseJson(ctx, nil) 26 | if err != nil { 27 | return nil, err 28 | } 29 | 30 | return &proto.GreetReply{ 31 | Message: fmt.Sprintf("Hello, empty request '%s' and empty '%s' host function request", request.GetName(), resp.GetResponse().GetName()), 32 | }, nil 33 | } 34 | -------------------------------------------------------------------------------- /gen/vtproto.go: -------------------------------------------------------------------------------- 1 | package gen 2 | 3 | import ( 4 | "bytes" 5 | ) 6 | 7 | func (gg *Generator) generateVTFile(f *fileInfo) { 8 | // It is a bit tricky, but necessary for workaround. 9 | // Generates a vtprotobuf file as a dummy file, modifies and writes that to the real file. 10 | filename := f.GeneratedFilenamePrefix + "_vt_dummy.pb.go" 11 | g := gg.plugin.NewGeneratedFile(filename, f.GoImportPath) 12 | gg.vtgen.GenerateFile(g, f.File) 13 | 14 | b, err := g.Content() 15 | if err != nil { 16 | gg.plugin.Error(err) 17 | return 18 | } 19 | 20 | // Skip the generated header 21 | if idx := bytes.Index(b, []byte("import ")); idx != -1 { 22 | b = b[idx:] 23 | } 24 | 25 | // Do not generate the dummy file 26 | g.Skip() 27 | 28 | // Write the replaced content 29 | filename = f.GeneratedFilenamePrefix + "_vtproto.pb.go" 30 | g = gg.plugin.NewGeneratedFile(filename, f.GoImportPath) 31 | gg.generateHeader(g, f) 32 | g.Write(b) 33 | } 34 | -------------------------------------------------------------------------------- /tests/host-functions/proto/host.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package host; 3 | 4 | option go_package = "github.com/knqyf263/go-plugin/tests/host-functions/proto"; 5 | 6 | // The greeting service definition. 7 | // go:plugin type=plugin version=1 8 | service Greeter { 9 | // Sends a greeting 10 | rpc Greet(GreetRequest) returns (GreetReply) {} 11 | } 12 | 13 | // The request message containing the user's name. 14 | message GreetRequest { 15 | string name = 1; 16 | } 17 | 18 | // The response message containing the greetings 19 | message GreetReply { 20 | string message = 1; 21 | } 22 | 23 | // The host functions embedded into the plugin 24 | // go:plugin type=host 25 | service HostFunctions { 26 | rpc ParseJson(ParseJsonRequest) returns (ParseJsonResponse) {} 27 | } 28 | 29 | message ParseJsonRequest { 30 | bytes content = 1; 31 | } 32 | 33 | message ParseJsonResponse { 34 | Person response = 1; 35 | } 36 | 37 | message Person { 38 | string name = 1; 39 | int64 age = 2; 40 | } 41 | -------------------------------------------------------------------------------- /tests/host-functions/plugin/plugin.go: -------------------------------------------------------------------------------- 1 | //go:build tinygo.wasm 2 | 3 | package main 4 | 5 | import ( 6 | "context" 7 | "fmt" 8 | 9 | "github.com/knqyf263/go-plugin/tests/host-functions/proto" 10 | ) 11 | 12 | // main is required for TinyGo to compile to Wasm. 13 | func main() { 14 | proto.RegisterGreeter(TestPlugin{}) 15 | } 16 | 17 | type TestPlugin struct{} 18 | 19 | var _ proto.Greeter = (*TestPlugin)(nil) 20 | 21 | func (p TestPlugin) Greet(ctx context.Context, request *proto.GreetRequest) (*proto.GreetReply, error) { 22 | hostFunctions := proto.NewHostFunctions() 23 | 24 | // Call the host function to parse JSON 25 | resp, err := hostFunctions.ParseJson(ctx, &proto.ParseJsonRequest{ 26 | Content: []byte(`{"name": "Yamada", "age": 20}`), 27 | }) 28 | if err != nil { 29 | return nil, err 30 | } 31 | 32 | return &proto.GreetReply{ 33 | Message: fmt.Sprintf("Hello, %s. This is %s (age %d).", 34 | request.GetName(), resp.GetResponse().GetName(), resp.GetResponse().GetAge()), 35 | }, nil 36 | } 37 | -------------------------------------------------------------------------------- /genid/field_mask_gen.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Code generated by generate-protos. DO NOT EDIT. 6 | 7 | package genid 8 | 9 | import ( 10 | protoreflect "google.golang.org/protobuf/reflect/protoreflect" 11 | ) 12 | 13 | const File_google_protobuf_field_mask_proto = "google/protobuf/field_mask.proto" 14 | 15 | // Names for google.protobuf.FieldMask. 16 | const ( 17 | FieldMask_message_name protoreflect.Name = "FieldMask" 18 | FieldMask_message_fullname protoreflect.FullName = "google.protobuf.FieldMask" 19 | ) 20 | 21 | // Field names for google.protobuf.FieldMask. 22 | const ( 23 | FieldMask_Paths_field_name protoreflect.Name = "paths" 24 | 25 | FieldMask_Paths_field_fullname protoreflect.FullName = "google.protobuf.FieldMask.paths" 26 | ) 27 | 28 | // Field numbers for google.protobuf.FieldMask. 29 | const ( 30 | FieldMask_Paths_field_number protoreflect.FieldNumber = 1 31 | ) 32 | -------------------------------------------------------------------------------- /tests/host-functions/bench_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "os" 6 | "testing" 7 | 8 | "github.com/tetratelabs/wazero" 9 | 10 | "github.com/knqyf263/go-plugin/tests/host-functions/proto" 11 | ) 12 | 13 | func BenchmarkHostFunctions(b *testing.B) { 14 | b.ReportAllocs() 15 | ctx := context.Background() 16 | mc := wazero.NewModuleConfig().WithStdout(os.Stdout) 17 | p, err := proto.NewGreeterPlugin(ctx, proto.WazeroRuntime(func(ctx context.Context) (wazero.Runtime, error) { 18 | return proto.DefaultWazeroRuntime()(ctx) 19 | }), proto.WazeroModuleConfig(mc)) 20 | if err != nil { 21 | b.Fatal(err) 22 | } 23 | 24 | // Pass my host functions that are embedded into the plugin. 25 | plugin, err := p.Load(ctx, "plugin/plugin.wasm", myHostFunctions{}) 26 | if err != nil { 27 | b.Fatal(err) 28 | } 29 | defer plugin.Close(ctx) 30 | 31 | b.ResetTimer() 32 | for i := 0; i < b.N; i++ { 33 | if _, err := plugin.Greet(ctx, &proto.GreetRequest{ 34 | Name: "Sato", 35 | }); err != nil { 36 | b.Fatal(err) 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /examples/host-functions/plugin/plugin.go: -------------------------------------------------------------------------------- 1 | //go:build tinygo.wasm 2 | 3 | package main 4 | 5 | import ( 6 | "context" 7 | 8 | "github.com/knqyf263/go-plugin/examples/host-functions/greeting" 9 | ) 10 | 11 | // main is required for TinyGo to compile to Wasm. 12 | func main() { 13 | greeting.RegisterGreeter(GreetingPlugin{}) 14 | } 15 | 16 | type GreetingPlugin struct{} 17 | 18 | var _ greeting.Greeter = (*GreetingPlugin)(nil) 19 | 20 | func (m GreetingPlugin) Greet(ctx context.Context, request *greeting.GreetRequest) (*greeting.GreetReply, error) { 21 | hostFunctions := greeting.NewHostFunctions() 22 | 23 | // Logging via the host function 24 | hostFunctions.Log(ctx, &greeting.LogRequest{ 25 | Message: "Sending a HTTP request...", 26 | }) 27 | 28 | // HTTP GET via the host function 29 | resp, err := hostFunctions.HttpGet(ctx, &greeting.HttpGetRequest{Url: "http://ifconfig.me"}) 30 | if err != nil { 31 | return nil, err 32 | } 33 | 34 | return &greeting.GreetReply{ 35 | Message: "Hello, " + request.GetName() + " from " + string(resp.Response), 36 | }, nil 37 | } 38 | -------------------------------------------------------------------------------- /tests/fields/bench_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/knqyf263/go-plugin/tests/fields/proto" 8 | ) 9 | 10 | func BenchmarkFields(b *testing.B) { 11 | b.ReportAllocs() 12 | ctx := context.Background() 13 | p, err := proto.NewFieldTestPlugin(ctx) 14 | if err != nil { 15 | b.Fatal(err) 16 | } 17 | 18 | plugin, err := p.Load(ctx, "plugin/plugin.wasm") 19 | if err != nil { 20 | b.Fatal(err) 21 | } 22 | defer plugin.Close(ctx) 23 | 24 | b.ResetTimer() 25 | for i := 0; i < b.N; i++ { 26 | if _, err = plugin.Test(ctx, &proto.Request{ 27 | A: 1.2, 28 | B: 3.4, 29 | C: 5, 30 | D: -6, 31 | E: 7, 32 | F: 8, 33 | G: 9, 34 | H: -10, 35 | I: 11, 36 | J: 12, 37 | K: 13, 38 | L: -14, 39 | M: false, 40 | N: "foo", 41 | O: []byte("hoge"), 42 | P: []string{"a", "b"}, 43 | Q: map[string]*proto.IntValue{ 44 | "key": {A: 15}, 45 | }, 46 | R: &proto.Request_Nested{ 47 | A: "samurai", 48 | }, 49 | S: proto.Enum_A, 50 | }); err != nil { 51 | b.Fatal(err) 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /genid/source_context_gen.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Code generated by generate-protos. DO NOT EDIT. 6 | 7 | package genid 8 | 9 | import ( 10 | protoreflect "google.golang.org/protobuf/reflect/protoreflect" 11 | ) 12 | 13 | const File_google_protobuf_source_context_proto = "google/protobuf/source_context.proto" 14 | 15 | // Names for google.protobuf.SourceContext. 16 | const ( 17 | SourceContext_message_name protoreflect.Name = "SourceContext" 18 | SourceContext_message_fullname protoreflect.FullName = "google.protobuf.SourceContext" 19 | ) 20 | 21 | // Field names for google.protobuf.SourceContext. 22 | const ( 23 | SourceContext_FileName_field_name protoreflect.Name = "file_name" 24 | 25 | SourceContext_FileName_field_fullname protoreflect.FullName = "google.protobuf.SourceContext.file_name" 26 | ) 27 | 28 | // Field numbers for google.protobuf.SourceContext. 29 | const ( 30 | SourceContext_FileName_field_number protoreflect.FieldNumber = 1 31 | ) 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Teppei Fukuda 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/host-functions/greeting/greet.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package greeting; 3 | 4 | import "google/protobuf/empty.proto"; 5 | 6 | option go_package = "github.com/knqyf263/go-plugin/examples/host-functions/greeting"; 7 | 8 | // The greeting service definition. 9 | // go:plugin type=plugin version=1 10 | service Greeter { 11 | // Sends a greeting 12 | rpc Greet(GreetRequest) returns (GreetReply) {} 13 | } 14 | 15 | // The request message containing the user's name. 16 | message GreetRequest { 17 | string name = 1; 18 | } 19 | 20 | // The response message containing the greetings 21 | message GreetReply { 22 | string message = 1; 23 | } 24 | 25 | // The host functions embedded into the plugin 26 | // go:plugin type=host 27 | service HostFunctions { 28 | // Sends a HTTP GET request 29 | rpc HttpGet(HttpGetRequest) returns (HttpGetResponse) {} 30 | // Shows a log message 31 | rpc Log(LogRequest) returns (google.protobuf.Empty) {} 32 | } 33 | 34 | message HttpGetRequest { 35 | string url = 1; 36 | } 37 | 38 | message HttpGetResponse { 39 | bytes response = 1; 40 | } 41 | 42 | message LogRequest { 43 | string message = 1; 44 | } 45 | -------------------------------------------------------------------------------- /genid/any_gen.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Code generated by generate-protos. DO NOT EDIT. 6 | 7 | package genid 8 | 9 | import ( 10 | protoreflect "google.golang.org/protobuf/reflect/protoreflect" 11 | ) 12 | 13 | const File_google_protobuf_any_proto = "google/protobuf/any.proto" 14 | 15 | // Names for google.protobuf.Any. 16 | const ( 17 | Any_message_name protoreflect.Name = "Any" 18 | Any_message_fullname protoreflect.FullName = "google.protobuf.Any" 19 | ) 20 | 21 | // Field names for google.protobuf.Any. 22 | const ( 23 | Any_TypeUrl_field_name protoreflect.Name = "type_url" 24 | Any_Value_field_name protoreflect.Name = "value" 25 | 26 | Any_TypeUrl_field_fullname protoreflect.FullName = "google.protobuf.Any.type_url" 27 | Any_Value_field_fullname protoreflect.FullName = "google.protobuf.Any.value" 28 | ) 29 | 30 | // Field numbers for google.protobuf.Any. 31 | const ( 32 | Any_TypeUrl_field_number protoreflect.FieldNumber = 1 33 | Any_Value_field_number protoreflect.FieldNumber = 2 34 | ) 35 | -------------------------------------------------------------------------------- /goreleaser.yaml: -------------------------------------------------------------------------------- 1 | project_name: go-plugin 2 | builds: 3 | - 4 | main: cmd/protoc-gen-go-plugin/main.go 5 | binary: protoc-gen-go-plugin 6 | ldflags: 7 | - -s -w 8 | - "-extldflags '-static'" 9 | - -X github.com/knqyf263/go-plugin/version.Version={{.Version}} 10 | env: 11 | - CGO_ENABLED=0 12 | goos: 13 | - darwin 14 | - linux 15 | - freebsd 16 | goarch: 17 | - amd64 18 | - 386 19 | - arm 20 | - arm64 21 | - s390x 22 | - ppc64le 23 | goarm: 24 | - 7 25 | ignore: 26 | - goos: darwin 27 | goarch: 386 28 | 29 | nfpms: 30 | - 31 | formats: 32 | - deb 33 | - rpm 34 | vendor: "knqyf263" 35 | homepage: "https://github.com/knqyf263/go-plugin" 36 | maintainer: "Teppei Fukuda " 37 | description: "Go Plugin System over WebAssembly" 38 | license: "MIT" 39 | file_name_template: "{{.ProjectName}}_{{.Version}}_{{.Os}}-{{.Arch}}" 40 | 41 | archives: 42 | - 43 | rlcp: true 44 | format: tar.gz 45 | name_template: "{{.ProjectName}}_{{.Version}}_{{.Os}}-{{.Arch}}" 46 | files: 47 | - README.md 48 | - LICENSE 49 | -------------------------------------------------------------------------------- /wasm/host.go: -------------------------------------------------------------------------------- 1 | //go:build !tinygo.wasm 2 | 3 | // This file is designed to be imported by hosts. 4 | 5 | package wasm 6 | 7 | import ( 8 | "context" 9 | "errors" 10 | "fmt" 11 | 12 | "github.com/tetratelabs/wazero/api" 13 | ) 14 | 15 | func ReadMemory(mem api.Memory, offset, size uint32) ([]byte, error) { 16 | buf, ok := mem.Read(offset, size) 17 | if !ok { 18 | return nil, fmt.Errorf("Memory.Read(%d, %d) out of range", offset, size) 19 | } 20 | return buf, nil 21 | } 22 | 23 | func WriteMemory(ctx context.Context, m api.Module, data []byte) (uint64, error) { 24 | malloc := m.ExportedFunction("malloc") 25 | if malloc == nil { 26 | return 0, errors.New("malloc is not exported") 27 | } 28 | 29 | l := uint64(len(data)) 30 | if l == 0 { 31 | return 0, nil 32 | } 33 | 34 | results, err := malloc.Call(ctx, l) 35 | if err != nil { 36 | return 0, err 37 | } 38 | dataPtr := results[0] 39 | 40 | // The pointer is a linear memory offset, which is where we write the name. 41 | if !m.Memory().Write(uint32(dataPtr), data) { 42 | return 0, fmt.Errorf("Memory.Write(%d, %d) out of range of memory size %d", 43 | dataPtr, len(data), m.Memory().Size()) 44 | } 45 | 46 | return dataPtr, nil 47 | } 48 | -------------------------------------------------------------------------------- /examples/wasi/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "embed" 6 | _ "embed" 7 | "fmt" 8 | "log" 9 | "os" 10 | 11 | "github.com/tetratelabs/wazero" 12 | 13 | "github.com/knqyf263/go-plugin/examples/wasi/cat" 14 | ) 15 | 16 | //go:embed testdata/hello.txt 17 | var f embed.FS 18 | 19 | func main() { 20 | if err := run(); err != nil { 21 | log.Fatal(err) 22 | } 23 | } 24 | 25 | func run() error { 26 | ctx := context.Background() 27 | mc := wazero.NewModuleConfig(). 28 | WithStdout(os.Stdout). // Attach stdout so that the plugin can write outputs to stdout 29 | WithStderr(os.Stderr). // Attach stderr so that the plugin can write errors to stderr 30 | WithFS(f) // Loaded plugins can access only files that the host allows. 31 | p, err := cat.NewFileCatPlugin(ctx, cat.WazeroModuleConfig(mc)) 32 | if err != nil { 33 | return err 34 | } 35 | 36 | wasiPlugin, err := p.Load(ctx, "plugin/plugin.wasm") 37 | if err != nil { 38 | return err 39 | } 40 | defer wasiPlugin.Close(ctx) 41 | 42 | reply, err := wasiPlugin.Cat(ctx, &cat.FileCatRequest{ 43 | FilePath: "testdata/hello.txt", 44 | }) 45 | if err != nil { 46 | return err 47 | } 48 | 49 | fmt.Println(reply.GetContent()) 50 | 51 | return nil 52 | } 53 | -------------------------------------------------------------------------------- /genid/duration_gen.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Code generated by generate-protos. DO NOT EDIT. 6 | 7 | package genid 8 | 9 | import ( 10 | protoreflect "google.golang.org/protobuf/reflect/protoreflect" 11 | ) 12 | 13 | const File_google_protobuf_duration_proto = "google/protobuf/duration.proto" 14 | 15 | // Names for google.protobuf.Duration. 16 | const ( 17 | Duration_message_name protoreflect.Name = "Duration" 18 | Duration_message_fullname protoreflect.FullName = "google.protobuf.Duration" 19 | ) 20 | 21 | // Field names for google.protobuf.Duration. 22 | const ( 23 | Duration_Seconds_field_name protoreflect.Name = "seconds" 24 | Duration_Nanos_field_name protoreflect.Name = "nanos" 25 | 26 | Duration_Seconds_field_fullname protoreflect.FullName = "google.protobuf.Duration.seconds" 27 | Duration_Nanos_field_fullname protoreflect.FullName = "google.protobuf.Duration.nanos" 28 | ) 29 | 30 | // Field numbers for google.protobuf.Duration. 31 | const ( 32 | Duration_Seconds_field_number protoreflect.FieldNumber = 1 33 | Duration_Nanos_field_number protoreflect.FieldNumber = 2 34 | ) 35 | -------------------------------------------------------------------------------- /genid/timestamp_gen.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Code generated by generate-protos. DO NOT EDIT. 6 | 7 | package genid 8 | 9 | import ( 10 | protoreflect "google.golang.org/protobuf/reflect/protoreflect" 11 | ) 12 | 13 | const File_google_protobuf_timestamp_proto = "google/protobuf/timestamp.proto" 14 | 15 | // Names for google.protobuf.Timestamp. 16 | const ( 17 | Timestamp_message_name protoreflect.Name = "Timestamp" 18 | Timestamp_message_fullname protoreflect.FullName = "google.protobuf.Timestamp" 19 | ) 20 | 21 | // Field names for google.protobuf.Timestamp. 22 | const ( 23 | Timestamp_Seconds_field_name protoreflect.Name = "seconds" 24 | Timestamp_Nanos_field_name protoreflect.Name = "nanos" 25 | 26 | Timestamp_Seconds_field_fullname protoreflect.FullName = "google.protobuf.Timestamp.seconds" 27 | Timestamp_Nanos_field_fullname protoreflect.FullName = "google.protobuf.Timestamp.nanos" 28 | ) 29 | 30 | // Field numbers for google.protobuf.Timestamp. 31 | const ( 32 | Timestamp_Seconds_field_number protoreflect.FieldNumber = 1 33 | Timestamp_Nanos_field_number protoreflect.FieldNumber = 2 34 | ) 35 | -------------------------------------------------------------------------------- /examples/host-function-library/library/json-parser/export/library_plugin.pb.go: -------------------------------------------------------------------------------- 1 | //go:build tinygo.wasm 2 | 3 | // Code generated by protoc-gen-go-plugin. DO NOT EDIT. 4 | // versions: 5 | // protoc-gen-go-plugin v0.1.0 6 | // protoc v3.21.12 7 | // source: examples/host-function-library/library/json-parser/export/library.proto 8 | 9 | package export 10 | 11 | import ( 12 | context "context" 13 | wasm "github.com/knqyf263/go-plugin/wasm" 14 | _ "unsafe" 15 | ) 16 | 17 | type parserLibrary struct{} 18 | 19 | func NewParserLibrary() ParserLibrary { 20 | return parserLibrary{} 21 | } 22 | 23 | //go:wasm-module json-parser 24 | //export parse_json 25 | //go:linkname _parse_json 26 | func _parse_json(ptr uint32, size uint32) uint64 27 | 28 | func (h parserLibrary) ParseJson(ctx context.Context, request *ParseJsonRequest) (*ParseJsonResponse, error) { 29 | buf, err := request.MarshalVT() 30 | if err != nil { 31 | return nil, err 32 | } 33 | ptr, size := wasm.ByteToPtr(buf) 34 | ptrSize := _parse_json(ptr, size) 35 | wasm.FreePtr(ptr) 36 | 37 | ptr = uint32(ptrSize >> 32) 38 | size = uint32(ptrSize) 39 | buf = wasm.PtrToByte(ptr, size) 40 | 41 | response := new(ParseJsonResponse) 42 | if err = response.UnmarshalVT(buf); err != nil { 43 | return nil, err 44 | } 45 | return response, nil 46 | } 47 | -------------------------------------------------------------------------------- /tests/import/proto/bar/bar_plugin.pb.go: -------------------------------------------------------------------------------- 1 | //go:build tinygo.wasm 2 | 3 | // Code generated by protoc-gen-go-plugin. DO NOT EDIT. 4 | // versions: 5 | // protoc-gen-go-plugin v0.1.0 6 | // protoc v3.21.12 7 | // source: tests/import/proto/bar/bar.proto 8 | 9 | package bar 10 | 11 | import ( 12 | context "context" 13 | wasm "github.com/knqyf263/go-plugin/wasm" 14 | ) 15 | 16 | const BarPluginAPIVersion = 1 17 | 18 | //export bar_api_version 19 | func _bar_api_version() uint64 { 20 | return BarPluginAPIVersion 21 | } 22 | 23 | var bar Bar 24 | 25 | func RegisterBar(p Bar) { 26 | bar = p 27 | } 28 | 29 | //export bar_hello 30 | func _bar_hello(ptr, size uint32) uint64 { 31 | b := wasm.PtrToByte(ptr, size) 32 | req := new(Request) 33 | if err := req.UnmarshalVT(b); err != nil { 34 | return 0 35 | } 36 | response, err := bar.Hello(context.Background(), req) 37 | if err != nil { 38 | ptr, size = wasm.ByteToPtr([]byte(err.Error())) 39 | return (uint64(ptr) << uint64(32)) | uint64(size) | 40 | // Indicate that this is the error string by setting the 32-th bit, assuming that 41 | // no data exceeds 31-bit size (2 GiB). 42 | (1 << 31) 43 | } 44 | 45 | b, err = response.MarshalVT() 46 | if err != nil { 47 | return 0 48 | } 49 | ptr, size = wasm.ByteToPtr(b) 50 | return (uint64(ptr) << uint64(32)) | uint64(size) 51 | } 52 | -------------------------------------------------------------------------------- /tests/import/proto/foo/foo.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go-plugin. DO NOT EDIT. 2 | // versions: 3 | // protoc-gen-go-plugin v0.1.0 4 | // protoc v3.21.12 5 | // source: tests/import/proto/foo/foo.proto 6 | 7 | package foo 8 | 9 | import ( 10 | context "context" 11 | bar "github.com/knqyf263/go-plugin/tests/import/proto/bar" 12 | protoreflect "google.golang.org/protobuf/reflect/protoreflect" 13 | protoimpl "google.golang.org/protobuf/runtime/protoimpl" 14 | ) 15 | 16 | const ( 17 | // Verify that this generated code is sufficiently up-to-date. 18 | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) 19 | // Verify that runtime/protoimpl is sufficiently up-to-date. 20 | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) 21 | ) 22 | 23 | type Request struct { 24 | state protoimpl.MessageState 25 | sizeCache protoimpl.SizeCache 26 | unknownFields protoimpl.UnknownFields 27 | 28 | A string `protobuf:"bytes,1,opt,name=a,proto3" json:"a,omitempty"` 29 | } 30 | 31 | func (x *Request) ProtoReflect() protoreflect.Message { 32 | panic(`not implemented`) 33 | } 34 | 35 | func (x *Request) GetA() string { 36 | if x != nil { 37 | return x.A 38 | } 39 | return "" 40 | } 41 | 42 | // go:plugin type=plugin version=1 43 | type Foo interface { 44 | Hello(context.Context, *Request) (*bar.Reply, error) 45 | } 46 | -------------------------------------------------------------------------------- /tests/import/proto/foo/foo_plugin.pb.go: -------------------------------------------------------------------------------- 1 | //go:build tinygo.wasm 2 | 3 | // Code generated by protoc-gen-go-plugin. DO NOT EDIT. 4 | // versions: 5 | // protoc-gen-go-plugin v0.1.0 6 | // protoc v3.21.12 7 | // source: tests/import/proto/foo/foo.proto 8 | 9 | package foo 10 | 11 | import ( 12 | context "context" 13 | wasm "github.com/knqyf263/go-plugin/wasm" 14 | ) 15 | 16 | const FooPluginAPIVersion = 1 17 | 18 | //export foo_api_version 19 | func _foo_api_version() uint64 { 20 | return FooPluginAPIVersion 21 | } 22 | 23 | var foo Foo 24 | 25 | func RegisterFoo(p Foo) { 26 | foo = p 27 | } 28 | 29 | //export foo_hello 30 | func _foo_hello(ptr, size uint32) uint64 { 31 | b := wasm.PtrToByte(ptr, size) 32 | req := new(Request) 33 | if err := req.UnmarshalVT(b); err != nil { 34 | return 0 35 | } 36 | response, err := foo.Hello(context.Background(), req) 37 | if err != nil { 38 | ptr, size = wasm.ByteToPtr([]byte(err.Error())) 39 | return (uint64(ptr) << uint64(32)) | uint64(size) | 40 | // Indicate that this is the error string by setting the 32-th bit, assuming that 41 | // no data exceeds 31-bit size (2 GiB). 42 | (1 << 31) 43 | } 44 | 45 | b, err = response.MarshalVT() 46 | if err != nil { 47 | return 0 48 | } 49 | ptr, size = wasm.ByteToPtr(b) 50 | return (uint64(ptr) << uint64(32)) | uint64(size) 51 | } 52 | -------------------------------------------------------------------------------- /examples/host-function-library/plugin/plugin.go: -------------------------------------------------------------------------------- 1 | //go:build tinygo.wasm 2 | 3 | package main 4 | 5 | import ( 6 | "context" 7 | "fmt" 8 | 9 | "github.com/knqyf263/go-plugin/examples/host-function-library/library/json-parser/export" 10 | "github.com/knqyf263/go-plugin/examples/host-function-library/proto" 11 | ) 12 | 13 | // main is required for TinyGo to compile to Wasm. 14 | func main() { 15 | proto.RegisterGreeter(TestPlugin{}) 16 | } 17 | 18 | type TestPlugin struct{} 19 | 20 | var _ proto.Greeter = (*TestPlugin)(nil) 21 | 22 | func (p TestPlugin) Greet(ctx context.Context, request *proto.GreetRequest) (*proto.GreetReply, error) { 23 | parserLibrary := export.NewParserLibrary() 24 | localHostFunctions := proto.NewHostFunctions() 25 | 26 | sanrequest, err := localHostFunctions.San(ctx, &proto.SanRequest{Message: "Yamada"}) 27 | if err != nil { 28 | return nil, err 29 | } 30 | 31 | // Call the host function to parse JSON 32 | resp, err := parserLibrary.ParseJson(ctx, &export.ParseJsonRequest{ 33 | Content: []byte(fmt.Sprintf(`{"name": "%s", "age": 20}`, sanrequest.Message)), 34 | }) 35 | if err != nil { 36 | return nil, err 37 | } 38 | 39 | return &proto.GreetReply{ 40 | Message: fmt.Sprintf("Hello, %s. This is %s (age %d).", 41 | request.GetName(), resp.GetResponse().GetName(), resp.GetResponse().GetAge()), 42 | }, nil 43 | } 44 | -------------------------------------------------------------------------------- /examples/wasi/cat/cat_options.pb.go: -------------------------------------------------------------------------------- 1 | //go:build !tinygo.wasm 2 | 3 | // Code generated by protoc-gen-go-plugin. DO NOT EDIT. 4 | // versions: 5 | // protoc-gen-go-plugin v0.1.0 6 | // protoc v3.21.12 7 | // source: examples/wasi/cat/cat.proto 8 | 9 | package cat 10 | 11 | import ( 12 | context "context" 13 | wazero "github.com/tetratelabs/wazero" 14 | wasi_snapshot_preview1 "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1" 15 | ) 16 | 17 | type wazeroConfigOption func(plugin *WazeroConfig) 18 | 19 | type WazeroNewRuntime func(context.Context) (wazero.Runtime, error) 20 | 21 | type WazeroConfig struct { 22 | newRuntime func(context.Context) (wazero.Runtime, error) 23 | moduleConfig wazero.ModuleConfig 24 | } 25 | 26 | func WazeroRuntime(newRuntime WazeroNewRuntime) wazeroConfigOption { 27 | return func(h *WazeroConfig) { 28 | h.newRuntime = newRuntime 29 | } 30 | } 31 | 32 | func DefaultWazeroRuntime() WazeroNewRuntime { 33 | return func(ctx context.Context) (wazero.Runtime, error) { 34 | r := wazero.NewRuntime(ctx) 35 | if _, err := wasi_snapshot_preview1.Instantiate(ctx, r); err != nil { 36 | return nil, err 37 | } 38 | 39 | return r, nil 40 | } 41 | } 42 | 43 | func WazeroModuleConfig(moduleConfig wazero.ModuleConfig) wazeroConfigOption { 44 | return func(h *WazeroConfig) { 45 | h.moduleConfig = moduleConfig 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /examples/known-types/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "os" 8 | "time" 9 | 10 | "github.com/tetratelabs/wazero" 11 | 12 | "github.com/knqyf263/go-plugin/examples/known-types/known" 13 | "github.com/knqyf263/go-plugin/types/known/structpb" 14 | "github.com/knqyf263/go-plugin/types/known/timestamppb" 15 | ) 16 | 17 | func main() { 18 | if err := run(); err != nil { 19 | log.Fatal(err) 20 | } 21 | } 22 | 23 | func run() error { 24 | ctx := context.Background() 25 | mc := wazero.NewModuleConfig().WithStdout(os.Stdout).WithStderr(os.Stderr) 26 | p, err := known.NewWellKnownPlugin(ctx, known.WazeroModuleConfig(mc)) 27 | if err != nil { 28 | return err 29 | } 30 | 31 | plugin, err := p.Load(ctx, "plugin/plugin.wasm") 32 | if err != nil { 33 | return err 34 | } 35 | defer plugin.Close(ctx) 36 | 37 | value, err := structpb.NewValue(map[string]interface{}{ 38 | "A": "Sushi", 39 | "B": "Tempura", 40 | }) 41 | if err != nil { 42 | return err 43 | } 44 | 45 | start := timestamppb.Now() 46 | end := timestamppb.New(start.AsTime().Add(1 * time.Hour)) 47 | reply, err := plugin.Diff(ctx, &known.DiffRequest{ 48 | Value: value, 49 | Start: start, 50 | End: end, 51 | }) 52 | if err != nil { 53 | return err 54 | } 55 | 56 | fmt.Printf("Duration: %s\n", reply.GetDuration().AsDuration()) 57 | 58 | return nil 59 | } 60 | -------------------------------------------------------------------------------- /tests/fields/proto/fields_options.pb.go: -------------------------------------------------------------------------------- 1 | //go:build !tinygo.wasm 2 | 3 | // Code generated by protoc-gen-go-plugin. DO NOT EDIT. 4 | // versions: 5 | // protoc-gen-go-plugin v0.1.0 6 | // protoc v3.21.12 7 | // source: tests/fields/proto/fields.proto 8 | 9 | package proto 10 | 11 | import ( 12 | context "context" 13 | wazero "github.com/tetratelabs/wazero" 14 | wasi_snapshot_preview1 "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1" 15 | ) 16 | 17 | type wazeroConfigOption func(plugin *WazeroConfig) 18 | 19 | type WazeroNewRuntime func(context.Context) (wazero.Runtime, error) 20 | 21 | type WazeroConfig struct { 22 | newRuntime func(context.Context) (wazero.Runtime, error) 23 | moduleConfig wazero.ModuleConfig 24 | } 25 | 26 | func WazeroRuntime(newRuntime WazeroNewRuntime) wazeroConfigOption { 27 | return func(h *WazeroConfig) { 28 | h.newRuntime = newRuntime 29 | } 30 | } 31 | 32 | func DefaultWazeroRuntime() WazeroNewRuntime { 33 | return func(ctx context.Context) (wazero.Runtime, error) { 34 | r := wazero.NewRuntime(ctx) 35 | if _, err := wasi_snapshot_preview1.Instantiate(ctx, r); err != nil { 36 | return nil, err 37 | } 38 | 39 | return r, nil 40 | } 41 | } 42 | 43 | func WazeroModuleConfig(moduleConfig wazero.ModuleConfig) wazeroConfigOption { 44 | return func(h *WazeroConfig) { 45 | h.moduleConfig = moduleConfig 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /tests/import/proto/bar/bar_options.pb.go: -------------------------------------------------------------------------------- 1 | //go:build !tinygo.wasm 2 | 3 | // Code generated by protoc-gen-go-plugin. DO NOT EDIT. 4 | // versions: 5 | // protoc-gen-go-plugin v0.1.0 6 | // protoc v3.21.12 7 | // source: tests/import/proto/bar/bar.proto 8 | 9 | package bar 10 | 11 | import ( 12 | context "context" 13 | wazero "github.com/tetratelabs/wazero" 14 | wasi_snapshot_preview1 "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1" 15 | ) 16 | 17 | type wazeroConfigOption func(plugin *WazeroConfig) 18 | 19 | type WazeroNewRuntime func(context.Context) (wazero.Runtime, error) 20 | 21 | type WazeroConfig struct { 22 | newRuntime func(context.Context) (wazero.Runtime, error) 23 | moduleConfig wazero.ModuleConfig 24 | } 25 | 26 | func WazeroRuntime(newRuntime WazeroNewRuntime) wazeroConfigOption { 27 | return func(h *WazeroConfig) { 28 | h.newRuntime = newRuntime 29 | } 30 | } 31 | 32 | func DefaultWazeroRuntime() WazeroNewRuntime { 33 | return func(ctx context.Context) (wazero.Runtime, error) { 34 | r := wazero.NewRuntime(ctx) 35 | if _, err := wasi_snapshot_preview1.Instantiate(ctx, r); err != nil { 36 | return nil, err 37 | } 38 | 39 | return r, nil 40 | } 41 | } 42 | 43 | func WazeroModuleConfig(moduleConfig wazero.ModuleConfig) wazeroConfigOption { 44 | return func(h *WazeroConfig) { 45 | h.moduleConfig = moduleConfig 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /tests/import/proto/foo/foo_options.pb.go: -------------------------------------------------------------------------------- 1 | //go:build !tinygo.wasm 2 | 3 | // Code generated by protoc-gen-go-plugin. DO NOT EDIT. 4 | // versions: 5 | // protoc-gen-go-plugin v0.1.0 6 | // protoc v3.21.12 7 | // source: tests/import/proto/foo/foo.proto 8 | 9 | package foo 10 | 11 | import ( 12 | context "context" 13 | wazero "github.com/tetratelabs/wazero" 14 | wasi_snapshot_preview1 "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1" 15 | ) 16 | 17 | type wazeroConfigOption func(plugin *WazeroConfig) 18 | 19 | type WazeroNewRuntime func(context.Context) (wazero.Runtime, error) 20 | 21 | type WazeroConfig struct { 22 | newRuntime func(context.Context) (wazero.Runtime, error) 23 | moduleConfig wazero.ModuleConfig 24 | } 25 | 26 | func WazeroRuntime(newRuntime WazeroNewRuntime) wazeroConfigOption { 27 | return func(h *WazeroConfig) { 28 | h.newRuntime = newRuntime 29 | } 30 | } 31 | 32 | func DefaultWazeroRuntime() WazeroNewRuntime { 33 | return func(ctx context.Context) (wazero.Runtime, error) { 34 | r := wazero.NewRuntime(ctx) 35 | if _, err := wasi_snapshot_preview1.Instantiate(ctx, r); err != nil { 36 | return nil, err 37 | } 38 | 39 | return r, nil 40 | } 41 | } 42 | 43 | func WazeroModuleConfig(moduleConfig wazero.ModuleConfig) wazeroConfigOption { 44 | return func(h *WazeroConfig) { 45 | h.moduleConfig = moduleConfig 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | GOPATH := $(shell go env GOPATH) 2 | GOBIN := $(if $(GOPATH),$(GOPATH)/bin,/usr/local/bin) 3 | 4 | go_sources := $(shell find cmd encoding gen genid version wasm -name "*.go") 5 | ifdef VERSION 6 | LDFLAGS := -ldflags="-X github.com/knqyf263/go-plugin/version.Version=${VERSION}" 7 | endif 8 | 9 | .PHONY: build 10 | build: $(GOBIN)/protoc-gen-go-plugin 11 | 12 | $(GOBIN)/protoc-gen-go-plugin: $(go_sources) 13 | go build ${LDFLAGS} -o $(GOPATH)/bin/protoc-gen-go-plugin cmd/protoc-gen-go-plugin/main.go 14 | 15 | tinygo_examples := $(shell find examples -path "*/plugin*/*.go") 16 | .PHONY: build.examples 17 | build.examples: $(tinygo_examples:.go=.wasm) 18 | 19 | tinygo_tests := $(shell find tests -path "*/plugin*/*.go") 20 | .PHONY: build.tests 21 | build.tests: $(tinygo_tests:.go=.wasm) 22 | 23 | %.wasm: %.go $(GOBIN)/protoc-gen-go-plugin 24 | tinygo build -o $@ -scheduler=none --no-debug --target=wasi $< 25 | 26 | proto_files := $(shell find . -name "*.proto") 27 | .PHONY: protoc 28 | protoc: $(proto_files:.proto=.pb.go) $(proto_files:.proto=_vtproto.pb.go) 29 | 30 | %.pb.go: %.proto $(GOBIN)/protoc-gen-go-plugin 31 | protoc --go-plugin_out=. --go-plugin_opt=paths=source_relative $<; 32 | 33 | .PHONY: fmt 34 | fmt: $(proto_files) 35 | @for f in $^; do \ 36 | clang-format -i $$f; \ 37 | done 38 | 39 | .PHONY: test 40 | test: build.tests build.examples 41 | go test -v -short ./... 42 | -------------------------------------------------------------------------------- /examples/wasi/cat/cat_plugin.pb.go: -------------------------------------------------------------------------------- 1 | //go:build tinygo.wasm 2 | 3 | // Code generated by protoc-gen-go-plugin. DO NOT EDIT. 4 | // versions: 5 | // protoc-gen-go-plugin v0.1.0 6 | // protoc v3.21.12 7 | // source: examples/wasi/cat/cat.proto 8 | 9 | package cat 10 | 11 | import ( 12 | context "context" 13 | wasm "github.com/knqyf263/go-plugin/wasm" 14 | ) 15 | 16 | const FileCatPluginAPIVersion = 1 17 | 18 | //export file_cat_api_version 19 | func _file_cat_api_version() uint64 { 20 | return FileCatPluginAPIVersion 21 | } 22 | 23 | var fileCat FileCat 24 | 25 | func RegisterFileCat(p FileCat) { 26 | fileCat = p 27 | } 28 | 29 | //export file_cat_cat 30 | func _file_cat_cat(ptr, size uint32) uint64 { 31 | b := wasm.PtrToByte(ptr, size) 32 | req := new(FileCatRequest) 33 | if err := req.UnmarshalVT(b); err != nil { 34 | return 0 35 | } 36 | response, err := fileCat.Cat(context.Background(), req) 37 | if err != nil { 38 | ptr, size = wasm.ByteToPtr([]byte(err.Error())) 39 | return (uint64(ptr) << uint64(32)) | uint64(size) | 40 | // Indicate that this is the error string by setting the 32-th bit, assuming that 41 | // no data exceeds 31-bit size (2 GiB). 42 | (1 << 31) 43 | } 44 | 45 | b, err = response.MarshalVT() 46 | if err != nil { 47 | return 0 48 | } 49 | ptr, size = wasm.ByteToPtr(b) 50 | return (uint64(ptr) << uint64(32)) | uint64(size) 51 | } 52 | -------------------------------------------------------------------------------- /tests/well-known/proto/known_options.pb.go: -------------------------------------------------------------------------------- 1 | //go:build !tinygo.wasm 2 | 3 | // Code generated by protoc-gen-go-plugin. DO NOT EDIT. 4 | // versions: 5 | // protoc-gen-go-plugin v0.1.0 6 | // protoc v3.21.12 7 | // source: tests/well-known/proto/known.proto 8 | 9 | package proto 10 | 11 | import ( 12 | context "context" 13 | wazero "github.com/tetratelabs/wazero" 14 | wasi_snapshot_preview1 "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1" 15 | ) 16 | 17 | type wazeroConfigOption func(plugin *WazeroConfig) 18 | 19 | type WazeroNewRuntime func(context.Context) (wazero.Runtime, error) 20 | 21 | type WazeroConfig struct { 22 | newRuntime func(context.Context) (wazero.Runtime, error) 23 | moduleConfig wazero.ModuleConfig 24 | } 25 | 26 | func WazeroRuntime(newRuntime WazeroNewRuntime) wazeroConfigOption { 27 | return func(h *WazeroConfig) { 28 | h.newRuntime = newRuntime 29 | } 30 | } 31 | 32 | func DefaultWazeroRuntime() WazeroNewRuntime { 33 | return func(ctx context.Context) (wazero.Runtime, error) { 34 | r := wazero.NewRuntime(ctx) 35 | if _, err := wasi_snapshot_preview1.Instantiate(ctx, r); err != nil { 36 | return nil, err 37 | } 38 | 39 | return r, nil 40 | } 41 | } 42 | 43 | func WazeroModuleConfig(moduleConfig wazero.ModuleConfig) wazeroConfigOption { 44 | return func(h *WazeroConfig) { 45 | h.moduleConfig = moduleConfig 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /tests/host-functions/proto/host_options.pb.go: -------------------------------------------------------------------------------- 1 | //go:build !tinygo.wasm 2 | 3 | // Code generated by protoc-gen-go-plugin. DO NOT EDIT. 4 | // versions: 5 | // protoc-gen-go-plugin v0.1.0 6 | // protoc v3.21.12 7 | // source: tests/host-functions/proto/host.proto 8 | 9 | package proto 10 | 11 | import ( 12 | context "context" 13 | wazero "github.com/tetratelabs/wazero" 14 | wasi_snapshot_preview1 "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1" 15 | ) 16 | 17 | type wazeroConfigOption func(plugin *WazeroConfig) 18 | 19 | type WazeroNewRuntime func(context.Context) (wazero.Runtime, error) 20 | 21 | type WazeroConfig struct { 22 | newRuntime func(context.Context) (wazero.Runtime, error) 23 | moduleConfig wazero.ModuleConfig 24 | } 25 | 26 | func WazeroRuntime(newRuntime WazeroNewRuntime) wazeroConfigOption { 27 | return func(h *WazeroConfig) { 28 | h.newRuntime = newRuntime 29 | } 30 | } 31 | 32 | func DefaultWazeroRuntime() WazeroNewRuntime { 33 | return func(ctx context.Context) (wazero.Runtime, error) { 34 | r := wazero.NewRuntime(ctx) 35 | if _, err := wasi_snapshot_preview1.Instantiate(ctx, r); err != nil { 36 | return nil, err 37 | } 38 | 39 | return r, nil 40 | } 41 | } 42 | 43 | func WazeroModuleConfig(moduleConfig wazero.ModuleConfig) wazeroConfigOption { 44 | return func(h *WazeroConfig) { 45 | h.moduleConfig = moduleConfig 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /examples/known-types/known/known_options.pb.go: -------------------------------------------------------------------------------- 1 | //go:build !tinygo.wasm 2 | 3 | // Code generated by protoc-gen-go-plugin. DO NOT EDIT. 4 | // versions: 5 | // protoc-gen-go-plugin v0.1.0 6 | // protoc v3.21.12 7 | // source: examples/known-types/known/known.proto 8 | 9 | package known 10 | 11 | import ( 12 | context "context" 13 | wazero "github.com/tetratelabs/wazero" 14 | wasi_snapshot_preview1 "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1" 15 | ) 16 | 17 | type wazeroConfigOption func(plugin *WazeroConfig) 18 | 19 | type WazeroNewRuntime func(context.Context) (wazero.Runtime, error) 20 | 21 | type WazeroConfig struct { 22 | newRuntime func(context.Context) (wazero.Runtime, error) 23 | moduleConfig wazero.ModuleConfig 24 | } 25 | 26 | func WazeroRuntime(newRuntime WazeroNewRuntime) wazeroConfigOption { 27 | return func(h *WazeroConfig) { 28 | h.newRuntime = newRuntime 29 | } 30 | } 31 | 32 | func DefaultWazeroRuntime() WazeroNewRuntime { 33 | return func(ctx context.Context) (wazero.Runtime, error) { 34 | r := wazero.NewRuntime(ctx) 35 | if _, err := wasi_snapshot_preview1.Instantiate(ctx, r); err != nil { 36 | return nil, err 37 | } 38 | 39 | return r, nil 40 | } 41 | } 42 | 43 | func WazeroModuleConfig(moduleConfig wazero.ModuleConfig) wazeroConfigOption { 44 | return func(h *WazeroConfig) { 45 | h.moduleConfig = moduleConfig 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /examples/helloworld/greeting/greet_options.pb.go: -------------------------------------------------------------------------------- 1 | //go:build !tinygo.wasm 2 | 3 | // Code generated by protoc-gen-go-plugin. DO NOT EDIT. 4 | // versions: 5 | // protoc-gen-go-plugin v0.1.0 6 | // protoc v3.21.12 7 | // source: examples/helloworld/greeting/greet.proto 8 | 9 | package greeting 10 | 11 | import ( 12 | context "context" 13 | wazero "github.com/tetratelabs/wazero" 14 | wasi_snapshot_preview1 "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1" 15 | ) 16 | 17 | type wazeroConfigOption func(plugin *WazeroConfig) 18 | 19 | type WazeroNewRuntime func(context.Context) (wazero.Runtime, error) 20 | 21 | type WazeroConfig struct { 22 | newRuntime func(context.Context) (wazero.Runtime, error) 23 | moduleConfig wazero.ModuleConfig 24 | } 25 | 26 | func WazeroRuntime(newRuntime WazeroNewRuntime) wazeroConfigOption { 27 | return func(h *WazeroConfig) { 28 | h.newRuntime = newRuntime 29 | } 30 | } 31 | 32 | func DefaultWazeroRuntime() WazeroNewRuntime { 33 | return func(ctx context.Context) (wazero.Runtime, error) { 34 | r := wazero.NewRuntime(ctx) 35 | if _, err := wasi_snapshot_preview1.Instantiate(ctx, r); err != nil { 36 | return nil, err 37 | } 38 | 39 | return r, nil 40 | } 41 | } 42 | 43 | func WazeroModuleConfig(moduleConfig wazero.ModuleConfig) wazeroConfigOption { 44 | return func(h *WazeroConfig) { 45 | h.moduleConfig = moduleConfig 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /examples/host-functions/greeting/greet_options.pb.go: -------------------------------------------------------------------------------- 1 | //go:build !tinygo.wasm 2 | 3 | // Code generated by protoc-gen-go-plugin. DO NOT EDIT. 4 | // versions: 5 | // protoc-gen-go-plugin v0.1.0 6 | // protoc v3.21.12 7 | // source: examples/host-functions/greeting/greet.proto 8 | 9 | package greeting 10 | 11 | import ( 12 | context "context" 13 | wazero "github.com/tetratelabs/wazero" 14 | wasi_snapshot_preview1 "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1" 15 | ) 16 | 17 | type wazeroConfigOption func(plugin *WazeroConfig) 18 | 19 | type WazeroNewRuntime func(context.Context) (wazero.Runtime, error) 20 | 21 | type WazeroConfig struct { 22 | newRuntime func(context.Context) (wazero.Runtime, error) 23 | moduleConfig wazero.ModuleConfig 24 | } 25 | 26 | func WazeroRuntime(newRuntime WazeroNewRuntime) wazeroConfigOption { 27 | return func(h *WazeroConfig) { 28 | h.newRuntime = newRuntime 29 | } 30 | } 31 | 32 | func DefaultWazeroRuntime() WazeroNewRuntime { 33 | return func(ctx context.Context) (wazero.Runtime, error) { 34 | r := wazero.NewRuntime(ctx) 35 | if _, err := wasi_snapshot_preview1.Instantiate(ctx, r); err != nil { 36 | return nil, err 37 | } 38 | 39 | return r, nil 40 | } 41 | } 42 | 43 | func WazeroModuleConfig(moduleConfig wazero.ModuleConfig) wazeroConfigOption { 44 | return func(h *WazeroConfig) { 45 | h.moduleConfig = moduleConfig 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /examples/helloworld/greeting/greet_plugin.pb.go: -------------------------------------------------------------------------------- 1 | //go:build tinygo.wasm 2 | 3 | // Code generated by protoc-gen-go-plugin. DO NOT EDIT. 4 | // versions: 5 | // protoc-gen-go-plugin v0.1.0 6 | // protoc v3.21.12 7 | // source: examples/helloworld/greeting/greet.proto 8 | 9 | package greeting 10 | 11 | import ( 12 | context "context" 13 | wasm "github.com/knqyf263/go-plugin/wasm" 14 | ) 15 | 16 | const GreeterPluginAPIVersion = 1 17 | 18 | //export greeter_api_version 19 | func _greeter_api_version() uint64 { 20 | return GreeterPluginAPIVersion 21 | } 22 | 23 | var greeter Greeter 24 | 25 | func RegisterGreeter(p Greeter) { 26 | greeter = p 27 | } 28 | 29 | //export greeter_greet 30 | func _greeter_greet(ptr, size uint32) uint64 { 31 | b := wasm.PtrToByte(ptr, size) 32 | req := new(GreetRequest) 33 | if err := req.UnmarshalVT(b); err != nil { 34 | return 0 35 | } 36 | response, err := greeter.Greet(context.Background(), req) 37 | if err != nil { 38 | ptr, size = wasm.ByteToPtr([]byte(err.Error())) 39 | return (uint64(ptr) << uint64(32)) | uint64(size) | 40 | // Indicate that this is the error string by setting the 32-th bit, assuming that 41 | // no data exceeds 31-bit size (2 GiB). 42 | (1 << 31) 43 | } 44 | 45 | b, err = response.MarshalVT() 46 | if err != nil { 47 | return 0 48 | } 49 | ptr, size = wasm.ByteToPtr(b) 50 | return (uint64(ptr) << uint64(32)) | uint64(size) 51 | } 52 | -------------------------------------------------------------------------------- /examples/host-function-library/proto/greet_options.pb.go: -------------------------------------------------------------------------------- 1 | //go:build !tinygo.wasm 2 | 3 | // Code generated by protoc-gen-go-plugin. DO NOT EDIT. 4 | // versions: 5 | // protoc-gen-go-plugin v0.1.0 6 | // protoc v3.21.12 7 | // source: examples/host-function-library/proto/greet.proto 8 | 9 | package proto 10 | 11 | import ( 12 | context "context" 13 | wazero "github.com/tetratelabs/wazero" 14 | wasi_snapshot_preview1 "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1" 15 | ) 16 | 17 | type wazeroConfigOption func(plugin *WazeroConfig) 18 | 19 | type WazeroNewRuntime func(context.Context) (wazero.Runtime, error) 20 | 21 | type WazeroConfig struct { 22 | newRuntime func(context.Context) (wazero.Runtime, error) 23 | moduleConfig wazero.ModuleConfig 24 | } 25 | 26 | func WazeroRuntime(newRuntime WazeroNewRuntime) wazeroConfigOption { 27 | return func(h *WazeroConfig) { 28 | h.newRuntime = newRuntime 29 | } 30 | } 31 | 32 | func DefaultWazeroRuntime() WazeroNewRuntime { 33 | return func(ctx context.Context) (wazero.Runtime, error) { 34 | r := wazero.NewRuntime(ctx) 35 | if _, err := wasi_snapshot_preview1.Instantiate(ctx, r); err != nil { 36 | return nil, err 37 | } 38 | 39 | return r, nil 40 | } 41 | } 42 | 43 | func WazeroModuleConfig(moduleConfig wazero.ModuleConfig) wazeroConfigOption { 44 | return func(h *WazeroConfig) { 45 | h.moduleConfig = moduleConfig 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /examples/known-types/README.md: -------------------------------------------------------------------------------- 1 | # Well-Known Types Example 2 | This example shows how to use [well-known types][well-known-types] in Protocol Buffers. 3 | 4 | ## Generate Go code 5 | A proto file is under `./known`. 6 | You can use well-known types by importing `google/protobuf/xxx.proto`. 7 | 8 | ```protobuf 9 | import "google/protobuf/timestamp.proto"; 10 | ``` 11 | 12 | Then, you can use those types as usual. 13 | 14 | ```protobuf 15 | message DiffRequest { 16 | google.protobuf.Value value = 1; 17 | google.protobuf.Timestamp start = 2; 18 | google.protobuf.Timestamp end = 3; 19 | } 20 | ``` 21 | 22 | Generate code. 23 | 24 | ```shell 25 | $ protoc --go-plugin_out=. --go-plugin_opt=paths=source_relative known/known.proto 26 | ``` 27 | 28 | NOTE: The generated code imports packages under https://github.com/knqyf263/go-plugin/tree/main/types/known. 29 | TinyGo cannot compile packages under https://github.com/protocolbuffers/protobuf-go/tree/master/types/known. 30 | 31 | ## Compile a plugin 32 | Use TinyGo to compile the plugin to Wasm. 33 | 34 | ```shell 35 | $ tinygo build -o plugin/plugin.wasm -scheduler=none -target=wasi --no-debug plugin/plugin.go 36 | ``` 37 | 38 | ## Run 39 | `main.go` loads the above plugin. 40 | 41 | ```shell 42 | $ go run main.go 43 | I love Sushi 44 | I love Tempura 45 | Duration: 1h0m0s 46 | ``` 47 | 48 | [well-known-types]: https://developers.google.com/protocol-buffers/docs/reference/google.protobuf -------------------------------------------------------------------------------- /examples/helloworld/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | ) 8 | 9 | //go:generate tinygo build -o plugin-morning/morning.wasm -scheduler=none -target=wasi --no-debug plugin-morning/morning.go 10 | //go:generate tinygo build -o plugin-evening/evening.wasm -scheduler=none -target=wasi --no-debug plugin-evening/evening.go 11 | 12 | import ( 13 | "github.com/knqyf263/go-plugin/examples/helloworld/greeting" 14 | ) 15 | 16 | func main() { 17 | if err := run(); err != nil { 18 | log.Fatal(err) 19 | } 20 | } 21 | 22 | func run() error { 23 | ctx := context.Background() 24 | p, err := greeting.NewGreeterPlugin(ctx) 25 | if err != nil { 26 | return err 27 | } 28 | 29 | morningPlugin, err := p.Load(ctx, "plugin-morning/morning.wasm") 30 | if err != nil { 31 | return err 32 | } 33 | defer morningPlugin.Close(ctx) 34 | 35 | eveningPlugin, err := p.Load(ctx, "plugin-evening/evening.wasm") 36 | if err != nil { 37 | return err 38 | } 39 | defer eveningPlugin.Close(ctx) 40 | 41 | reply, err := morningPlugin.Greet(ctx, &greeting.GreetRequest{ 42 | Name: "go-plugin", 43 | }) 44 | if err != nil { 45 | return err 46 | } 47 | 48 | fmt.Println(reply.GetMessage()) 49 | 50 | reply, err = eveningPlugin.Greet(ctx, &greeting.GreetRequest{ 51 | Name: "go-plugin", 52 | }) 53 | if err != nil { 54 | return err 55 | } 56 | 57 | fmt.Println(reply.GetMessage()) 58 | 59 | return nil 60 | } 61 | -------------------------------------------------------------------------------- /examples/known-types/known/known_plugin.pb.go: -------------------------------------------------------------------------------- 1 | //go:build tinygo.wasm 2 | 3 | // Code generated by protoc-gen-go-plugin. DO NOT EDIT. 4 | // versions: 5 | // protoc-gen-go-plugin v0.1.0 6 | // protoc v3.21.12 7 | // source: examples/known-types/known/known.proto 8 | 9 | package known 10 | 11 | import ( 12 | context "context" 13 | wasm "github.com/knqyf263/go-plugin/wasm" 14 | ) 15 | 16 | const WellKnownPluginAPIVersion = 1 17 | 18 | //export well_known_api_version 19 | func _well_known_api_version() uint64 { 20 | return WellKnownPluginAPIVersion 21 | } 22 | 23 | var wellKnown WellKnown 24 | 25 | func RegisterWellKnown(p WellKnown) { 26 | wellKnown = p 27 | } 28 | 29 | //export well_known_diff 30 | func _well_known_diff(ptr, size uint32) uint64 { 31 | b := wasm.PtrToByte(ptr, size) 32 | req := new(DiffRequest) 33 | if err := req.UnmarshalVT(b); err != nil { 34 | return 0 35 | } 36 | response, err := wellKnown.Diff(context.Background(), req) 37 | if err != nil { 38 | ptr, size = wasm.ByteToPtr([]byte(err.Error())) 39 | return (uint64(ptr) << uint64(32)) | uint64(size) | 40 | // Indicate that this is the error string by setting the 32-th bit, assuming that 41 | // no data exceeds 31-bit size (2 GiB). 42 | (1 << 31) 43 | } 44 | 45 | b, err = response.MarshalVT() 46 | if err != nil { 47 | return 0 48 | } 49 | ptr, size = wasm.ByteToPtr(b) 50 | return (uint64(ptr) << uint64(32)) | uint64(size) 51 | } 52 | -------------------------------------------------------------------------------- /types/known/emptypb/empty.pb.go: -------------------------------------------------------------------------------- 1 | // Protocol Buffers - Google's data interchange format 2 | // Copyright 2008 Google Inc. All rights reserved. 3 | // Copyright 2022 Teppei Fukuda. All rights reserved. 4 | // https://developers.google.com/protocol-buffers/ 5 | 6 | // Code generated by protoc-gen-go-plugin. DO NOT EDIT. 7 | // versions: 8 | // protoc-gen-go-plugin v0.1.0 9 | // protoc v3.21.12 10 | // source: types/known/emptypb/empty.proto 11 | 12 | package emptypb 13 | 14 | import ( 15 | protoreflect "google.golang.org/protobuf/reflect/protoreflect" 16 | protoimpl "google.golang.org/protobuf/runtime/protoimpl" 17 | ) 18 | 19 | const ( 20 | // Verify that this generated code is sufficiently up-to-date. 21 | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) 22 | // Verify that runtime/protoimpl is sufficiently up-to-date. 23 | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) 24 | ) 25 | 26 | // A generic empty message that you can re-use to avoid defining duplicated 27 | // empty messages in your APIs. A typical example is to use it as the request 28 | // or the response type of an API method. For instance: 29 | // 30 | // service Foo { 31 | // rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); 32 | // } 33 | type Empty struct { 34 | state protoimpl.MessageState 35 | sizeCache protoimpl.SizeCache 36 | unknownFields protoimpl.UnknownFields 37 | } 38 | 39 | func (x *Empty) ProtoReflect() protoreflect.Message { 40 | panic(`not implemented`) 41 | } 42 | -------------------------------------------------------------------------------- /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | name: Test 2 | on: 3 | push: 4 | branches: 5 | - main 6 | paths-ignore: 7 | - '*.md' 8 | - 'imgs/**' 9 | - 'LICENSE' 10 | pull_request: 11 | env: 12 | GO_VERSION: "1.18" 13 | PROTOC_VERSION: "21.12" 14 | 15 | jobs: 16 | test: 17 | name: Test (TinyGo ${{ matrix.tinygo-version }}) 18 | runs-on: ubuntu-latest 19 | strategy: 20 | fail-fast: false 21 | matrix: 22 | tinygo-version: 23 | - "0.25.0" 24 | - "0.26.0" 25 | - "0.27.0" 26 | 27 | steps: 28 | - uses: actions/checkout@v3 29 | 30 | - name: Set up Go 31 | uses: actions/setup-go@v3 32 | with: 33 | go-version-file: go.mod 34 | 35 | - name: go mod tidy 36 | run: | 37 | go mod tidy 38 | if [ -n "$(git status --porcelain)" ]; then 39 | echo "Run 'go mod tidy' and push it" 40 | exit 1 41 | fi 42 | 43 | - name: Install protoc 44 | run: | 45 | wget $PB_REL/download/v${PROTOC_VERSION}/protoc-${PROTOC_VERSION}-linux-x86_64.zip 46 | unzip protoc-${PROTOC_VERSION}-linux-x86_64.zip -d /usr/local/bin/ 47 | env: 48 | PB_REL: https://github.com/protocolbuffers/protobuf/releases 49 | 50 | - name: Install TinyGo 51 | run: | 52 | wget https://github.com/tinygo-org/tinygo/releases/download/v${{ matrix.tinygo-version }}/tinygo_${{ matrix.tinygo-version }}_amd64.deb 53 | sudo dpkg -i tinygo_${{ matrix.tinygo-version }}_amd64.deb 54 | 55 | - name: Run unit tests 56 | run: make test 57 | -------------------------------------------------------------------------------- /types/known/wrapperspb/wrappers.go: -------------------------------------------------------------------------------- 1 | // Protocol Buffers - Google's data interchange format 2 | // Copyright 2008 Google Inc. All rights reserved. 3 | // Copyright 2022 Teppei Fukuda. All rights reserved. 4 | // https://developers.google.com/protocol-buffers/ 5 | 6 | package wrapperspb 7 | 8 | // Double stores v in a new DoubleValue and returns a pointer to it. 9 | func Double(v float64) *DoubleValue { 10 | return &DoubleValue{Value: v} 11 | } 12 | 13 | // Float stores v in a new FloatValue and returns a pointer to it. 14 | func Float(v float32) *FloatValue { 15 | return &FloatValue{Value: v} 16 | } 17 | 18 | // Int64 stores v in a new Int64Value and returns a pointer to it. 19 | func Int64(v int64) *Int64Value { 20 | return &Int64Value{Value: v} 21 | } 22 | 23 | // UInt64 stores v in a new UInt64Value and returns a pointer to it. 24 | func UInt64(v uint64) *UInt64Value { 25 | return &UInt64Value{Value: v} 26 | } 27 | 28 | // Int32 stores v in a new Int32Value and returns a pointer to it. 29 | func Int32(v int32) *Int32Value { 30 | return &Int32Value{Value: v} 31 | } 32 | 33 | // UInt32 stores v in a new UInt32Value and returns a pointer to it. 34 | func UInt32(v uint32) *UInt32Value { 35 | return &UInt32Value{Value: v} 36 | } 37 | 38 | // Bool stores v in a new BoolValue and returns a pointer to it. 39 | func Bool(v bool) *BoolValue { 40 | return &BoolValue{Value: v} 41 | } 42 | 43 | // String stores v in a new StringValue and returns a pointer to it. 44 | func String(v string) *StringValue { 45 | return &StringValue{Value: v} 46 | } 47 | 48 | // Bytes stores v in a new BytesValue and returns a pointer to it. 49 | func Bytes(v []byte) *BytesValue { 50 | return &BytesValue{Value: v} 51 | } 52 | -------------------------------------------------------------------------------- /examples/host-function-library/main.go: -------------------------------------------------------------------------------- 1 | //go:build !tinygo.wasm 2 | 3 | //go:generate tinygo build -o plugin/plugin.wasm -scheduler=none -target=wasi --no-debug plugin/plugin.go 4 | 5 | package main 6 | 7 | import ( 8 | "context" 9 | "fmt" 10 | "log" 11 | 12 | "github.com/tetratelabs/wazero" 13 | 14 | "github.com/knqyf263/go-plugin/examples/host-function-library/library/json-parser/export" 15 | "github.com/knqyf263/go-plugin/examples/host-function-library/library/json-parser/impl" 16 | "github.com/knqyf263/go-plugin/examples/host-function-library/proto" 17 | ) 18 | 19 | func main() { 20 | if err := run(); err != nil { 21 | log.Fatal(err) 22 | } 23 | } 24 | 25 | func run() error { 26 | ctx := context.Background() 27 | p, err := proto.NewGreeterPlugin(ctx, proto.WazeroRuntime(func(ctx context.Context) (wazero.Runtime, error) { 28 | r, err := proto.DefaultWazeroRuntime()(ctx) 29 | if err != nil { 30 | return nil, err 31 | } 32 | return r, export.Instantiate(ctx, r, impl.ParserLibraryImpl{}) 33 | })) 34 | 35 | // Pass my host functions that are embedded into the plugin. 36 | plugin, err := p.Load(ctx, "plugin/plugin.wasm", myHostFunctions{}) 37 | if err != nil { 38 | return err 39 | } 40 | 41 | defer plugin.Close(ctx) 42 | 43 | reply, err := plugin.Greet(ctx, &proto.GreetRequest{ 44 | Name: "Sato", 45 | }) 46 | 47 | fmt.Println(reply.GetMessage()) 48 | 49 | return nil 50 | } 51 | 52 | type myHostFunctions struct{} 53 | 54 | var _ proto.HostFunctions = (*myHostFunctions)(nil) 55 | 56 | func (m myHostFunctions) San(_ context.Context, request *proto.SanRequest) (*proto.SanResponse, error) { 57 | return &proto.SanResponse{Message: fmt.Sprintf("%s-san", request.GetMessage())}, nil 58 | } 59 | -------------------------------------------------------------------------------- /tests/import/proto/bar/bar.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go-plugin. DO NOT EDIT. 2 | // versions: 3 | // protoc-gen-go-plugin v0.1.0 4 | // protoc v3.21.12 5 | // source: tests/import/proto/bar/bar.proto 6 | 7 | package bar 8 | 9 | import ( 10 | context "context" 11 | protoreflect "google.golang.org/protobuf/reflect/protoreflect" 12 | protoimpl "google.golang.org/protobuf/runtime/protoimpl" 13 | ) 14 | 15 | const ( 16 | // Verify that this generated code is sufficiently up-to-date. 17 | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) 18 | // Verify that runtime/protoimpl is sufficiently up-to-date. 19 | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) 20 | ) 21 | 22 | type Request struct { 23 | state protoimpl.MessageState 24 | sizeCache protoimpl.SizeCache 25 | unknownFields protoimpl.UnknownFields 26 | 27 | A string `protobuf:"bytes,1,opt,name=a,proto3" json:"a,omitempty"` 28 | } 29 | 30 | func (x *Request) ProtoReflect() protoreflect.Message { 31 | panic(`not implemented`) 32 | } 33 | 34 | func (x *Request) GetA() string { 35 | if x != nil { 36 | return x.A 37 | } 38 | return "" 39 | } 40 | 41 | type Reply struct { 42 | state protoimpl.MessageState 43 | sizeCache protoimpl.SizeCache 44 | unknownFields protoimpl.UnknownFields 45 | 46 | A string `protobuf:"bytes,1,opt,name=a,proto3" json:"a,omitempty"` 47 | } 48 | 49 | func (x *Reply) ProtoReflect() protoreflect.Message { 50 | panic(`not implemented`) 51 | } 52 | 53 | func (x *Reply) GetA() string { 54 | if x != nil { 55 | return x.A 56 | } 57 | return "" 58 | } 59 | 60 | // go:plugin type=plugin version=1 61 | type Bar interface { 62 | Hello(context.Context, *Request) (*Reply, error) 63 | } 64 | -------------------------------------------------------------------------------- /examples/wasi/README.md: -------------------------------------------------------------------------------- 1 | # WASI Example 2 | This example shows how to access the local files. 3 | 4 | ## Generate Go code 5 | A proto file is under `./cat`. 6 | 7 | ```shell 8 | $ protoc --go-plugin_out=. --go-plugin_opt=paths=source_relative cat/cat.proto 9 | ``` 10 | 11 | ## Pass fs.FS in a host 12 | A host can control which files/dirs plugins can access. 13 | 14 | ```go 15 | import ( 16 | "github.com/knqyf263/go-plugin/options" 17 | ) 18 | 19 | //go:embed testdata/hello.txt 20 | 21 | var f embed.FS 22 | 23 | func run() error { 24 | ctx := context.Background() 25 | mc := wazero.NewModuleConfig(). 26 | WithStdout(os.Stdout). // Attach stdout so that the plugin can write outputs to stdout 27 | WithStderr(os.Stderr). // Attach stderr so that the plugin can write errors to stderr 28 | WithFS(f) // Loaded plugins can access only files that the host allows. 29 | p, err := cat.NewFileCatPlugin(ctx, cat.WazeroModuleConfig(mc)) 30 | ``` 31 | 32 | In this example, the host just passes `testdata/hello.txt` via `FileCatPluginOption`, but you can pass whatever you want. 33 | Please refer to [io/fs][io/fs]. 34 | 35 | ## Open a file in a plugin 36 | A plugin can open a file as usual. 37 | 38 | ```go 39 | b, err := os.ReadFile(request.GetFilePath()) 40 | if err != nil { 41 | return cat.FileCatReply{}, err 42 | } 43 | ``` 44 | 45 | ## Compile a plugin 46 | Use TinyGo to compile the plugin to Wasm. 47 | 48 | ```shell 49 | $ tinygo build -o plugin/plugin.wasm -scheduler=none -target=wasi --no-debug plugin/plugin.go 50 | ``` 51 | 52 | ## Run 53 | `main.go` loads the above plugin. 54 | 55 | ```shell 56 | $ go run main.go 57 | File loading... 58 | Hello WASI! 59 | ``` 60 | 61 | [io/fs]: https://pkg.go.dev/io/fs -------------------------------------------------------------------------------- /types/known/sourcecontextpb/source_context.pb.go: -------------------------------------------------------------------------------- 1 | // Protocol Buffers - Google's data interchange format 2 | // Copyright 2008 Google Inc. All rights reserved. 3 | // Copyright 2022 Teppei Fukuda. All rights reserved. 4 | // https://developers.google.com/protocol-buffers/ 5 | 6 | // Code generated by protoc-gen-go-plugin. DO NOT EDIT. 7 | // versions: 8 | // protoc-gen-go-plugin v0.1.0 9 | // protoc v3.21.12 10 | // source: types/known/sourcecontextpb/source_context.proto 11 | 12 | package sourcecontextpb 13 | 14 | import ( 15 | protoreflect "google.golang.org/protobuf/reflect/protoreflect" 16 | protoimpl "google.golang.org/protobuf/runtime/protoimpl" 17 | ) 18 | 19 | const ( 20 | // Verify that this generated code is sufficiently up-to-date. 21 | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) 22 | // Verify that runtime/protoimpl is sufficiently up-to-date. 23 | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) 24 | ) 25 | 26 | // `SourceContext` represents information about the source of a 27 | // protobuf element, like the file in which it is defined. 28 | type SourceContext struct { 29 | state protoimpl.MessageState 30 | sizeCache protoimpl.SizeCache 31 | unknownFields protoimpl.UnknownFields 32 | 33 | // The path-qualified name of the .proto file that contained the associated 34 | // protobuf element. For example: `"google/protobuf/source_context.proto"`. 35 | FileName string `protobuf:"bytes,1,opt,name=file_name,json=fileName,proto3" json:"file_name,omitempty"` 36 | } 37 | 38 | func (x *SourceContext) ProtoReflect() protoreflect.Message { 39 | panic(`not implemented`) 40 | } 41 | 42 | func (x *SourceContext) GetFileName() string { 43 | if x != nil { 44 | return x.FileName 45 | } 46 | return "" 47 | } 48 | -------------------------------------------------------------------------------- /tests/well-known/proto/known.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package greeting; 3 | 4 | import "google/protobuf/duration.proto"; 5 | import "google/protobuf/empty.proto"; 6 | import "google/protobuf/struct.proto"; 7 | import "google/protobuf/timestamp.proto"; 8 | import "google/protobuf/wrappers.proto"; 9 | 10 | option go_package = "github.com/knqyf263/go-plugin/test/well-known/proto"; 11 | 12 | // go:plugin type=plugin version=1 13 | service KnownTypesTest { 14 | rpc Test(Request) returns (Response) {} 15 | } 16 | 17 | // go:plugin type=plugin version=1 18 | service EmptyTest { 19 | rpc DoNothing(google.protobuf.Empty) returns (google.protobuf.Empty) {} 20 | } 21 | 22 | message Request { 23 | // duration 24 | google.protobuf.Duration a = 1; 25 | 26 | // timestamp 27 | google.protobuf.Timestamp b = 2; 28 | 29 | // struct 30 | google.protobuf.Value c = 3; 31 | 32 | // wrappers 33 | google.protobuf.BoolValue d = 4; 34 | google.protobuf.BytesValue e = 5; 35 | google.protobuf.DoubleValue f = 6; 36 | google.protobuf.FloatValue g = 7; 37 | google.protobuf.Int32Value h = 8; 38 | google.protobuf.Int64Value i = 9; 39 | google.protobuf.StringValue j = 10; 40 | google.protobuf.UInt32Value k = 11; 41 | google.protobuf.UInt64Value l = 12; 42 | } 43 | 44 | message Response { 45 | google.protobuf.Duration a = 1; 46 | google.protobuf.Timestamp b = 2; 47 | google.protobuf.Value c = 3; 48 | google.protobuf.BoolValue d = 4; 49 | google.protobuf.BytesValue e = 5; 50 | google.protobuf.DoubleValue f = 6; 51 | google.protobuf.FloatValue g = 7; 52 | google.protobuf.Int32Value h = 8; 53 | google.protobuf.Int64Value i = 9; 54 | google.protobuf.StringValue j = 10; 55 | google.protobuf.UInt32Value k = 11; 56 | google.protobuf.UInt64Value l = 12; 57 | } -------------------------------------------------------------------------------- /tests/fields/plugin/plugin.go: -------------------------------------------------------------------------------- 1 | //go:build tinygo.wasm 2 | 3 | package main 4 | 5 | import ( 6 | "context" 7 | "errors" 8 | 9 | "github.com/knqyf263/go-plugin/tests/fields/proto" 10 | "github.com/knqyf263/go-plugin/types/known/emptypb" 11 | ) 12 | 13 | // main is required for TinyGo to compile to Wasm. 14 | func main() { 15 | proto.RegisterFieldTest(TestPlugin{}) 16 | } 17 | 18 | var _ proto.FieldTest = (*TestPlugin)(nil) 19 | 20 | type TestPlugin struct{} 21 | 22 | func (p TestPlugin) TestEmptyInput(_ context.Context, _ *emptypb.Empty) (*proto.TestEmptyInputResponse, error) { 23 | return &proto.TestEmptyInputResponse{Ok: true}, nil 24 | } 25 | 26 | func (p TestPlugin) Test(_ context.Context, request *proto.Request) (*proto.Response, error) { 27 | return &proto.Response{ 28 | A: request.GetA() * 2, 29 | B: request.GetB() * 2, 30 | C: request.GetC() * 2, 31 | D: request.GetD() * 2, 32 | E: request.GetE() * 2, 33 | F: request.GetF() * 2, 34 | G: request.GetG() * 2, 35 | H: request.GetH() * 2, 36 | I: request.GetI() * 2, 37 | J: request.GetJ() * 2, 38 | K: request.GetK() * 2, 39 | L: request.GetL() * 2, 40 | M: !request.GetM(), 41 | N: request.GetN() + "bar", 42 | O: append(request.GetO(), []byte("fuga")...), 43 | P: request.GetP()[1:], 44 | Q: func() map[string]*proto.IntValue { 45 | q := request.GetQ() 46 | q["key"].A++ 47 | return q 48 | }(), 49 | R: func() *proto.Response_Nested { 50 | r := request.GetR() 51 | if r.A == "samurai" { 52 | return &proto.Response_Nested{ 53 | A: "ninja", 54 | } 55 | } 56 | return nil 57 | }(), 58 | S: request.GetS() + 1, 59 | }, nil 60 | } 61 | 62 | func (p TestPlugin) TestError(_ context.Context, request *proto.ErrorRequest) (*proto.Response, error) { 63 | return nil, errors.New(request.ErrText) 64 | } 65 | -------------------------------------------------------------------------------- /gen/options.go: -------------------------------------------------------------------------------- 1 | package gen 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func (gg *Generator) generateOptionsFile(f *fileInfo) { 8 | filename := f.GeneratedFilenamePrefix + "_options.pb.go" 9 | g := gg.plugin.NewGeneratedFile(filename, f.GoImportPath) 10 | 11 | if len(f.pluginServices) == 0 { 12 | g.Skip() 13 | } 14 | 15 | // Build constraints 16 | g.P("//go:build !tinygo.wasm") 17 | gg.generateHeader(g, f) 18 | 19 | g.P(fmt.Sprintf(`type wazeroConfigOption func(plugin *WazeroConfig) 20 | 21 | type WazeroNewRuntime func(%s) (%s, error) 22 | 23 | type WazeroConfig struct { 24 | newRuntime func(%s) (%s, error) 25 | moduleConfig %s 26 | } 27 | 28 | func WazeroRuntime(newRuntime WazeroNewRuntime) wazeroConfigOption { 29 | return func(h *WazeroConfig) { 30 | h.newRuntime = newRuntime 31 | } 32 | } 33 | 34 | func DefaultWazeroRuntime() WazeroNewRuntime { 35 | return func(ctx %s) (%s, error) { 36 | r := %s(ctx) 37 | if _, err := %s(ctx, r); err != nil { 38 | return nil, err 39 | } 40 | 41 | return r, nil 42 | } 43 | } 44 | 45 | func WazeroModuleConfig(moduleConfig %s) wazeroConfigOption { 46 | return func(h *WazeroConfig) { 47 | h.moduleConfig = moduleConfig 48 | } 49 | } 50 | `, 51 | g.QualifiedGoIdent(contextPackage.Ident("Context")), 52 | g.QualifiedGoIdent(wazeroPackage.Ident("Runtime")), 53 | g.QualifiedGoIdent(contextPackage.Ident("Context")), 54 | g.QualifiedGoIdent(wazeroPackage.Ident("Runtime")), 55 | g.QualifiedGoIdent(wazeroPackage.Ident("ModuleConfig")), 56 | g.QualifiedGoIdent(contextPackage.Ident("Context")), 57 | g.QualifiedGoIdent(wazeroPackage.Ident("Runtime")), 58 | g.QualifiedGoIdent(wazeroPackage.Ident("NewRuntime")), 59 | g.QualifiedGoIdent(wazeroWasiPackage.Ident("Instantiate")), 60 | g.QualifiedGoIdent(wazeroPackage.Ident("ModuleConfig")), 61 | )) 62 | } 63 | -------------------------------------------------------------------------------- /examples/wasi/cat/cat.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go-plugin. DO NOT EDIT. 2 | // versions: 3 | // protoc-gen-go-plugin v0.1.0 4 | // protoc v3.21.12 5 | // source: examples/wasi/cat/cat.proto 6 | 7 | package cat 8 | 9 | import ( 10 | context "context" 11 | protoreflect "google.golang.org/protobuf/reflect/protoreflect" 12 | protoimpl "google.golang.org/protobuf/runtime/protoimpl" 13 | ) 14 | 15 | const ( 16 | // Verify that this generated code is sufficiently up-to-date. 17 | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) 18 | // Verify that runtime/protoimpl is sufficiently up-to-date. 19 | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) 20 | ) 21 | 22 | type FileCatRequest struct { 23 | state protoimpl.MessageState 24 | sizeCache protoimpl.SizeCache 25 | unknownFields protoimpl.UnknownFields 26 | 27 | FilePath string `protobuf:"bytes,1,opt,name=file_path,json=filePath,proto3" json:"file_path,omitempty"` 28 | } 29 | 30 | func (x *FileCatRequest) ProtoReflect() protoreflect.Message { 31 | panic(`not implemented`) 32 | } 33 | 34 | func (x *FileCatRequest) GetFilePath() string { 35 | if x != nil { 36 | return x.FilePath 37 | } 38 | return "" 39 | } 40 | 41 | type FileCatReply struct { 42 | state protoimpl.MessageState 43 | sizeCache protoimpl.SizeCache 44 | unknownFields protoimpl.UnknownFields 45 | 46 | Content string `protobuf:"bytes,1,opt,name=content,proto3" json:"content,omitempty"` 47 | } 48 | 49 | func (x *FileCatReply) ProtoReflect() protoreflect.Message { 50 | panic(`not implemented`) 51 | } 52 | 53 | func (x *FileCatReply) GetContent() string { 54 | if x != nil { 55 | return x.Content 56 | } 57 | return "" 58 | } 59 | 60 | // go:plugin type=plugin 61 | type FileCat interface { 62 | Cat(context.Context, *FileCatRequest) (*FileCatReply, error) 63 | } 64 | -------------------------------------------------------------------------------- /examples/host-functions/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "io" 7 | "log" 8 | "net/http" 9 | 10 | "github.com/knqyf263/go-plugin/examples/host-functions/greeting" 11 | "github.com/knqyf263/go-plugin/types/known/emptypb" 12 | ) 13 | 14 | func main() { 15 | if err := run(); err != nil { 16 | log.Fatal(err) 17 | } 18 | } 19 | 20 | func run() error { 21 | ctx := context.Background() 22 | p, err := greeting.NewGreeterPlugin(ctx) 23 | if err != nil { 24 | return err 25 | } 26 | 27 | // Pass my host functions that are embedded into the plugin. 28 | greetingPlugin, err := p.Load(ctx, "plugin/plugin.wasm", myHostFunctions{}) 29 | if err != nil { 30 | return err 31 | } 32 | defer greetingPlugin.Close(ctx) 33 | 34 | reply, err := greetingPlugin.Greet(ctx, &greeting.GreetRequest{ 35 | Name: "go-plugin", 36 | }) 37 | if err != nil { 38 | return err 39 | } 40 | 41 | fmt.Println(reply.GetMessage()) 42 | 43 | return nil 44 | } 45 | 46 | // myHostFunctions implements greeting.HostFunctions 47 | type myHostFunctions struct{} 48 | 49 | var _ greeting.HostFunctions = (*myHostFunctions)(nil) 50 | 51 | // HttpGet is embedded into the plugin and can be called by the plugin. 52 | func (myHostFunctions) HttpGet(_ context.Context, request *greeting.HttpGetRequest) (*greeting.HttpGetResponse, error) { 53 | resp, err := http.Get(request.Url) 54 | if err != nil { 55 | return nil, err 56 | } 57 | defer resp.Body.Close() 58 | 59 | buf, err := io.ReadAll(resp.Body) 60 | if err != nil { 61 | return nil, err 62 | } 63 | 64 | return &greeting.HttpGetResponse{Response: buf}, nil 65 | } 66 | 67 | // Log is embedded into the plugin and can be called by the plugin. 68 | func (myHostFunctions) Log(_ context.Context, request *greeting.LogRequest) (*emptypb.Empty, error) { 69 | // Use the host logger 70 | log.Println(request.GetMessage()) 71 | return &emptypb.Empty{}, nil 72 | } 73 | -------------------------------------------------------------------------------- /examples/host-function-library/library/json-parser/export/library_host.pb.go: -------------------------------------------------------------------------------- 1 | //go:build !tinygo.wasm 2 | 3 | // Code generated by protoc-gen-go-plugin. DO NOT EDIT. 4 | // versions: 5 | // protoc-gen-go-plugin v0.1.0 6 | // protoc v3.21.12 7 | // source: examples/host-function-library/library/json-parser/export/library.proto 8 | 9 | package export 10 | 11 | import ( 12 | context "context" 13 | wasm "github.com/knqyf263/go-plugin/wasm" 14 | wazero "github.com/tetratelabs/wazero" 15 | api "github.com/tetratelabs/wazero/api" 16 | ) 17 | 18 | const ( 19 | i32 = api.ValueTypeI32 20 | i64 = api.ValueTypeI64 21 | ) 22 | 23 | type _parserLibrary struct { 24 | ParserLibrary 25 | } 26 | 27 | // Instantiate a Go-defined module named "json-parser" that exports host functions. 28 | func Instantiate(ctx context.Context, r wazero.Runtime, hostFunctions ParserLibrary) error { 29 | envBuilder := r.NewHostModuleBuilder("json-parser") 30 | h := _parserLibrary{hostFunctions} 31 | 32 | envBuilder.NewFunctionBuilder(). 33 | WithGoModuleFunction(api.GoModuleFunc(h._ParseJson), []api.ValueType{i32, i32}, []api.ValueType{i64}). 34 | WithParameterNames("offset", "size"). 35 | Export("parse_json") 36 | 37 | _, err := envBuilder.Instantiate(ctx) 38 | return err 39 | } 40 | 41 | func (h _parserLibrary) _ParseJson(ctx context.Context, m api.Module, stack []uint64) { 42 | offset, size := uint32(stack[0]), uint32(stack[1]) 43 | buf, err := wasm.ReadMemory(m.Memory(), offset, size) 44 | if err != nil { 45 | panic(err) 46 | } 47 | request := new(ParseJsonRequest) 48 | err = request.UnmarshalVT(buf) 49 | if err != nil { 50 | panic(err) 51 | } 52 | resp, err := h.ParseJson(ctx, request) 53 | if err != nil { 54 | panic(err) 55 | } 56 | buf, err = resp.MarshalVT() 57 | if err != nil { 58 | panic(err) 59 | } 60 | ptr, err := wasm.WriteMemory(ctx, m, buf) 61 | if err != nil { 62 | panic(err) 63 | } 64 | ptrLen := (ptr << uint64(32)) | uint64(len(buf)) 65 | stack[0] = ptrLen 66 | } 67 | -------------------------------------------------------------------------------- /examples/helloworld/greeting/greet.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go-plugin. DO NOT EDIT. 2 | // versions: 3 | // protoc-gen-go-plugin v0.1.0 4 | // protoc v3.21.12 5 | // source: examples/helloworld/greeting/greet.proto 6 | 7 | package greeting 8 | 9 | import ( 10 | context "context" 11 | protoreflect "google.golang.org/protobuf/reflect/protoreflect" 12 | protoimpl "google.golang.org/protobuf/runtime/protoimpl" 13 | ) 14 | 15 | const ( 16 | // Verify that this generated code is sufficiently up-to-date. 17 | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) 18 | // Verify that runtime/protoimpl is sufficiently up-to-date. 19 | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) 20 | ) 21 | 22 | // The request message containing the user's name. 23 | type GreetRequest struct { 24 | state protoimpl.MessageState 25 | sizeCache protoimpl.SizeCache 26 | unknownFields protoimpl.UnknownFields 27 | 28 | Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` 29 | } 30 | 31 | func (x *GreetRequest) ProtoReflect() protoreflect.Message { 32 | panic(`not implemented`) 33 | } 34 | 35 | func (x *GreetRequest) GetName() string { 36 | if x != nil { 37 | return x.Name 38 | } 39 | return "" 40 | } 41 | 42 | // The response message containing the greetings 43 | type GreetReply struct { 44 | state protoimpl.MessageState 45 | sizeCache protoimpl.SizeCache 46 | unknownFields protoimpl.UnknownFields 47 | 48 | Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` 49 | } 50 | 51 | func (x *GreetReply) ProtoReflect() protoreflect.Message { 52 | panic(`not implemented`) 53 | } 54 | 55 | func (x *GreetReply) GetMessage() string { 56 | if x != nil { 57 | return x.Message 58 | } 59 | return "" 60 | } 61 | 62 | // The greeting service definition. 63 | // go:plugin type=plugin version=1 64 | type Greeter interface { 65 | // Sends a greeting 66 | Greet(context.Context, *GreetRequest) (*GreetReply, error) 67 | } 68 | -------------------------------------------------------------------------------- /types/known/timestamppb/timestamp.pb.go: -------------------------------------------------------------------------------- 1 | // Protocol Buffers - Google's data interchange format 2 | // Copyright 2008 Google Inc. All rights reserved. 3 | // Copyright 2022 Teppei Fukuda. All rights reserved. 4 | // https://developers.google.com/protocol-buffers/ 5 | 6 | // Code generated by protoc-gen-go-plugin. DO NOT EDIT. 7 | // versions: 8 | // protoc-gen-go-plugin v0.1.0 9 | // protoc v3.21.12 10 | // source: types/known/timestamppb/timestamp.proto 11 | 12 | package timestamppb 13 | 14 | import ( 15 | protoreflect "google.golang.org/protobuf/reflect/protoreflect" 16 | protoimpl "google.golang.org/protobuf/runtime/protoimpl" 17 | ) 18 | 19 | const ( 20 | // Verify that this generated code is sufficiently up-to-date. 21 | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) 22 | // Verify that runtime/protoimpl is sufficiently up-to-date. 23 | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) 24 | ) 25 | 26 | type Timestamp struct { 27 | state protoimpl.MessageState 28 | sizeCache protoimpl.SizeCache 29 | unknownFields protoimpl.UnknownFields 30 | 31 | // Represents seconds of UTC time since Unix epoch 32 | // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to 33 | // 9999-12-31T23:59:59Z inclusive. 34 | Seconds int64 `protobuf:"varint,1,opt,name=seconds,proto3" json:"seconds,omitempty"` 35 | // Non-negative fractions of a second at nanosecond resolution. Negative 36 | // second values with fractions must still have non-negative nanos values 37 | // that count forward in time. Must be from 0 to 999,999,999 38 | // inclusive. 39 | Nanos int32 `protobuf:"varint,2,opt,name=nanos,proto3" json:"nanos,omitempty"` 40 | } 41 | 42 | func (x *Timestamp) ProtoReflect() protoreflect.Message { 43 | panic(`not implemented`) 44 | } 45 | 46 | func (x *Timestamp) GetSeconds() int64 { 47 | if x != nil { 48 | return x.Seconds 49 | } 50 | return 0 51 | } 52 | 53 | func (x *Timestamp) GetNanos() int32 { 54 | if x != nil { 55 | return x.Nanos 56 | } 57 | return 0 58 | } 59 | -------------------------------------------------------------------------------- /tests/well-known/plugin/plugin.go: -------------------------------------------------------------------------------- 1 | //go:build tinygo.wasm 2 | 3 | package main 4 | 5 | import ( 6 | "context" 7 | "time" 8 | 9 | "github.com/knqyf263/go-plugin/tests/well-known/proto" 10 | "github.com/knqyf263/go-plugin/types/known/durationpb" 11 | "github.com/knqyf263/go-plugin/types/known/emptypb" 12 | "github.com/knqyf263/go-plugin/types/known/structpb" 13 | "github.com/knqyf263/go-plugin/types/known/timestamppb" 14 | "github.com/knqyf263/go-plugin/types/known/wrapperspb" 15 | ) 16 | 17 | // main is required for TinyGo to compile to Wasm. 18 | func main() { 19 | proto.RegisterKnownTypesTest(TestPlugin{}) 20 | proto.RegisterEmptyTest(TestPlugin{}) 21 | } 22 | 23 | var _ proto.EmptyTest = (*TestPlugin)(nil) 24 | 25 | type TestPlugin struct{} 26 | 27 | func (p TestPlugin) Test(_ context.Context, request *proto.Request) (*proto.Response, error) { 28 | c, err := p.GetC(request.GetC()) 29 | if err != nil { 30 | return nil, err 31 | } 32 | return &proto.Response{ 33 | A: durationpb.New(2 * time.Minute), 34 | B: timestamppb.New(request.GetB().AsTime().Add(request.GetA().AsDuration())), 35 | C: c, 36 | D: wrapperspb.Bool(!request.GetD().Value), 37 | E: wrapperspb.Bytes(append(request.GetE().Value, []byte(`Value`)...)), 38 | F: wrapperspb.Double(request.GetF().Value * 2), 39 | G: wrapperspb.Float(request.GetG().Value * 2), 40 | H: wrapperspb.Int32(request.GetH().Value * 2), 41 | I: wrapperspb.Int64(request.GetI().Value * 2), 42 | J: wrapperspb.String(request.GetJ().Value + "Value"), 43 | K: wrapperspb.UInt32(request.GetK().Value * 2), 44 | L: wrapperspb.UInt64(request.GetL().Value * 2), 45 | }, nil 46 | } 47 | 48 | func (p TestPlugin) GetC(v *structpb.Value) (*structpb.Value, error) { 49 | c := v.AsInterface().(map[string]interface{}) 50 | c["CA"] = c["CA"].(string) + "BBB" 51 | c["CB"] = !c["CB"].(bool) 52 | c["CC"] = c["CC"].(float64) * 2 53 | c["CD"] = append(c["CD"].([]interface{}), "FOO") 54 | return structpb.NewValue(c) 55 | } 56 | 57 | func (p TestPlugin) DoNothing(_ context.Context, _ *emptypb.Empty) (*emptypb.Empty, error) { 58 | return nil, nil 59 | } 60 | -------------------------------------------------------------------------------- /types/known/wrapperspb/wrappers.proto: -------------------------------------------------------------------------------- 1 | // Protocol Buffers - Google's data interchange format 2 | // Copyright 2008 Google Inc. All rights reserved. 3 | // Copyright 2008 Teppei Fukuda. All rights reserved. 4 | // https://developers.google.com/protocol-buffers/ 5 | 6 | syntax = "proto3"; 7 | 8 | package google.protobuf; 9 | 10 | option go_package = "github.com/knqyf263/go-plugin/types/known/wrapperspb"; 11 | 12 | // Wrapper message for `double`. 13 | // 14 | // The JSON representation for `DoubleValue` is JSON number. 15 | message DoubleValue { 16 | // The double value. 17 | double value = 1; 18 | } 19 | 20 | // Wrapper message for `float`. 21 | // 22 | // The JSON representation for `FloatValue` is JSON number. 23 | message FloatValue { 24 | // The float value. 25 | float value = 1; 26 | } 27 | 28 | // Wrapper message for `int64`. 29 | // 30 | // The JSON representation for `Int64Value` is JSON string. 31 | message Int64Value { 32 | // The int64 value. 33 | int64 value = 1; 34 | } 35 | 36 | // Wrapper message for `uint64`. 37 | // 38 | // The JSON representation for `UInt64Value` is JSON string. 39 | message UInt64Value { 40 | // The uint64 value. 41 | uint64 value = 1; 42 | } 43 | 44 | // Wrapper message for `int32`. 45 | // 46 | // The JSON representation for `Int32Value` is JSON number. 47 | message Int32Value { 48 | // The int32 value. 49 | int32 value = 1; 50 | } 51 | 52 | // Wrapper message for `uint32`. 53 | // 54 | // The JSON representation for `UInt32Value` is JSON number. 55 | message UInt32Value { 56 | // The uint32 value. 57 | uint32 value = 1; 58 | } 59 | 60 | // Wrapper message for `bool`. 61 | // 62 | // The JSON representation for `BoolValue` is JSON `true` and `false`. 63 | message BoolValue { 64 | // The bool value. 65 | bool value = 1; 66 | } 67 | 68 | // Wrapper message for `string`. 69 | // 70 | // The JSON representation for `StringValue` is JSON string. 71 | message StringValue { 72 | // The string value. 73 | string value = 1; 74 | } 75 | 76 | // Wrapper message for `bytes`. 77 | // 78 | // The JSON representation for `BytesValue` is JSON string. 79 | message BytesValue { 80 | // The bytes value. 81 | bytes value = 1; 82 | } 83 | -------------------------------------------------------------------------------- /examples/host-function-library/proto/greet_plugin.pb.go: -------------------------------------------------------------------------------- 1 | //go:build tinygo.wasm 2 | 3 | // Code generated by protoc-gen-go-plugin. DO NOT EDIT. 4 | // versions: 5 | // protoc-gen-go-plugin v0.1.0 6 | // protoc v3.21.12 7 | // source: examples/host-function-library/proto/greet.proto 8 | 9 | package proto 10 | 11 | import ( 12 | context "context" 13 | wasm "github.com/knqyf263/go-plugin/wasm" 14 | _ "unsafe" 15 | ) 16 | 17 | const GreeterPluginAPIVersion = 1 18 | 19 | //export greeter_api_version 20 | func _greeter_api_version() uint64 { 21 | return GreeterPluginAPIVersion 22 | } 23 | 24 | var greeter Greeter 25 | 26 | func RegisterGreeter(p Greeter) { 27 | greeter = p 28 | } 29 | 30 | //export greeter_greet 31 | func _greeter_greet(ptr, size uint32) uint64 { 32 | b := wasm.PtrToByte(ptr, size) 33 | req := new(GreetRequest) 34 | if err := req.UnmarshalVT(b); err != nil { 35 | return 0 36 | } 37 | response, err := greeter.Greet(context.Background(), req) 38 | if err != nil { 39 | ptr, size = wasm.ByteToPtr([]byte(err.Error())) 40 | return (uint64(ptr) << uint64(32)) | uint64(size) | 41 | // Indicate that this is the error string by setting the 32-th bit, assuming that 42 | // no data exceeds 31-bit size (2 GiB). 43 | (1 << 31) 44 | } 45 | 46 | b, err = response.MarshalVT() 47 | if err != nil { 48 | return 0 49 | } 50 | ptr, size = wasm.ByteToPtr(b) 51 | return (uint64(ptr) << uint64(32)) | uint64(size) 52 | } 53 | 54 | type hostFunctions struct{} 55 | 56 | func NewHostFunctions() HostFunctions { 57 | return hostFunctions{} 58 | } 59 | 60 | //go:wasm-module env 61 | //export san 62 | //go:linkname _san 63 | func _san(ptr uint32, size uint32) uint64 64 | 65 | func (h hostFunctions) San(ctx context.Context, request *SanRequest) (*SanResponse, error) { 66 | buf, err := request.MarshalVT() 67 | if err != nil { 68 | return nil, err 69 | } 70 | ptr, size := wasm.ByteToPtr(buf) 71 | ptrSize := _san(ptr, size) 72 | wasm.FreePtr(ptr) 73 | 74 | ptr = uint32(ptrSize >> 32) 75 | size = uint32(ptrSize) 76 | buf = wasm.PtrToByte(ptr, size) 77 | 78 | response := new(SanResponse) 79 | if err = response.UnmarshalVT(buf); err != nil { 80 | return nil, err 81 | } 82 | return response, nil 83 | } 84 | -------------------------------------------------------------------------------- /tests/fields/proto/fields.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package greeting; 3 | 4 | import "google/protobuf/empty.proto"; 5 | 6 | option go_package = "github.com/knqyf263/go-plugin/test/fields/proto"; 7 | 8 | // go:plugin type=plugin version=1 9 | service FieldTest { 10 | rpc Test(Request) returns (Response) {} 11 | rpc TestEmptyInput(google.protobuf.Empty) returns (TestEmptyInputResponse) {} 12 | rpc TestError(ErrorRequest) returns (Response) {} 13 | } 14 | 15 | message TestEmptyInputResponse { 16 | bool ok = 1; 17 | } 18 | 19 | enum Enum { 20 | A = 0; 21 | B = 1; 22 | C = 2; 23 | } 24 | 25 | message Request { 26 | double a = 1; 27 | float b = 2; 28 | int32 c = 3; 29 | int64 d = 4; 30 | uint32 e = 5; 31 | uint64 f = 6; 32 | sint32 g = 7; 33 | sint64 h = 8; 34 | fixed32 i = 9; 35 | fixed64 j = 10; 36 | sfixed32 k = 11; 37 | sfixed64 l = 12; 38 | bool m = 13; 39 | string n = 14; 40 | bytes o = 15; 41 | repeated string p = 16; 42 | map q = 17; 43 | Nested r = 18; 44 | Enum s = 19; 45 | 46 | message Nested { 47 | string a = 1; 48 | } 49 | } 50 | 51 | message IntValue { 52 | int32 a = 1; 53 | } 54 | 55 | message Response { 56 | double a = 1; 57 | float b = 2; 58 | int32 c = 3; 59 | int64 d = 4; 60 | uint32 e = 5; 61 | uint64 f = 6; 62 | sint32 g = 7; 63 | sint64 h = 8; 64 | fixed32 i = 9; 65 | fixed64 j = 10; 66 | sfixed32 k = 11; 67 | sfixed64 l = 12; 68 | bool m = 13; 69 | string n = 14; 70 | bytes o = 15; 71 | repeated string p = 16; 72 | map q = 17; 73 | Nested r = 18; 74 | Enum s = 19; 75 | 76 | message Nested { 77 | string a = 1; 78 | } 79 | } 80 | 81 | message ErrorRequest { 82 | string errText = 1; 83 | } 84 | -------------------------------------------------------------------------------- /tests/host-functions/proto/host_plugin.pb.go: -------------------------------------------------------------------------------- 1 | //go:build tinygo.wasm 2 | 3 | // Code generated by protoc-gen-go-plugin. DO NOT EDIT. 4 | // versions: 5 | // protoc-gen-go-plugin v0.1.0 6 | // protoc v3.21.12 7 | // source: tests/host-functions/proto/host.proto 8 | 9 | package proto 10 | 11 | import ( 12 | context "context" 13 | wasm "github.com/knqyf263/go-plugin/wasm" 14 | _ "unsafe" 15 | ) 16 | 17 | const GreeterPluginAPIVersion = 1 18 | 19 | //export greeter_api_version 20 | func _greeter_api_version() uint64 { 21 | return GreeterPluginAPIVersion 22 | } 23 | 24 | var greeter Greeter 25 | 26 | func RegisterGreeter(p Greeter) { 27 | greeter = p 28 | } 29 | 30 | //export greeter_greet 31 | func _greeter_greet(ptr, size uint32) uint64 { 32 | b := wasm.PtrToByte(ptr, size) 33 | req := new(GreetRequest) 34 | if err := req.UnmarshalVT(b); err != nil { 35 | return 0 36 | } 37 | response, err := greeter.Greet(context.Background(), req) 38 | if err != nil { 39 | ptr, size = wasm.ByteToPtr([]byte(err.Error())) 40 | return (uint64(ptr) << uint64(32)) | uint64(size) | 41 | // Indicate that this is the error string by setting the 32-th bit, assuming that 42 | // no data exceeds 31-bit size (2 GiB). 43 | (1 << 31) 44 | } 45 | 46 | b, err = response.MarshalVT() 47 | if err != nil { 48 | return 0 49 | } 50 | ptr, size = wasm.ByteToPtr(b) 51 | return (uint64(ptr) << uint64(32)) | uint64(size) 52 | } 53 | 54 | type hostFunctions struct{} 55 | 56 | func NewHostFunctions() HostFunctions { 57 | return hostFunctions{} 58 | } 59 | 60 | //go:wasm-module env 61 | //export parse_json 62 | //go:linkname _parse_json 63 | func _parse_json(ptr uint32, size uint32) uint64 64 | 65 | func (h hostFunctions) ParseJson(ctx context.Context, request *ParseJsonRequest) (*ParseJsonResponse, error) { 66 | buf, err := request.MarshalVT() 67 | if err != nil { 68 | return nil, err 69 | } 70 | ptr, size := wasm.ByteToPtr(buf) 71 | ptrSize := _parse_json(ptr, size) 72 | wasm.FreePtr(ptr) 73 | 74 | ptr = uint32(ptrSize >> 32) 75 | size = uint32(ptrSize) 76 | buf = wasm.PtrToByte(ptr, size) 77 | 78 | response := new(ParseJsonResponse) 79 | if err = response.UnmarshalVT(buf); err != nil { 80 | return nil, err 81 | } 82 | return response, nil 83 | } 84 | -------------------------------------------------------------------------------- /types/known/structpb/struct.proto: -------------------------------------------------------------------------------- 1 | // Protocol Buffers - Google's data interchange format 2 | // Copyright 2008 Google Inc. All rights reserved. 3 | // Copyright 2022 Teppei Fukuda. All rights reserved. 4 | // https://developers.google.com/protocol-buffers/ 5 | 6 | syntax = "proto3"; 7 | 8 | package google.protobuf; 9 | 10 | option go_package = "github.com/knqyf263/go-plugin/types/known/structpb"; 11 | 12 | // `Struct` represents a structured data value, consisting of fields 13 | // which map to dynamically typed values. In some languages, `Struct` 14 | // might be supported by a native representation. For example, in 15 | // scripting languages like JS a struct is represented as an 16 | // object. The details of that representation are described together 17 | // with the proto support for the language. 18 | // 19 | // The JSON representation for `Struct` is JSON object. 20 | message Struct { 21 | // Unordered map of dynamically typed values. 22 | map fields = 1; 23 | } 24 | 25 | // `Value` represents a dynamically typed value which can be either 26 | // null, a number, a string, a boolean, a recursive struct value, or a 27 | // list of values. A producer of value is expected to set one of these 28 | // variants. Absence of any variant indicates an error. 29 | // 30 | // The JSON representation for `Value` is JSON value. 31 | message Value { 32 | // The kind of value. 33 | oneof kind { 34 | // Represents a null value. 35 | NullValue null_value = 1; 36 | // Represents a double value. 37 | double number_value = 2; 38 | // Represents a string value. 39 | string string_value = 3; 40 | // Represents a boolean value. 41 | bool bool_value = 4; 42 | // Represents a structured value. 43 | Struct struct_value = 5; 44 | // Represents a repeated `Value`. 45 | ListValue list_value = 6; 46 | } 47 | } 48 | 49 | // `NullValue` is a singleton enumeration to represent the null value for the 50 | // `Value` type union. 51 | // 52 | // The JSON representation for `NullValue` is JSON `null`. 53 | enum NullValue { 54 | // Null value. 55 | NULL_VALUE = 0; 56 | } 57 | 58 | // `ListValue` is a wrapper around a repeated field of values. 59 | // 60 | // The JSON representation for `ListValue` is JSON array. 61 | message ListValue { 62 | // Repeated field of dynamically typed values. 63 | repeated Value values = 1; 64 | } 65 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 4 | github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= 5 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 6 | github.com/planetscale/vtprotobuf v0.3.0 h1:oMrOdDFHS1ADc0dHtC2EApxiM5xd0cQkZeibm0WgXiQ= 7 | github.com/planetscale/vtprotobuf v0.3.0/go.mod h1:wm1N3qk9G/4+VM1WhpkLbvY/d8+0PbwYYpP5P5VhTks= 8 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 9 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 10 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 11 | github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= 12 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 13 | github.com/tetratelabs/wazero v1.0.2 h1:lpwL5zczFHk2mxKur98035Gig+Z3vd9JURk6lUdZxXY= 14 | github.com/tetratelabs/wazero v1.0.2/go.mod h1:wYx2gNRg8/WihJfSDxA1TIL8H+GkfLYm+bIfbblu9VQ= 15 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 16 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= 17 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 18 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 19 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 20 | google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= 21 | google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 22 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 23 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 24 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 25 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 26 | -------------------------------------------------------------------------------- /tests/fields/fields_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | "github.com/stretchr/testify/require" 9 | 10 | "github.com/knqyf263/go-plugin/tests/fields/proto" 11 | "github.com/knqyf263/go-plugin/types/known/emptypb" 12 | ) 13 | 14 | func TestFields(t *testing.T) { 15 | ctx := context.Background() 16 | p, err := proto.NewFieldTestPlugin(ctx) 17 | require.NoError(t, err) 18 | 19 | plugin, err := p.Load(ctx, "plugin/plugin.wasm") 20 | require.NoError(t, err) 21 | defer plugin.Close(ctx) 22 | 23 | res, err := plugin.TestEmptyInput(ctx, &emptypb.Empty{}) 24 | require.NoError(t, err) 25 | require.True(t, res.GetOk()) 26 | 27 | got, err := plugin.Test(ctx, &proto.Request{ 28 | A: 1.2, 29 | B: 3.4, 30 | C: 5, 31 | D: -6, 32 | E: 7, 33 | F: 8, 34 | G: 9, 35 | H: -10, 36 | I: 11, 37 | J: 12, 38 | K: 13, 39 | L: -14, 40 | M: false, 41 | N: "foo", 42 | O: []byte("hoge"), 43 | P: []string{"a", "b"}, 44 | Q: map[string]*proto.IntValue{ 45 | "key": {A: 15}, 46 | }, 47 | R: &proto.Request_Nested{ 48 | A: "samurai", 49 | }, 50 | S: proto.Enum_A, 51 | }) 52 | 53 | want := &proto.Response{ 54 | A: 2.4, 55 | B: 6.8, 56 | C: 10, 57 | D: -12, 58 | E: 14, 59 | F: 16, 60 | G: 18, 61 | H: -20, 62 | I: 22, 63 | J: 24, 64 | K: 26, 65 | L: -28, 66 | M: true, 67 | N: "foobar", 68 | O: []byte("hogefuga"), 69 | P: []string{"b"}, 70 | Q: map[string]*proto.IntValue{ 71 | "key": {A: 16}, 72 | }, 73 | R: &proto.Response_Nested{ 74 | A: "ninja", 75 | }, 76 | S: proto.Enum_B, 77 | } 78 | assert.Equal(t, want, got) 79 | } 80 | 81 | func TestErrorResponse(t *testing.T) { 82 | ctx := context.Background() 83 | p, err := proto.NewFieldTestPlugin(ctx) 84 | require.NoError(t, err) 85 | 86 | plugin, err := p.Load(ctx, "plugin/plugin.wasm") 87 | require.NoError(t, err) 88 | defer plugin.Close(ctx) 89 | 90 | for _, tt := range []struct { 91 | name string 92 | errMessage string 93 | }{{ 94 | "empty", 95 | "", 96 | }, { 97 | "normal", 98 | "error from plugin", 99 | }} { 100 | t.Run(tt.name, func(t *testing.T) { 101 | _, err := plugin.TestError(ctx, &proto.ErrorRequest{ErrText: tt.errMessage}) 102 | require.Error(t, err) 103 | require.Equal(t, err.Error(), tt.errMessage) 104 | }) 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /types/known/timestamppb/timestamp.go: -------------------------------------------------------------------------------- 1 | // Protocol Buffers - Google's data interchange format 2 | // Copyright 2008 Google Inc. All rights reserved. 3 | // Copyright 2022 Teppei Fukuda. All rights reserved. 4 | // https://developers.google.com/protocol-buffers/ 5 | 6 | package timestamppb 7 | 8 | import ( 9 | "time" 10 | 11 | "google.golang.org/protobuf/runtime/protoimpl" 12 | ) 13 | 14 | // Now constructs a new Timestamp from the current time. 15 | func Now() *Timestamp { 16 | return New(time.Now()) 17 | } 18 | 19 | // New constructs a new Timestamp from the provided time.Time. 20 | func New(t time.Time) *Timestamp { 21 | return &Timestamp{Seconds: int64(t.Unix()), Nanos: int32(t.Nanosecond())} 22 | } 23 | 24 | // AsTime converts x to a time.Time. 25 | func (x *Timestamp) AsTime() time.Time { 26 | return time.Unix(int64(x.GetSeconds()), int64(x.GetNanos())).UTC() 27 | } 28 | 29 | // IsValid reports whether the timestamp is valid. 30 | // It is equivalent to CheckValid == nil. 31 | func (x *Timestamp) IsValid() bool { 32 | return x.check() == 0 33 | } 34 | 35 | // CheckValid returns an error if the timestamp is invalid. 36 | // In particular, it checks whether the value represents a date that is 37 | // in the range of 0001-01-01T00:00:00Z to 9999-12-31T23:59:59Z inclusive. 38 | // An error is reported for a nil Timestamp. 39 | func (x *Timestamp) CheckValid() error { 40 | switch x.check() { 41 | case invalidNil: 42 | return protoimpl.X.NewError("invalid nil Timestamp") 43 | case invalidUnderflow: 44 | return protoimpl.X.NewError("timestamp (%v) before 0001-01-01", x) 45 | case invalidOverflow: 46 | return protoimpl.X.NewError("timestamp (%v) after 9999-12-31", x) 47 | case invalidNanos: 48 | return protoimpl.X.NewError("timestamp (%v) has out-of-range nanos", x) 49 | default: 50 | return nil 51 | } 52 | } 53 | 54 | const ( 55 | _ = iota 56 | invalidNil 57 | invalidUnderflow 58 | invalidOverflow 59 | invalidNanos 60 | ) 61 | 62 | func (x *Timestamp) check() uint { 63 | const minTimestamp = -62135596800 // Seconds between 1970-01-01T00:00:00Z and 0001-01-01T00:00:00Z, inclusive 64 | const maxTimestamp = +253402300799 // Seconds between 1970-01-01T00:00:00Z and 9999-12-31T23:59:59Z, inclusive 65 | secs := x.GetSeconds() 66 | nanos := x.GetNanos() 67 | switch { 68 | case x == nil: 69 | return invalidNil 70 | case secs < minTimestamp: 71 | return invalidUnderflow 72 | case secs > maxTimestamp: 73 | return invalidOverflow 74 | case nanos < 0 || nanos >= 1e9: 75 | return invalidNanos 76 | default: 77 | return 0 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /examples/known-types/known/known.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go-plugin. DO NOT EDIT. 2 | // versions: 3 | // protoc-gen-go-plugin v0.1.0 4 | // protoc v3.21.12 5 | // source: examples/known-types/known/known.proto 6 | 7 | package known 8 | 9 | import ( 10 | context "context" 11 | durationpb "github.com/knqyf263/go-plugin/types/known/durationpb" 12 | structpb "github.com/knqyf263/go-plugin/types/known/structpb" 13 | timestamppb "github.com/knqyf263/go-plugin/types/known/timestamppb" 14 | protoreflect "google.golang.org/protobuf/reflect/protoreflect" 15 | protoimpl "google.golang.org/protobuf/runtime/protoimpl" 16 | ) 17 | 18 | const ( 19 | // Verify that this generated code is sufficiently up-to-date. 20 | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) 21 | // Verify that runtime/protoimpl is sufficiently up-to-date. 22 | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) 23 | ) 24 | 25 | type DiffRequest struct { 26 | state protoimpl.MessageState 27 | sizeCache protoimpl.SizeCache 28 | unknownFields protoimpl.UnknownFields 29 | 30 | Value *structpb.Value `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` 31 | Start *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=start,proto3" json:"start,omitempty"` 32 | End *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=end,proto3" json:"end,omitempty"` 33 | } 34 | 35 | func (x *DiffRequest) ProtoReflect() protoreflect.Message { 36 | panic(`not implemented`) 37 | } 38 | 39 | func (x *DiffRequest) GetValue() *structpb.Value { 40 | if x != nil { 41 | return x.Value 42 | } 43 | return nil 44 | } 45 | 46 | func (x *DiffRequest) GetStart() *timestamppb.Timestamp { 47 | if x != nil { 48 | return x.Start 49 | } 50 | return nil 51 | } 52 | 53 | func (x *DiffRequest) GetEnd() *timestamppb.Timestamp { 54 | if x != nil { 55 | return x.End 56 | } 57 | return nil 58 | } 59 | 60 | type DiffReply struct { 61 | state protoimpl.MessageState 62 | sizeCache protoimpl.SizeCache 63 | unknownFields protoimpl.UnknownFields 64 | 65 | Duration *durationpb.Duration `protobuf:"bytes,1,opt,name=duration,proto3" json:"duration,omitempty"` 66 | } 67 | 68 | func (x *DiffReply) ProtoReflect() protoreflect.Message { 69 | panic(`not implemented`) 70 | } 71 | 72 | func (x *DiffReply) GetDuration() *durationpb.Duration { 73 | if x != nil { 74 | return x.Duration 75 | } 76 | return nil 77 | } 78 | 79 | // The greeting service definition. 80 | // go:plugin type=plugin 81 | type WellKnown interface { 82 | Diff(context.Context, *DiffRequest) (*DiffReply, error) 83 | } 84 | -------------------------------------------------------------------------------- /tests/host-functions/host_functions_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "os" 7 | "testing" 8 | 9 | "github.com/knqyf263/go-plugin/tests/host-functions/proto" 10 | "github.com/stretchr/testify/assert" 11 | "github.com/stretchr/testify/require" 12 | "github.com/tetratelabs/wazero" 13 | ) 14 | 15 | func TestHostFunctions(t *testing.T) { 16 | ctx := context.Background() 17 | mc := wazero.NewModuleConfig().WithStdout(os.Stdout) 18 | p, err := proto.NewGreeterPlugin(ctx, proto.WazeroRuntime(func(ctx context.Context) (wazero.Runtime, error) { 19 | return proto.DefaultWazeroRuntime()(ctx) 20 | }), proto.WazeroModuleConfig(mc)) 21 | require.NoError(t, err) 22 | 23 | // Pass my host functions that are embedded into the plugin. 24 | plugin, err := p.Load(ctx, "plugin/plugin.wasm", myHostFunctions{}) 25 | require.NoError(t, err) 26 | defer plugin.Close(ctx) 27 | 28 | reply, err := plugin.Greet(ctx, &proto.GreetRequest{ 29 | Name: "Sato", 30 | }) 31 | require.NoError(t, err) 32 | 33 | want := "Hello, Sato. This is Yamada (age 20)." 34 | assert.Equal(t, want, reply.GetMessage()) 35 | } 36 | 37 | func TestEmptyRequest(t *testing.T) { 38 | ctx := context.Background() 39 | p, err := proto.NewGreeterPlugin(ctx) 40 | require.NoError(t, err) 41 | 42 | plugin, err := p.Load(ctx, "plugin-empty/plugin.wasm", myEmptyHostFunctions{}) 43 | require.NoError(t, err) 44 | defer plugin.Close(ctx) 45 | 46 | reply, err := plugin.Greet(ctx, nil) 47 | require.NoError(t, err) 48 | want := "Hello, empty request '' and empty '' host function request" 49 | assert.Equal(t, want, reply.GetMessage()) 50 | } 51 | 52 | // myHostFunctions implements proto.HostFunctions 53 | type myHostFunctions struct{} 54 | 55 | var _ proto.HostFunctions = (*myHostFunctions)(nil) 56 | 57 | // ParseJson is embedded into the plugin and can be called by the plugin. 58 | func (myHostFunctions) ParseJson(_ context.Context, request *proto.ParseJsonRequest) (*proto.ParseJsonResponse, error) { 59 | var person proto.Person 60 | if err := json.Unmarshal(request.GetContent(), &person); err != nil { 61 | return nil, err 62 | } 63 | 64 | return &proto.ParseJsonResponse{Response: &person}, nil 65 | } 66 | 67 | type myEmptyHostFunctions struct{} 68 | 69 | var _ proto.HostFunctions = (*myEmptyHostFunctions)(nil) 70 | 71 | // ParseJson is embedded into the plugin and can be called by the plugin. 72 | func (myEmptyHostFunctions) ParseJson(_ context.Context, _ *proto.ParseJsonRequest) (*proto.ParseJsonResponse, error) { 73 | return &proto.ParseJsonResponse{Response: &proto.Person{}}, nil 74 | } 75 | -------------------------------------------------------------------------------- /tests/well-known/proto/known_plugin.pb.go: -------------------------------------------------------------------------------- 1 | //go:build tinygo.wasm 2 | 3 | // Code generated by protoc-gen-go-plugin. DO NOT EDIT. 4 | // versions: 5 | // protoc-gen-go-plugin v0.1.0 6 | // protoc v3.21.12 7 | // source: tests/well-known/proto/known.proto 8 | 9 | package proto 10 | 11 | import ( 12 | context "context" 13 | emptypb "github.com/knqyf263/go-plugin/types/known/emptypb" 14 | wasm "github.com/knqyf263/go-plugin/wasm" 15 | ) 16 | 17 | const KnownTypesTestPluginAPIVersion = 1 18 | 19 | //export known_types_test_api_version 20 | func _known_types_test_api_version() uint64 { 21 | return KnownTypesTestPluginAPIVersion 22 | } 23 | 24 | var knownTypesTest KnownTypesTest 25 | 26 | func RegisterKnownTypesTest(p KnownTypesTest) { 27 | knownTypesTest = p 28 | } 29 | 30 | //export known_types_test_test 31 | func _known_types_test_test(ptr, size uint32) uint64 { 32 | b := wasm.PtrToByte(ptr, size) 33 | req := new(Request) 34 | if err := req.UnmarshalVT(b); err != nil { 35 | return 0 36 | } 37 | response, err := knownTypesTest.Test(context.Background(), req) 38 | if err != nil { 39 | ptr, size = wasm.ByteToPtr([]byte(err.Error())) 40 | return (uint64(ptr) << uint64(32)) | uint64(size) | 41 | // Indicate that this is the error string by setting the 32-th bit, assuming that 42 | // no data exceeds 31-bit size (2 GiB). 43 | (1 << 31) 44 | } 45 | 46 | b, err = response.MarshalVT() 47 | if err != nil { 48 | return 0 49 | } 50 | ptr, size = wasm.ByteToPtr(b) 51 | return (uint64(ptr) << uint64(32)) | uint64(size) 52 | } 53 | 54 | const EmptyTestPluginAPIVersion = 1 55 | 56 | //export empty_test_api_version 57 | func _empty_test_api_version() uint64 { 58 | return EmptyTestPluginAPIVersion 59 | } 60 | 61 | var emptyTest EmptyTest 62 | 63 | func RegisterEmptyTest(p EmptyTest) { 64 | emptyTest = p 65 | } 66 | 67 | //export empty_test_do_nothing 68 | func _empty_test_do_nothing(ptr, size uint32) uint64 { 69 | b := wasm.PtrToByte(ptr, size) 70 | req := new(emptypb.Empty) 71 | if err := req.UnmarshalVT(b); err != nil { 72 | return 0 73 | } 74 | response, err := emptyTest.DoNothing(context.Background(), req) 75 | if err != nil { 76 | ptr, size = wasm.ByteToPtr([]byte(err.Error())) 77 | return (uint64(ptr) << uint64(32)) | uint64(size) | 78 | // Indicate that this is the error string by setting the 32-th bit, assuming that 79 | // no data exceeds 31-bit size (2 GiB). 80 | (1 << 31) 81 | } 82 | 83 | b, err = response.MarshalVT() 84 | if err != nil { 85 | return 0 86 | } 87 | ptr, size = wasm.ByteToPtr(b) 88 | return (uint64(ptr) << uint64(32)) | uint64(size) 89 | } 90 | -------------------------------------------------------------------------------- /examples/host-function-library/library/json-parser/export/library.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go-plugin. DO NOT EDIT. 2 | // versions: 3 | // protoc-gen-go-plugin v0.1.0 4 | // protoc v3.21.12 5 | // source: examples/host-function-library/library/json-parser/export/library.proto 6 | 7 | package export 8 | 9 | import ( 10 | context "context" 11 | protoreflect "google.golang.org/protobuf/reflect/protoreflect" 12 | protoimpl "google.golang.org/protobuf/runtime/protoimpl" 13 | ) 14 | 15 | const ( 16 | // Verify that this generated code is sufficiently up-to-date. 17 | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) 18 | // Verify that runtime/protoimpl is sufficiently up-to-date. 19 | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) 20 | ) 21 | 22 | type ParseJsonRequest struct { 23 | state protoimpl.MessageState 24 | sizeCache protoimpl.SizeCache 25 | unknownFields protoimpl.UnknownFields 26 | 27 | Content []byte `protobuf:"bytes,1,opt,name=content,proto3" json:"content,omitempty"` 28 | } 29 | 30 | func (x *ParseJsonRequest) ProtoReflect() protoreflect.Message { 31 | panic(`not implemented`) 32 | } 33 | 34 | func (x *ParseJsonRequest) GetContent() []byte { 35 | if x != nil { 36 | return x.Content 37 | } 38 | return nil 39 | } 40 | 41 | type ParseJsonResponse struct { 42 | state protoimpl.MessageState 43 | sizeCache protoimpl.SizeCache 44 | unknownFields protoimpl.UnknownFields 45 | 46 | Response *Person `protobuf:"bytes,1,opt,name=response,proto3" json:"response,omitempty"` 47 | } 48 | 49 | func (x *ParseJsonResponse) ProtoReflect() protoreflect.Message { 50 | panic(`not implemented`) 51 | } 52 | 53 | func (x *ParseJsonResponse) GetResponse() *Person { 54 | if x != nil { 55 | return x.Response 56 | } 57 | return nil 58 | } 59 | 60 | type Person struct { 61 | state protoimpl.MessageState 62 | sizeCache protoimpl.SizeCache 63 | unknownFields protoimpl.UnknownFields 64 | 65 | Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` 66 | Age int64 `protobuf:"varint,2,opt,name=age,proto3" json:"age,omitempty"` 67 | } 68 | 69 | func (x *Person) ProtoReflect() protoreflect.Message { 70 | panic(`not implemented`) 71 | } 72 | 73 | func (x *Person) GetName() string { 74 | if x != nil { 75 | return x.Name 76 | } 77 | return "" 78 | } 79 | 80 | func (x *Person) GetAge() int64 { 81 | if x != nil { 82 | return x.Age 83 | } 84 | return 0 85 | } 86 | 87 | // Distributing host functions without plugin code 88 | // go:plugin type=host module=json-parser 89 | type ParserLibrary interface { 90 | ParseJson(context.Context, *ParseJsonRequest) (*ParseJsonResponse, error) 91 | } 92 | -------------------------------------------------------------------------------- /gen/reflect.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Go Authors. All rights reserved. 2 | // Copyright 2022 Teppei Fukuda. All rights reserved. 3 | 4 | package gen 5 | 6 | import ( 7 | "fmt" 8 | "strings" 9 | "unicode/utf8" 10 | 11 | "google.golang.org/protobuf/compiler/protogen" 12 | "google.golang.org/protobuf/proto" 13 | "google.golang.org/protobuf/types/descriptorpb" 14 | ) 15 | 16 | func genFileDescriptor(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo) { 17 | descProto := proto.Clone(f.Proto).(*descriptorpb.FileDescriptorProto) 18 | descProto.SourceCodeInfo = nil // drop source code information 19 | 20 | b, err := proto.MarshalOptions{AllowPartial: true, Deterministic: true}.Marshal(descProto) 21 | if err != nil { 22 | gen.Error(err) 23 | return 24 | } 25 | 26 | g.P("var ", rawDescVarName(f), " = []byte{") 27 | for len(b) > 0 { 28 | n := 16 29 | if n > len(b) { 30 | n = len(b) 31 | } 32 | 33 | s := "" 34 | for _, c := range b[:n] { 35 | s += fmt.Sprintf("0x%02x,", c) 36 | } 37 | g.P(s) 38 | 39 | b = b[n:] 40 | } 41 | g.P("}") 42 | g.P() 43 | 44 | if f.needRawDesc { 45 | onceVar := rawDescVarName(f) + "Once" 46 | dataVar := rawDescVarName(f) + "Data" 47 | g.P("var (") 48 | g.P(onceVar, " ", syncPackage.Ident("Once")) 49 | g.P(dataVar, " = ", rawDescVarName(f)) 50 | g.P(")") 51 | g.P() 52 | 53 | g.P("func ", rawDescVarName(f), "GZIP() []byte {") 54 | g.P(onceVar, ".Do(func() {") 55 | g.P(dataVar, " = ", protoimplPackage.Ident("X"), ".CompressGZIP(", dataVar, ")") 56 | g.P("})") 57 | g.P("return ", dataVar) 58 | g.P("}") 59 | g.P() 60 | } 61 | } 62 | 63 | func genMessageReflectMethods(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo) { 64 | // ProtoReflect method. 65 | // A dummy method is defined so that it implements proto.Message, 66 | // but it is not supposed to be called. 67 | g.P("func (x *", m.GoIdent, ") ProtoReflect() ", protoreflectPackage.Ident("Message"), " {") 68 | g.P("panic(`not implemented`)") 69 | g.P("}") 70 | g.P() 71 | } 72 | 73 | func fileVarName(f *protogen.File, suffix string) string { 74 | prefix := f.GoDescriptorIdent.GoName 75 | _, n := utf8.DecodeRuneInString(prefix) 76 | prefix = strings.ToLower(prefix[:n]) + prefix[n:] 77 | return prefix + "_" + suffix 78 | } 79 | func rawDescVarName(f *fileInfo) string { 80 | return fileVarName(f.File, "rawDesc") 81 | } 82 | func goTypesVarName(f *fileInfo) string { 83 | return fileVarName(f.File, "goTypes") 84 | } 85 | func depIdxsVarName(f *fileInfo) string { 86 | return fileVarName(f.File, "depIdxs") 87 | } 88 | func enumTypesVarName(f *fileInfo) string { 89 | return fileVarName(f.File, "enumTypes") 90 | } 91 | func messageTypesVarName(f *fileInfo) string { 92 | return fileVarName(f.File, "msgTypes") 93 | } 94 | func extensionTypesVarName(f *fileInfo) string { 95 | return fileVarName(f.File, "extTypes") 96 | } 97 | func initFuncName(f *protogen.File) string { 98 | return fileVarName(f, "init") 99 | } 100 | -------------------------------------------------------------------------------- /examples/host-functions/greeting/greet_plugin.pb.go: -------------------------------------------------------------------------------- 1 | //go:build tinygo.wasm 2 | 3 | // Code generated by protoc-gen-go-plugin. DO NOT EDIT. 4 | // versions: 5 | // protoc-gen-go-plugin v0.1.0 6 | // protoc v3.21.12 7 | // source: examples/host-functions/greeting/greet.proto 8 | 9 | package greeting 10 | 11 | import ( 12 | context "context" 13 | emptypb "github.com/knqyf263/go-plugin/types/known/emptypb" 14 | wasm "github.com/knqyf263/go-plugin/wasm" 15 | _ "unsafe" 16 | ) 17 | 18 | const GreeterPluginAPIVersion = 1 19 | 20 | //export greeter_api_version 21 | func _greeter_api_version() uint64 { 22 | return GreeterPluginAPIVersion 23 | } 24 | 25 | var greeter Greeter 26 | 27 | func RegisterGreeter(p Greeter) { 28 | greeter = p 29 | } 30 | 31 | //export greeter_greet 32 | func _greeter_greet(ptr, size uint32) uint64 { 33 | b := wasm.PtrToByte(ptr, size) 34 | req := new(GreetRequest) 35 | if err := req.UnmarshalVT(b); err != nil { 36 | return 0 37 | } 38 | response, err := greeter.Greet(context.Background(), req) 39 | if err != nil { 40 | ptr, size = wasm.ByteToPtr([]byte(err.Error())) 41 | return (uint64(ptr) << uint64(32)) | uint64(size) | 42 | // Indicate that this is the error string by setting the 32-th bit, assuming that 43 | // no data exceeds 31-bit size (2 GiB). 44 | (1 << 31) 45 | } 46 | 47 | b, err = response.MarshalVT() 48 | if err != nil { 49 | return 0 50 | } 51 | ptr, size = wasm.ByteToPtr(b) 52 | return (uint64(ptr) << uint64(32)) | uint64(size) 53 | } 54 | 55 | type hostFunctions struct{} 56 | 57 | func NewHostFunctions() HostFunctions { 58 | return hostFunctions{} 59 | } 60 | 61 | //go:wasm-module env 62 | //export http_get 63 | //go:linkname _http_get 64 | func _http_get(ptr uint32, size uint32) uint64 65 | 66 | func (h hostFunctions) HttpGet(ctx context.Context, request *HttpGetRequest) (*HttpGetResponse, error) { 67 | buf, err := request.MarshalVT() 68 | if err != nil { 69 | return nil, err 70 | } 71 | ptr, size := wasm.ByteToPtr(buf) 72 | ptrSize := _http_get(ptr, size) 73 | wasm.FreePtr(ptr) 74 | 75 | ptr = uint32(ptrSize >> 32) 76 | size = uint32(ptrSize) 77 | buf = wasm.PtrToByte(ptr, size) 78 | 79 | response := new(HttpGetResponse) 80 | if err = response.UnmarshalVT(buf); err != nil { 81 | return nil, err 82 | } 83 | return response, nil 84 | } 85 | 86 | //go:wasm-module env 87 | //export log 88 | //go:linkname _log 89 | func _log(ptr uint32, size uint32) uint64 90 | 91 | func (h hostFunctions) Log(ctx context.Context, request *LogRequest) (*emptypb.Empty, error) { 92 | buf, err := request.MarshalVT() 93 | if err != nil { 94 | return nil, err 95 | } 96 | ptr, size := wasm.ByteToPtr(buf) 97 | ptrSize := _log(ptr, size) 98 | wasm.FreePtr(ptr) 99 | 100 | ptr = uint32(ptrSize >> 32) 101 | size = uint32(ptrSize) 102 | buf = wasm.PtrToByte(ptr, size) 103 | 104 | response := new(emptypb.Empty) 105 | if err = response.UnmarshalVT(buf); err != nil { 106 | return nil, err 107 | } 108 | return response, nil 109 | } 110 | -------------------------------------------------------------------------------- /tests/fields/proto/fields_plugin.pb.go: -------------------------------------------------------------------------------- 1 | //go:build tinygo.wasm 2 | 3 | // Code generated by protoc-gen-go-plugin. DO NOT EDIT. 4 | // versions: 5 | // protoc-gen-go-plugin v0.1.0 6 | // protoc v3.21.12 7 | // source: tests/fields/proto/fields.proto 8 | 9 | package proto 10 | 11 | import ( 12 | context "context" 13 | emptypb "github.com/knqyf263/go-plugin/types/known/emptypb" 14 | wasm "github.com/knqyf263/go-plugin/wasm" 15 | ) 16 | 17 | const FieldTestPluginAPIVersion = 1 18 | 19 | //export field_test_api_version 20 | func _field_test_api_version() uint64 { 21 | return FieldTestPluginAPIVersion 22 | } 23 | 24 | var fieldTest FieldTest 25 | 26 | func RegisterFieldTest(p FieldTest) { 27 | fieldTest = p 28 | } 29 | 30 | //export field_test_test 31 | func _field_test_test(ptr, size uint32) uint64 { 32 | b := wasm.PtrToByte(ptr, size) 33 | req := new(Request) 34 | if err := req.UnmarshalVT(b); err != nil { 35 | return 0 36 | } 37 | response, err := fieldTest.Test(context.Background(), req) 38 | if err != nil { 39 | ptr, size = wasm.ByteToPtr([]byte(err.Error())) 40 | return (uint64(ptr) << uint64(32)) | uint64(size) | 41 | // Indicate that this is the error string by setting the 32-th bit, assuming that 42 | // no data exceeds 31-bit size (2 GiB). 43 | (1 << 31) 44 | } 45 | 46 | b, err = response.MarshalVT() 47 | if err != nil { 48 | return 0 49 | } 50 | ptr, size = wasm.ByteToPtr(b) 51 | return (uint64(ptr) << uint64(32)) | uint64(size) 52 | } 53 | 54 | //export field_test_test_empty_input 55 | func _field_test_test_empty_input(ptr, size uint32) uint64 { 56 | b := wasm.PtrToByte(ptr, size) 57 | req := new(emptypb.Empty) 58 | if err := req.UnmarshalVT(b); err != nil { 59 | return 0 60 | } 61 | response, err := fieldTest.TestEmptyInput(context.Background(), req) 62 | if err != nil { 63 | ptr, size = wasm.ByteToPtr([]byte(err.Error())) 64 | return (uint64(ptr) << uint64(32)) | uint64(size) | 65 | // Indicate that this is the error string by setting the 32-th bit, assuming that 66 | // no data exceeds 31-bit size (2 GiB). 67 | (1 << 31) 68 | } 69 | 70 | b, err = response.MarshalVT() 71 | if err != nil { 72 | return 0 73 | } 74 | ptr, size = wasm.ByteToPtr(b) 75 | return (uint64(ptr) << uint64(32)) | uint64(size) 76 | } 77 | 78 | //export field_test_test_error 79 | func _field_test_test_error(ptr, size uint32) uint64 { 80 | b := wasm.PtrToByte(ptr, size) 81 | req := new(ErrorRequest) 82 | if err := req.UnmarshalVT(b); err != nil { 83 | return 0 84 | } 85 | response, err := fieldTest.TestError(context.Background(), req) 86 | if err != nil { 87 | ptr, size = wasm.ByteToPtr([]byte(err.Error())) 88 | return (uint64(ptr) << uint64(32)) | uint64(size) | 89 | // Indicate that this is the error string by setting the 32-th bit, assuming that 90 | // no data exceeds 31-bit size (2 GiB). 91 | (1 << 31) 92 | } 93 | 94 | b, err = response.MarshalVT() 95 | if err != nil { 96 | return 0 97 | } 98 | ptr, size = wasm.ByteToPtr(b) 99 | return (uint64(ptr) << uint64(32)) | uint64(size) 100 | } 101 | -------------------------------------------------------------------------------- /tests/well-known/well_known_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | "time" 7 | 8 | "github.com/stretchr/testify/assert" 9 | "github.com/stretchr/testify/require" 10 | 11 | "github.com/knqyf263/go-plugin/tests/well-known/proto" 12 | "github.com/knqyf263/go-plugin/types/known/durationpb" 13 | "github.com/knqyf263/go-plugin/types/known/emptypb" 14 | "github.com/knqyf263/go-plugin/types/known/structpb" 15 | "github.com/knqyf263/go-plugin/types/known/timestamppb" 16 | "github.com/knqyf263/go-plugin/types/known/wrapperspb" 17 | ) 18 | 19 | func TestWellKnownTypes(t *testing.T) { 20 | ctx := context.Background() 21 | p, err := proto.NewKnownTypesTestPlugin(ctx) 22 | require.NoError(t, err) 23 | 24 | plugin, err := p.Load(ctx, "plugin/plugin.wasm") 25 | require.NoError(t, err) 26 | defer plugin.Close(ctx) 27 | 28 | b := time.Date(2022, 1, 2, 3, 4, 5, 6, time.UTC) 29 | c, err := structpb.NewValue(map[string]interface{}{ 30 | "CA": "AAA", 31 | "CB": true, 32 | "CC": 100, 33 | "CD": []interface{}{ 34 | map[string]interface{}{ 35 | "CE": "CF", 36 | }, 37 | map[string]interface{}{ 38 | "CG": "CH", 39 | "CI": "CJ", 40 | }, 41 | }, 42 | "CK": nil, 43 | }) 44 | require.NoError(t, err) 45 | 46 | got, err := plugin.Test(ctx, &proto.Request{ 47 | // duration 48 | A: durationpb.New(1 * time.Hour), 49 | 50 | // timestamp 51 | B: timestamppb.New(b), 52 | 53 | // struct 54 | C: c, 55 | 56 | // wrappers 57 | D: wrapperspb.Bool(true), 58 | E: wrapperspb.Bytes([]byte(`Bytes`)), 59 | F: wrapperspb.Double(1.2), 60 | G: wrapperspb.Float(3.4), 61 | H: wrapperspb.Int32(1), 62 | I: wrapperspb.Int64(2), 63 | J: wrapperspb.String("String"), 64 | K: wrapperspb.UInt32(3), 65 | L: wrapperspb.UInt64(4), 66 | }) 67 | 68 | c, err = structpb.NewValue(map[string]interface{}{ 69 | "CA": "AAABBB", 70 | "CB": false, 71 | "CC": 200, 72 | "CD": []interface{}{ 73 | map[string]interface{}{ 74 | "CE": "CF", 75 | }, 76 | map[string]interface{}{ 77 | "CG": "CH", 78 | "CI": "CJ", 79 | }, 80 | "FOO", 81 | }, 82 | "CK": nil, 83 | }) 84 | require.NoError(t, err) 85 | 86 | want := &proto.Response{ 87 | A: durationpb.New(2 * time.Minute), 88 | B: timestamppb.New(b.Add(1 * time.Hour)), 89 | C: c, 90 | D: wrapperspb.Bool(false), 91 | E: wrapperspb.Bytes([]byte(`BytesValue`)), 92 | F: wrapperspb.Double(2.4), 93 | G: wrapperspb.Float(6.8), 94 | H: wrapperspb.Int32(2), 95 | I: wrapperspb.Int64(4), 96 | J: wrapperspb.String("StringValue"), 97 | K: wrapperspb.UInt32(6), 98 | L: wrapperspb.UInt64(8), 99 | } 100 | assert.Equal(t, want, got) 101 | } 102 | 103 | func TestEmpty(t *testing.T) { 104 | ctx := context.Background() 105 | p, err := proto.NewEmptyTestPlugin(ctx) 106 | require.NoError(t, err) 107 | 108 | plugin, err := p.Load(ctx, "plugin/plugin.wasm") 109 | require.NoError(t, err) 110 | defer plugin.Close(ctx) 111 | 112 | got, err := plugin.DoNothing(ctx, &emptypb.Empty{}) 113 | require.NoError(t, err) 114 | assert.Equal(t, &emptypb.Empty{}, got) 115 | } 116 | -------------------------------------------------------------------------------- /examples/host-functions/README.md: -------------------------------------------------------------------------------- 1 | # Host Functions Example 2 | This example shows how to embed functions defined in a host into plugins to expand the plugin functionalities. 3 | 4 | ## Generate Go code 5 | A proto file is under `./greeting`. 6 | 7 | ```protobuf 8 | // The host functions embedded into the plugin 9 | // go:plugin type=host 10 | service HostFunctions { 11 | // Sends a HTTP GET request 12 | rpc HttpGet(HttpGetRequest) returns (HttpGetResponse) {} 13 | // Shows a log message 14 | rpc Log(LogRequest) returns (google.protobuf.Empty) {} 15 | } 16 | ``` 17 | 18 | NOTE: You must specify `type=host` in the comment. It represents the service is for host functions. 19 | 20 | Then, generate source code. 21 | 22 | ```shell 23 | $ protoc --go-plugin_out=. --go-plugin_opt=paths=source_relative greeting/greet.proto 24 | ``` 25 | 26 | ## Implement host functions 27 | The following interface is generated. 28 | 29 | ```go 30 | type HostFunctions interface { 31 | HttpGet(context.Context, HttpGetRequest) (HttpGetResponse, error) 32 | Log(context.Context, LogRequest) (emptypb.Empty, error) 33 | } 34 | ``` 35 | 36 | Implement that interface. 37 | 38 | ```go 39 | // myHostFunctions implements greeting.HostFunctions 40 | type myHostFunctions struct{} 41 | 42 | // HttpGet is embedded into the plugin and can be called by the plugin. 43 | func (myHostFunctions) HttpGet(ctx context.Context, request greeting.HttpGetRequest) (greeting.HttpGetResponse, error) { 44 | resp, err := http.Get(request.Url) 45 | if err != nil { 46 | return greeting.HttpGetResponse{}, err 47 | } 48 | defer resp.Body.Close() 49 | 50 | buf, err := io.ReadAll(resp.Body) 51 | if err != nil { 52 | return greeting.HttpGetResponse{}, err 53 | } 54 | 55 | return greeting.HttpGetResponse{Response: buf}, nil 56 | } 57 | 58 | // Log is embedded into the plugin and can be called by the plugin. 59 | func (myHostFunctions) Log(ctx context.Context, request greeting.LogRequest) (emptypb.Empty, error) { 60 | // Use the host logger 61 | log.Println(request.GetMessage()) 62 | return emptypb.Empty{}, nil 63 | } 64 | ``` 65 | 66 | Pass it to a plugin in `Load()`. 67 | 68 | ```go 69 | // Pass my host functions that are embedded into the plugin. 70 | greetingPlugin, err := p.Load(ctx, "plugin/plugin.wasm", myHostFunctions{}) 71 | ``` 72 | 73 | ## Call host functions in a plugin 74 | The embedded host functions can be called in plugins. 75 | 76 | ```go 77 | hostFunctions := greeting.NewHostFunctions() 78 | 79 | // Logging via the host function 80 | hostFunctions.Log(ctx, greeting.LogRequest{ 81 | Message: "Sending a HTTP request...", 82 | }) 83 | 84 | // HTTP GET via the host function 85 | resp, err := hostFunctions.HttpGet(ctx, greeting.HttpGetRequest{Url: "http://ifconfig.me"}) 86 | ``` 87 | 88 | ## Compile a plugin 89 | Use TinyGo to compile the plugin to Wasm. 90 | 91 | ```shell 92 | $ tinygo build -o plugin/plugin.wasm -scheduler=none -target=wasi --no-debug plugin/plugin.go 93 | ``` 94 | 95 | ## Run 96 | `main.go` loads the above plugin. 97 | 98 | ```shell 99 | $ go run main.go 100 | 2022/08/28 10:13:57 Sending a HTTP request... 101 | Hello, go-plugin from x.x.x.x 102 | ``` -------------------------------------------------------------------------------- /types/known/durationpb/duration.go: -------------------------------------------------------------------------------- 1 | // Protocol Buffers - Google's data interchange format 2 | // Copyright 2008 Google Inc. All rights reserved. 3 | // Copyright 2022 Teppei Fukuda. All rights reserved. 4 | // https://developers.google.com/protocol-buffers/ 5 | 6 | package durationpb 7 | 8 | import ( 9 | "math" 10 | "time" 11 | 12 | "google.golang.org/protobuf/runtime/protoimpl" 13 | ) 14 | 15 | // New constructs a new Duration from the provided time.Duration. 16 | func New(d time.Duration) *Duration { 17 | nanos := d.Nanoseconds() 18 | secs := nanos / 1e9 19 | nanos -= secs * 1e9 20 | return &Duration{Seconds: int64(secs), Nanos: int32(nanos)} 21 | } 22 | 23 | // AsDuration converts x to a time.Duration, 24 | // returning the closest duration value in the event of overflow. 25 | func (x *Duration) AsDuration() time.Duration { 26 | secs := x.GetSeconds() 27 | nanos := x.GetNanos() 28 | d := time.Duration(secs) * time.Second 29 | // TODO: "__multi3" is not exported in module 'env' 30 | //overflow := d/time.Second != time.Duration(secs) 31 | d += time.Duration(nanos) * time.Nanosecond 32 | overflow := secs < 0 && nanos < 0 && d > 0 33 | overflow = overflow || (secs > 0 && nanos > 0 && d < 0) 34 | if overflow { 35 | switch { 36 | case secs < 0: 37 | return time.Duration(math.MinInt64) 38 | case secs > 0: 39 | return time.Duration(math.MaxInt64) 40 | } 41 | } 42 | return d 43 | } 44 | 45 | // IsValid reports whether the duration is valid. 46 | // It is equivalent to CheckValid == nil. 47 | func (x *Duration) IsValid() bool { 48 | return x.check() == 0 49 | } 50 | 51 | // CheckValid returns an error if the duration is invalid. 52 | // In particular, it checks whether the value is within the range of 53 | // -10000 years to +10000 years inclusive. 54 | // An error is reported for a nil Duration. 55 | func (x *Duration) CheckValid() error { 56 | switch x.check() { 57 | case invalidNil: 58 | return protoimpl.X.NewError("invalid nil Duration") 59 | case invalidUnderflow: 60 | return protoimpl.X.NewError("duration (%v) exceeds -10000 years", x) 61 | case invalidOverflow: 62 | return protoimpl.X.NewError("duration (%v) exceeds +10000 years", x) 63 | case invalidNanosRange: 64 | return protoimpl.X.NewError("duration (%v) has out-of-range nanos", x) 65 | case invalidNanosSign: 66 | return protoimpl.X.NewError("duration (%v) has seconds and nanos with different signs", x) 67 | default: 68 | return nil 69 | } 70 | } 71 | 72 | const ( 73 | _ = iota 74 | invalidNil 75 | invalidUnderflow 76 | invalidOverflow 77 | invalidNanosRange 78 | invalidNanosSign 79 | ) 80 | 81 | func (x *Duration) check() uint { 82 | const absDuration = 315576000000 // 10000yr * 365.25day/yr * 24hr/day * 60min/hr * 60sec/min 83 | secs := x.GetSeconds() 84 | nanos := x.GetNanos() 85 | switch { 86 | case x == nil: 87 | return invalidNil 88 | case secs < -absDuration: 89 | return invalidUnderflow 90 | case secs > +absDuration: 91 | return invalidOverflow 92 | case nanos <= -1e9 || nanos >= +1e9: 93 | return invalidNanosRange 94 | case (secs > 0 && nanos < 0) || (secs < 0 && nanos > 0): 95 | return invalidNanosSign 96 | default: 97 | return 0 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /examples/host-function-library/proto/greet.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go-plugin. DO NOT EDIT. 2 | // versions: 3 | // protoc-gen-go-plugin v0.1.0 4 | // protoc v3.21.12 5 | // source: examples/host-function-library/proto/greet.proto 6 | 7 | package proto 8 | 9 | import ( 10 | context "context" 11 | protoreflect "google.golang.org/protobuf/reflect/protoreflect" 12 | protoimpl "google.golang.org/protobuf/runtime/protoimpl" 13 | ) 14 | 15 | const ( 16 | // Verify that this generated code is sufficiently up-to-date. 17 | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) 18 | // Verify that runtime/protoimpl is sufficiently up-to-date. 19 | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) 20 | ) 21 | 22 | // The request message containing the user's name. 23 | type GreetRequest struct { 24 | state protoimpl.MessageState 25 | sizeCache protoimpl.SizeCache 26 | unknownFields protoimpl.UnknownFields 27 | 28 | Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` 29 | } 30 | 31 | func (x *GreetRequest) ProtoReflect() protoreflect.Message { 32 | panic(`not implemented`) 33 | } 34 | 35 | func (x *GreetRequest) GetName() string { 36 | if x != nil { 37 | return x.Name 38 | } 39 | return "" 40 | } 41 | 42 | // The response message containing the greetings 43 | type GreetReply struct { 44 | state protoimpl.MessageState 45 | sizeCache protoimpl.SizeCache 46 | unknownFields protoimpl.UnknownFields 47 | 48 | Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` 49 | } 50 | 51 | func (x *GreetReply) ProtoReflect() protoreflect.Message { 52 | panic(`not implemented`) 53 | } 54 | 55 | func (x *GreetReply) GetMessage() string { 56 | if x != nil { 57 | return x.Message 58 | } 59 | return "" 60 | } 61 | 62 | type SanRequest struct { 63 | state protoimpl.MessageState 64 | sizeCache protoimpl.SizeCache 65 | unknownFields protoimpl.UnknownFields 66 | 67 | Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` 68 | } 69 | 70 | func (x *SanRequest) ProtoReflect() protoreflect.Message { 71 | panic(`not implemented`) 72 | } 73 | 74 | func (x *SanRequest) GetMessage() string { 75 | if x != nil { 76 | return x.Message 77 | } 78 | return "" 79 | } 80 | 81 | type SanResponse struct { 82 | state protoimpl.MessageState 83 | sizeCache protoimpl.SizeCache 84 | unknownFields protoimpl.UnknownFields 85 | 86 | Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` 87 | } 88 | 89 | func (x *SanResponse) ProtoReflect() protoreflect.Message { 90 | panic(`not implemented`) 91 | } 92 | 93 | func (x *SanResponse) GetMessage() string { 94 | if x != nil { 95 | return x.Message 96 | } 97 | return "" 98 | } 99 | 100 | // The greeting service definition. 101 | // go:plugin type=plugin version=1 102 | type Greeter interface { 103 | // Sends a greeting 104 | Greet(context.Context, *GreetRequest) (*GreetReply, error) 105 | } 106 | 107 | // The host functions embedded into the plugin 108 | // go:plugin type=host 109 | type HostFunctions interface { 110 | San(context.Context, *SanRequest) (*SanResponse, error) 111 | } 112 | -------------------------------------------------------------------------------- /types/known/durationpb/duration.proto: -------------------------------------------------------------------------------- 1 | // Protocol Buffers - Google's data interchange format 2 | // Copyright 2008 Google Inc. All rights reserved. 3 | // Copyright 2022 Teppei Fukuda. All rights reserved. 4 | // https://developers.google.com/protocol-buffers/ 5 | 6 | syntax = "proto3"; 7 | 8 | package google.protobuf; 9 | 10 | option go_package = "github.com/knqyf263/go-plugin/types/known/durationpb"; 11 | 12 | // A Duration represents a signed, fixed-length span of time represented 13 | // as a count of seconds and fractions of seconds at nanosecond 14 | // resolution. It is independent of any calendar and concepts like "day" 15 | // or "month". It is related to Timestamp in that the difference between 16 | // two Timestamp values is a Duration and it can be added or subtracted 17 | // from a Timestamp. Range is approximately +-10,000 years. 18 | // 19 | // # Examples 20 | // 21 | // Example 1: Compute Duration from two Timestamps in pseudo code. 22 | // 23 | // Timestamp start = ...; 24 | // Timestamp end = ...; 25 | // Duration duration = ...; 26 | // 27 | // duration.seconds = end.seconds - start.seconds; 28 | // duration.nanos = end.nanos - start.nanos; 29 | // 30 | // if (duration.seconds < 0 && duration.nanos > 0) { 31 | // duration.seconds += 1; 32 | // duration.nanos -= 1000000000; 33 | // } else if (duration.seconds > 0 && duration.nanos < 0) { 34 | // duration.seconds -= 1; 35 | // duration.nanos += 1000000000; 36 | // } 37 | // 38 | // Example 2: Compute Timestamp from Timestamp + Duration in pseudo code. 39 | // 40 | // Timestamp start = ...; 41 | // Duration duration = ...; 42 | // Timestamp end = ...; 43 | // 44 | // end.seconds = start.seconds + duration.seconds; 45 | // end.nanos = start.nanos + duration.nanos; 46 | // 47 | // if (end.nanos < 0) { 48 | // end.seconds -= 1; 49 | // end.nanos += 1000000000; 50 | // } else if (end.nanos >= 1000000000) { 51 | // end.seconds += 1; 52 | // end.nanos -= 1000000000; 53 | // } 54 | // 55 | // Example 3: Compute Duration from datetime.timedelta in Python. 56 | // 57 | // td = datetime.timedelta(days=3, minutes=10) 58 | // duration = Duration() 59 | // duration.FromTimedelta(td) 60 | // 61 | // # JSON Mapping 62 | // 63 | // In JSON format, the Duration type is encoded as a string rather than an 64 | // object, where the string ends in the suffix "s" (indicating seconds) and 65 | // is preceded by the number of seconds, with nanoseconds expressed as 66 | // fractional seconds. For example, 3 seconds with 0 nanoseconds should be 67 | // encoded in JSON format as "3s", while 3 seconds and 1 nanosecond should 68 | // be expressed in JSON format as "3.000000001s", and 3 seconds and 1 69 | // microsecond should be expressed in JSON format as "3.000001s". 70 | // 71 | // 72 | message Duration { 73 | // Signed seconds of the span of time. Must be from -315,576,000,000 74 | // to +315,576,000,000 inclusive. Note: these bounds are computed from: 75 | // 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years 76 | int64 seconds = 1; 77 | 78 | // Signed fractions of a second at nanosecond resolution of the span 79 | // of time. Durations less than one second are represented with a 0 80 | // `seconds` field and a positive or negative `nanos` field. For durations 81 | // of one second or more, a non-zero value for the `nanos` field must be 82 | // of the same sign as the `seconds` field. Must be from -999,999,999 83 | // to +999,999,999 inclusive. 84 | int32 nanos = 2; 85 | } 86 | -------------------------------------------------------------------------------- /encoding/defval/default.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package defval marshals and unmarshals textual forms of default values. 6 | // 7 | // This package handles both the form historically used in Go struct field tags 8 | // and also the form used by google.protobuf.FieldDescriptorProto.default_value 9 | // since they differ in superficial ways. 10 | package defval 11 | 12 | import ( 13 | "fmt" 14 | "math" 15 | "strconv" 16 | 17 | "google.golang.org/protobuf/reflect/protoreflect" 18 | ) 19 | 20 | // Format is the serialization format used to represent the default value. 21 | type Format int 22 | 23 | const ( 24 | _ Format = iota 25 | 26 | // GoTag uses the historical serialization format in Go struct field tags. 27 | GoTag 28 | ) 29 | 30 | // Marshal serializes v as the default string according to the given kind k. 31 | // When specifying the Descriptor format for an enum kind, the associated 32 | // enum value descriptor must be provided. 33 | func Marshal(v protoreflect.Value, ev protoreflect.EnumValueDescriptor, k protoreflect.Kind, f Format) (string, error) { 34 | switch k { 35 | case protoreflect.BoolKind: 36 | if f == GoTag { 37 | if v.Bool() { 38 | return "1", nil 39 | } else { 40 | return "0", nil 41 | } 42 | } else { 43 | if v.Bool() { 44 | return "true", nil 45 | } else { 46 | return "false", nil 47 | } 48 | } 49 | case protoreflect.EnumKind: 50 | if f == GoTag { 51 | return strconv.FormatInt(int64(v.Enum()), 10), nil 52 | } else { 53 | return string(ev.Name()), nil 54 | } 55 | case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind, protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind: 56 | return strconv.FormatInt(v.Int(), 10), nil 57 | case protoreflect.Uint32Kind, protoreflect.Fixed32Kind, protoreflect.Uint64Kind, protoreflect.Fixed64Kind: 58 | return strconv.FormatUint(v.Uint(), 10), nil 59 | case protoreflect.FloatKind, protoreflect.DoubleKind: 60 | f := v.Float() 61 | switch { 62 | case math.IsInf(f, -1): 63 | return "-inf", nil 64 | case math.IsInf(f, +1): 65 | return "inf", nil 66 | case math.IsNaN(f): 67 | return "nan", nil 68 | default: 69 | if k == protoreflect.FloatKind { 70 | return strconv.FormatFloat(f, 'g', -1, 32), nil 71 | } else { 72 | return strconv.FormatFloat(f, 'g', -1, 64), nil 73 | } 74 | } 75 | case protoreflect.StringKind: 76 | // String values are serialized as is without any escaping. 77 | return v.String(), nil 78 | case protoreflect.BytesKind: 79 | if s, ok := marshalBytes(v.Bytes()); ok { 80 | return s, nil 81 | } 82 | } 83 | return "", fmt.Errorf("could not format value for %v: %v", k, v) 84 | } 85 | 86 | // marshalBytes serializes bytes by using C escaping. 87 | // To match the exact output of protoc, this is identical to the 88 | // CEscape function in strutil.cc of the protoc source code. 89 | func marshalBytes(b []byte) (string, bool) { 90 | var s []byte 91 | for _, c := range b { 92 | switch c { 93 | case '\n': 94 | s = append(s, `\n`...) 95 | case '\r': 96 | s = append(s, `\r`...) 97 | case '\t': 98 | s = append(s, `\t`...) 99 | case '"': 100 | s = append(s, `\"`...) 101 | case '\'': 102 | s = append(s, `\'`...) 103 | case '\\': 104 | s = append(s, `\\`...) 105 | default: 106 | if printableASCII := c >= 0x20 && c <= 0x7e; printableASCII { 107 | s = append(s, c) 108 | } else { 109 | s = append(s, fmt.Sprintf(`\%03o`, c)...) 110 | } 111 | } 112 | } 113 | return string(s), true 114 | } 115 | -------------------------------------------------------------------------------- /encoding/tag/tag.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package tag marshals and unmarshals the legacy struct tags as generated 6 | // by historical versions of protoc-gen-go. 7 | package tag 8 | 9 | import ( 10 | "reflect" 11 | "strconv" 12 | "strings" 13 | 14 | "google.golang.org/protobuf/reflect/protoreflect" 15 | 16 | "github.com/knqyf263/go-plugin/encoding/defval" 17 | ) 18 | 19 | var byteType = reflect.TypeOf(byte(0)) 20 | 21 | // Marshal encodes the protoreflect.FieldDescriptor as a tag. 22 | // 23 | // The enumName must be provided if the kind is an enum. 24 | // Historically, the formulation of the enum "name" was the proto package 25 | // dot-concatenated with the generated Go identifier for the enum type. 26 | // Depending on the context on how Marshal is called, there are different ways 27 | // through which that information is determined. As such it is the caller's 28 | // responsibility to provide a function to obtain that information. 29 | func Marshal(fd protoreflect.FieldDescriptor, enumName string) string { 30 | var tag []string 31 | switch fd.Kind() { 32 | case protoreflect.BoolKind, protoreflect.EnumKind, protoreflect.Int32Kind, protoreflect.Uint32Kind, protoreflect.Int64Kind, protoreflect.Uint64Kind: 33 | tag = append(tag, "varint") 34 | case protoreflect.Sint32Kind: 35 | tag = append(tag, "zigzag32") 36 | case protoreflect.Sint64Kind: 37 | tag = append(tag, "zigzag64") 38 | case protoreflect.Sfixed32Kind, protoreflect.Fixed32Kind, protoreflect.FloatKind: 39 | tag = append(tag, "fixed32") 40 | case protoreflect.Sfixed64Kind, protoreflect.Fixed64Kind, protoreflect.DoubleKind: 41 | tag = append(tag, "fixed64") 42 | case protoreflect.StringKind, protoreflect.BytesKind, protoreflect.MessageKind: 43 | tag = append(tag, "bytes") 44 | case protoreflect.GroupKind: 45 | tag = append(tag, "group") 46 | } 47 | tag = append(tag, strconv.Itoa(int(fd.Number()))) 48 | switch fd.Cardinality() { 49 | case protoreflect.Optional: 50 | tag = append(tag, "opt") 51 | case protoreflect.Required: 52 | tag = append(tag, "req") 53 | case protoreflect.Repeated: 54 | tag = append(tag, "rep") 55 | } 56 | if fd.IsPacked() { 57 | tag = append(tag, "packed") 58 | } 59 | name := string(fd.Name()) 60 | if fd.Kind() == protoreflect.GroupKind { 61 | // The name of the FieldDescriptor for a group field is 62 | // lowercased. To find the original capitalization, we 63 | // look in the field's MessageType. 64 | name = string(fd.Message().Name()) 65 | } 66 | tag = append(tag, "name="+name) 67 | if jsonName := fd.JSONName(); jsonName != "" && jsonName != name && !fd.IsExtension() { 68 | // NOTE: The jsonName != name condition is suspect, but it preserve 69 | // the exact same semantics from the previous generator. 70 | tag = append(tag, "json="+jsonName) 71 | } 72 | if fd.IsWeak() { 73 | tag = append(tag, "weak="+string(fd.Message().FullName())) 74 | } 75 | // The previous implementation does not tag extension fields as proto3, 76 | // even when the field is defined in a proto3 file. Match that behavior 77 | // for consistency. 78 | if fd.Syntax() == protoreflect.Proto3 && !fd.IsExtension() { 79 | tag = append(tag, "proto3") 80 | } 81 | if fd.Kind() == protoreflect.EnumKind && enumName != "" { 82 | tag = append(tag, "enum="+enumName) 83 | } 84 | if fd.ContainingOneof() != nil { 85 | tag = append(tag, "oneof") 86 | } 87 | // This must appear last in the tag, since commas in strings aren't escaped. 88 | if fd.HasDefault() { 89 | def, _ := defval.Marshal(fd.Default(), fd.DefaultEnumValue(), fd.Kind(), defval.GoTag) 90 | tag = append(tag, "def="+def) 91 | } 92 | return strings.Join(tag, ",") 93 | } 94 | -------------------------------------------------------------------------------- /examples/host-function-library/README.md: -------------------------------------------------------------------------------- 1 | # Register Host Function Library Example 2 | This example shows how to embed functions defined in a host into plugins to expand the plugin functionalities. 3 | Host functions can be defined either in the current plugin distribution in the proto file or imported from another module defined as a source for these host functions. 4 | This allows host functions to be distributed as a library or SDK instead of having to copy them every time you create plugins. 5 | 6 | ## Generate Go code for distributed host functions 7 | A proto file is under `library/json-parser/export`. 8 | 9 | ```protobuf 10 | // Distributing host functions without plugin code 11 | // go:plugin type=host module=json-parser 12 | service ParserLibrary { 13 | rpc ParseJson(ParseJsonRequest) returns (ParseJsonResponse) {} 14 | } 15 | ``` 16 | 17 | > **_NOTE:_** You must specify `type=host` in the comment `module=json-parser` and module name to be unique for the host function redistribution and registration. 18 | It represents the service is for host functions. 19 | 20 | Then, generate source code for `json-parser` module hosts functions. 21 | 22 | ```shell 23 | $ protoc --go-plugin_out=. --go-plugin_opt=paths=source_relative library/json-parser/export/library.proto 24 | ``` 25 | 26 | ## Implement `json-parser` module host functions 27 | The following interface is generated. 28 | 29 | ```go 30 | // Distributing host functions without plugin code 31 | // go:plugin type=host module=json-parser 32 | type ParserLibrary interface { 33 | ParseJson(context.Context, *ParseJsonRequest) (*ParseJsonResponse, error) 34 | } 35 | ``` 36 | 37 | Implement that interface in separate package which will be exported to the main plugin implementation. 38 | 39 | ```go 40 | // ParserLibraryImpl implements export.ParserLibrary functions 41 | type ParserLibraryImpl struct{} 42 | 43 | // ParseJson is embedded into the plugin and can be called by the plugin. 44 | func (ParserLibraryImpl) ParseJson(_ context.Context, request *export.ParseJsonRequest) (*export.ParseJsonResponse, error) { 45 | var person export.Person 46 | if err := json.Unmarshal(request.GetContent(), &person); err != nil { 47 | return nil, err 48 | } 49 | 50 | return &export.ParseJsonResponse{Response: &person}, nil 51 | } 52 | ``` 53 | 54 | Then, generate source code stubs for the base plugin. 55 | 56 | ```shell 57 | $ protoc --go-plugin_out=. --go-plugin_opt=paths=source_relative proto/greer.proto 58 | ``` 59 | 60 | Register exported hosts functions module int while providing new WazeroRuntime in the `proto.NewGreeterPlugin()`. 61 | 62 | ```go 63 | ctx := context.Background() 64 | p, err := proto.NewGreeterPlugin(ctx, proto.WazeroRuntime(func(ctx context.Context) (wazero.Runtime, error) { 65 | r, err := proto.DefaultWazeroRuntime()(ctx) 66 | if err != nil { 67 | return nil, err 68 | } 69 | return r, export.Instantiate(ctx, r, impl.ParserLibraryImpl{}) 70 | })) 71 | ``` 72 | 73 | ## Call host functions in a plugin 74 | The exported as a library or SDK host functions can be called in plugins. 75 | 76 | ```go 77 | parserLibrary := export.NewParserLibrary() 78 | 79 | // Call the host function to parse JSON 80 | resp, err := parserLibrary.ParseJson(ctx, &export.ParseJsonRequest{ 81 | Content: []byte(fmt.Sprintf(`{"name": "%s", "age": 20}`, sanrequest.Message)), 82 | }) 83 | if err != nil { 84 | return nil, err 85 | } 86 | ``` 87 | 88 | ## Compile a plugin 89 | Use TinyGo to compile the plugin to Wasm. 90 | 91 | ```shell 92 | $ go generate main.go 93 | ``` 94 | 95 | ## Run 96 | `main.go` loads the above plugin. 97 | 98 | ```shell 99 | $ go run main.go 100 | ``` 101 | ```shll 102 | 2022/08/28 10:13:57 Sending a HTTP request... 103 | Hello, Sato. This is Yamada-san (age 20). 104 | ``` 105 | 106 | 107 | -------------------------------------------------------------------------------- /examples/host-functions/greeting/greet.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go-plugin. DO NOT EDIT. 2 | // versions: 3 | // protoc-gen-go-plugin v0.1.0 4 | // protoc v3.21.12 5 | // source: examples/host-functions/greeting/greet.proto 6 | 7 | package greeting 8 | 9 | import ( 10 | context "context" 11 | emptypb "github.com/knqyf263/go-plugin/types/known/emptypb" 12 | protoreflect "google.golang.org/protobuf/reflect/protoreflect" 13 | protoimpl "google.golang.org/protobuf/runtime/protoimpl" 14 | ) 15 | 16 | const ( 17 | // Verify that this generated code is sufficiently up-to-date. 18 | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) 19 | // Verify that runtime/protoimpl is sufficiently up-to-date. 20 | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) 21 | ) 22 | 23 | // The request message containing the user's name. 24 | type GreetRequest struct { 25 | state protoimpl.MessageState 26 | sizeCache protoimpl.SizeCache 27 | unknownFields protoimpl.UnknownFields 28 | 29 | Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` 30 | } 31 | 32 | func (x *GreetRequest) ProtoReflect() protoreflect.Message { 33 | panic(`not implemented`) 34 | } 35 | 36 | func (x *GreetRequest) GetName() string { 37 | if x != nil { 38 | return x.Name 39 | } 40 | return "" 41 | } 42 | 43 | // The response message containing the greetings 44 | type GreetReply struct { 45 | state protoimpl.MessageState 46 | sizeCache protoimpl.SizeCache 47 | unknownFields protoimpl.UnknownFields 48 | 49 | Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` 50 | } 51 | 52 | func (x *GreetReply) ProtoReflect() protoreflect.Message { 53 | panic(`not implemented`) 54 | } 55 | 56 | func (x *GreetReply) GetMessage() string { 57 | if x != nil { 58 | return x.Message 59 | } 60 | return "" 61 | } 62 | 63 | type HttpGetRequest struct { 64 | state protoimpl.MessageState 65 | sizeCache protoimpl.SizeCache 66 | unknownFields protoimpl.UnknownFields 67 | 68 | Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"` 69 | } 70 | 71 | func (x *HttpGetRequest) ProtoReflect() protoreflect.Message { 72 | panic(`not implemented`) 73 | } 74 | 75 | func (x *HttpGetRequest) GetUrl() string { 76 | if x != nil { 77 | return x.Url 78 | } 79 | return "" 80 | } 81 | 82 | type HttpGetResponse struct { 83 | state protoimpl.MessageState 84 | sizeCache protoimpl.SizeCache 85 | unknownFields protoimpl.UnknownFields 86 | 87 | Response []byte `protobuf:"bytes,1,opt,name=response,proto3" json:"response,omitempty"` 88 | } 89 | 90 | func (x *HttpGetResponse) ProtoReflect() protoreflect.Message { 91 | panic(`not implemented`) 92 | } 93 | 94 | func (x *HttpGetResponse) GetResponse() []byte { 95 | if x != nil { 96 | return x.Response 97 | } 98 | return nil 99 | } 100 | 101 | type LogRequest struct { 102 | state protoimpl.MessageState 103 | sizeCache protoimpl.SizeCache 104 | unknownFields protoimpl.UnknownFields 105 | 106 | Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` 107 | } 108 | 109 | func (x *LogRequest) ProtoReflect() protoreflect.Message { 110 | panic(`not implemented`) 111 | } 112 | 113 | func (x *LogRequest) GetMessage() string { 114 | if x != nil { 115 | return x.Message 116 | } 117 | return "" 118 | } 119 | 120 | // The greeting service definition. 121 | // go:plugin type=plugin version=1 122 | type Greeter interface { 123 | // Sends a greeting 124 | Greet(context.Context, *GreetRequest) (*GreetReply, error) 125 | } 126 | 127 | // The host functions embedded into the plugin 128 | // go:plugin type=host 129 | type HostFunctions interface { 130 | // Sends a HTTP GET request 131 | HttpGet(context.Context, *HttpGetRequest) (*HttpGetResponse, error) 132 | // Shows a log message 133 | Log(context.Context, *LogRequest) (*emptypb.Empty, error) 134 | } 135 | -------------------------------------------------------------------------------- /tests/host-functions/proto/host.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go-plugin. DO NOT EDIT. 2 | // versions: 3 | // protoc-gen-go-plugin v0.1.0 4 | // protoc v3.21.12 5 | // source: tests/host-functions/proto/host.proto 6 | 7 | package proto 8 | 9 | import ( 10 | context "context" 11 | protoreflect "google.golang.org/protobuf/reflect/protoreflect" 12 | protoimpl "google.golang.org/protobuf/runtime/protoimpl" 13 | ) 14 | 15 | const ( 16 | // Verify that this generated code is sufficiently up-to-date. 17 | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) 18 | // Verify that runtime/protoimpl is sufficiently up-to-date. 19 | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) 20 | ) 21 | 22 | // The request message containing the user's name. 23 | type GreetRequest struct { 24 | state protoimpl.MessageState 25 | sizeCache protoimpl.SizeCache 26 | unknownFields protoimpl.UnknownFields 27 | 28 | Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` 29 | } 30 | 31 | func (x *GreetRequest) ProtoReflect() protoreflect.Message { 32 | panic(`not implemented`) 33 | } 34 | 35 | func (x *GreetRequest) GetName() string { 36 | if x != nil { 37 | return x.Name 38 | } 39 | return "" 40 | } 41 | 42 | // The response message containing the greetings 43 | type GreetReply struct { 44 | state protoimpl.MessageState 45 | sizeCache protoimpl.SizeCache 46 | unknownFields protoimpl.UnknownFields 47 | 48 | Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` 49 | } 50 | 51 | func (x *GreetReply) ProtoReflect() protoreflect.Message { 52 | panic(`not implemented`) 53 | } 54 | 55 | func (x *GreetReply) GetMessage() string { 56 | if x != nil { 57 | return x.Message 58 | } 59 | return "" 60 | } 61 | 62 | type ParseJsonRequest struct { 63 | state protoimpl.MessageState 64 | sizeCache protoimpl.SizeCache 65 | unknownFields protoimpl.UnknownFields 66 | 67 | Content []byte `protobuf:"bytes,1,opt,name=content,proto3" json:"content,omitempty"` 68 | } 69 | 70 | func (x *ParseJsonRequest) ProtoReflect() protoreflect.Message { 71 | panic(`not implemented`) 72 | } 73 | 74 | func (x *ParseJsonRequest) GetContent() []byte { 75 | if x != nil { 76 | return x.Content 77 | } 78 | return nil 79 | } 80 | 81 | type ParseJsonResponse struct { 82 | state protoimpl.MessageState 83 | sizeCache protoimpl.SizeCache 84 | unknownFields protoimpl.UnknownFields 85 | 86 | Response *Person `protobuf:"bytes,1,opt,name=response,proto3" json:"response,omitempty"` 87 | } 88 | 89 | func (x *ParseJsonResponse) ProtoReflect() protoreflect.Message { 90 | panic(`not implemented`) 91 | } 92 | 93 | func (x *ParseJsonResponse) GetResponse() *Person { 94 | if x != nil { 95 | return x.Response 96 | } 97 | return nil 98 | } 99 | 100 | type Person struct { 101 | state protoimpl.MessageState 102 | sizeCache protoimpl.SizeCache 103 | unknownFields protoimpl.UnknownFields 104 | 105 | Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` 106 | Age int64 `protobuf:"varint,2,opt,name=age,proto3" json:"age,omitempty"` 107 | } 108 | 109 | func (x *Person) ProtoReflect() protoreflect.Message { 110 | panic(`not implemented`) 111 | } 112 | 113 | func (x *Person) GetName() string { 114 | if x != nil { 115 | return x.Name 116 | } 117 | return "" 118 | } 119 | 120 | func (x *Person) GetAge() int64 { 121 | if x != nil { 122 | return x.Age 123 | } 124 | return 0 125 | } 126 | 127 | // The greeting service definition. 128 | // go:plugin type=plugin version=1 129 | type Greeter interface { 130 | // Sends a greeting 131 | Greet(context.Context, *GreetRequest) (*GreetReply, error) 132 | } 133 | 134 | // The host functions embedded into the plugin 135 | // go:plugin type=host 136 | type HostFunctions interface { 137 | ParseJson(context.Context, *ParseJsonRequest) (*ParseJsonResponse, error) 138 | } 139 | -------------------------------------------------------------------------------- /types/known/durationpb/duration.pb.go: -------------------------------------------------------------------------------- 1 | // Protocol Buffers - Google's data interchange format 2 | // Copyright 2008 Google Inc. All rights reserved. 3 | // Copyright 2022 Teppei Fukuda. All rights reserved. 4 | // https://developers.google.com/protocol-buffers/ 5 | 6 | // Code generated by protoc-gen-go-plugin. DO NOT EDIT. 7 | // versions: 8 | // protoc-gen-go-plugin v0.1.0 9 | // protoc v3.21.12 10 | // source: types/known/durationpb/duration.proto 11 | 12 | package durationpb 13 | 14 | import ( 15 | protoreflect "google.golang.org/protobuf/reflect/protoreflect" 16 | protoimpl "google.golang.org/protobuf/runtime/protoimpl" 17 | ) 18 | 19 | const ( 20 | // Verify that this generated code is sufficiently up-to-date. 21 | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) 22 | // Verify that runtime/protoimpl is sufficiently up-to-date. 23 | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) 24 | ) 25 | 26 | // A Duration represents a signed, fixed-length span of time represented 27 | // as a count of seconds and fractions of seconds at nanosecond 28 | // resolution. It is independent of any calendar and concepts like "day" 29 | // or "month". It is related to Timestamp in that the difference between 30 | // two Timestamp values is a Duration and it can be added or subtracted 31 | // from a Timestamp. Range is approximately +-10,000 years. 32 | // 33 | // # Examples 34 | // 35 | // Example 1: Compute Duration from two Timestamps in pseudo code. 36 | // 37 | // Timestamp start = ...; 38 | // Timestamp end = ...; 39 | // Duration duration = ...; 40 | // 41 | // duration.seconds = end.seconds - start.seconds; 42 | // duration.nanos = end.nanos - start.nanos; 43 | // 44 | // if (duration.seconds < 0 && duration.nanos > 0) { 45 | // duration.seconds += 1; 46 | // duration.nanos -= 1000000000; 47 | // } else if (duration.seconds > 0 && duration.nanos < 0) { 48 | // duration.seconds -= 1; 49 | // duration.nanos += 1000000000; 50 | // } 51 | // 52 | // Example 2: Compute Timestamp from Timestamp + Duration in pseudo code. 53 | // 54 | // Timestamp start = ...; 55 | // Duration duration = ...; 56 | // Timestamp end = ...; 57 | // 58 | // end.seconds = start.seconds + duration.seconds; 59 | // end.nanos = start.nanos + duration.nanos; 60 | // 61 | // if (end.nanos < 0) { 62 | // end.seconds -= 1; 63 | // end.nanos += 1000000000; 64 | // } else if (end.nanos >= 1000000000) { 65 | // end.seconds += 1; 66 | // end.nanos -= 1000000000; 67 | // } 68 | // 69 | // Example 3: Compute Duration from datetime.timedelta in Python. 70 | // 71 | // td = datetime.timedelta(days=3, minutes=10) 72 | // duration = Duration() 73 | // duration.FromTimedelta(td) 74 | // 75 | // # JSON Mapping 76 | // 77 | // In JSON format, the Duration type is encoded as a string rather than an 78 | // object, where the string ends in the suffix "s" (indicating seconds) and 79 | // is preceded by the number of seconds, with nanoseconds expressed as 80 | // fractional seconds. For example, 3 seconds with 0 nanoseconds should be 81 | // encoded in JSON format as "3s", while 3 seconds and 1 nanosecond should 82 | // be expressed in JSON format as "3.000000001s", and 3 seconds and 1 83 | // microsecond should be expressed in JSON format as "3.000001s". 84 | type Duration struct { 85 | state protoimpl.MessageState 86 | sizeCache protoimpl.SizeCache 87 | unknownFields protoimpl.UnknownFields 88 | 89 | // Signed seconds of the span of time. Must be from -315,576,000,000 90 | // to +315,576,000,000 inclusive. Note: these bounds are computed from: 91 | // 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years 92 | Seconds int64 `protobuf:"varint,1,opt,name=seconds,proto3" json:"seconds,omitempty"` 93 | // Signed fractions of a second at nanosecond resolution of the span 94 | // of time. Durations less than one second are represented with a 0 95 | // `seconds` field and a positive or negative `nanos` field. For durations 96 | // of one second or more, a non-zero value for the `nanos` field must be 97 | // of the same sign as the `seconds` field. Must be from -999,999,999 98 | // to +999,999,999 inclusive. 99 | Nanos int32 `protobuf:"varint,2,opt,name=nanos,proto3" json:"nanos,omitempty"` 100 | } 101 | 102 | func (x *Duration) ProtoReflect() protoreflect.Message { 103 | panic(`not implemented`) 104 | } 105 | 106 | func (x *Duration) GetSeconds() int64 { 107 | if x != nil { 108 | return x.Seconds 109 | } 110 | return 0 111 | } 112 | 113 | func (x *Duration) GetNanos() int32 { 114 | if x != nil { 115 | return x.Nanos 116 | } 117 | return 0 118 | } 119 | -------------------------------------------------------------------------------- /gen/plugin.go: -------------------------------------------------------------------------------- 1 | package gen 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "google.golang.org/protobuf/compiler/protogen" 8 | ) 9 | 10 | func (gg *Generator) generatePluginFile(f *fileInfo) { 11 | // This file will be imported by plugins written in TinyGo 12 | filename := f.GeneratedFilenamePrefix + "_plugin.pb.go" 13 | g := gg.plugin.NewGeneratedFile(filename, f.GoImportPath) 14 | 15 | if len(f.pluginServices) == 0 && f.hostService == nil { 16 | g.Skip() 17 | } 18 | 19 | // Build constraints 20 | g.P("//go:build tinygo.wasm") 21 | 22 | // Generate header 23 | gg.generateHeader(g, f) 24 | 25 | // Generate exported functions that wrap interfaces 26 | for _, service := range f.pluginServices { 27 | genPlugin(g, f, service) 28 | } 29 | 30 | genHostFunctions(g, f) 31 | } 32 | 33 | func genPlugin(g *protogen.GeneratedFile, f *fileInfo, service *serviceInfo) { 34 | serviceVar := strings.ToLower(service.GoName[:1]) + service.GoName[1:] 35 | 36 | // API version 37 | g.P("const ", service.GoName, "PluginAPIVersion = ", service.Version) 38 | g.P(fmt.Sprintf(` 39 | //export %s_api_version 40 | func _%s_api_version() uint64 { 41 | return %sPluginAPIVersion 42 | }`, 43 | toSnakeCase(service.GoName), toSnakeCase(service.GoName), service.GoName, 44 | )) 45 | 46 | // Variable definition 47 | g.P("var ", serviceVar, " ", service.GoName) 48 | 49 | // Register function 50 | g.P("func Register", service.GoName, "(p ", service.GoName, ") {") 51 | g.P(serviceVar, "= p") 52 | g.P("}") 53 | 54 | // Exported functions 55 | for _, method := range service.Methods { 56 | exportedName := toSnakeCase(service.GoName + method.GoName) 57 | g.P("//export ", exportedName) 58 | g.P("func _", exportedName, "(ptr, size uint32) uint64 {") 59 | g.P("b := ", g.QualifiedGoIdent(pluginWasmPackage.Ident("PtrToByte")), "(ptr, size)") 60 | 61 | g.P("req := new(", g.QualifiedGoIdent(method.Input.GoIdent), ")") 62 | g.P(`if err := req.UnmarshalVT(b); err != nil { 63 | return 0 64 | }`) 65 | g.P(fmt.Sprintf(`response, err := %s.%s(%s(), req)`, 66 | serviceVar, method.GoName, g.QualifiedGoIdent(contextPackage.Ident("Background")))) 67 | g.P(fmt.Sprintf(`if err != nil { 68 | ptr, size = %s([]byte(err.Error())) 69 | return (uint64(ptr) << uint64(32)) | uint64(size) | 70 | // Indicate that this is the error string by setting the 32-th bit, assuming that 71 | // no data exceeds 31-bit size (2 GiB). 72 | %s 73 | } 74 | 75 | b, err = response.MarshalVT() 76 | if err != nil { 77 | return 0 78 | } 79 | ptr, size = %s(b) 80 | return (uint64(ptr) << uint64(32)) | uint64(size)`, 81 | g.QualifiedGoIdent(pluginWasmPackage.Ident("ByteToPtr")), 82 | ErrorMaskBit, 83 | g.QualifiedGoIdent(pluginWasmPackage.Ident("ByteToPtr")))) 84 | g.P("}") 85 | } 86 | } 87 | 88 | func genHostFunctions(g *protogen.GeneratedFile, f *fileInfo) { 89 | if f.hostService == nil { 90 | return 91 | } 92 | 93 | g.Import(unsafePackage) 94 | 95 | // Host functions 96 | structName := strings.ToLower(f.hostService.GoName[:1]) + f.hostService.GoName[1:] 97 | g.P("type ", structName, " struct{}") 98 | g.P() 99 | g.P("func New", f.hostService.GoName, "()", f.hostService.GoName, "{") 100 | g.P(" return ", structName, "{}") 101 | g.P("}") 102 | 103 | for _, method := range f.hostService.Methods { 104 | importedName := toSnakeCase(method.GoName) 105 | g.P(fmt.Sprintf(` 106 | //go:wasm-module %s 107 | //export %s 108 | //go:linkname _%s 109 | func _%s(ptr uint32, size uint32) uint64 110 | 111 | func (h %s) %s(ctx %s, request *%s) (*%s, error) { 112 | buf, err := request.MarshalVT() 113 | if err != nil { 114 | return nil, err 115 | } 116 | ptr, size := %s(buf) 117 | ptrSize := _%s(ptr, size) 118 | %s(ptr) 119 | 120 | ptr = uint32(ptrSize >> 32) 121 | size = uint32(ptrSize) 122 | buf = %s(ptr, size) 123 | 124 | response := new(%s) 125 | if err = response.UnmarshalVT(buf); err != nil { 126 | return nil, err 127 | } 128 | return response, nil 129 | }`, 130 | f.hostService.Module, importedName, importedName, importedName, structName, method.GoName, 131 | g.QualifiedGoIdent(contextPackage.Ident("Context")), 132 | g.QualifiedGoIdent(method.Input.GoIdent), 133 | g.QualifiedGoIdent(method.Output.GoIdent), 134 | g.QualifiedGoIdent(pluginWasmPackage.Ident("ByteToPtr")), 135 | importedName, 136 | g.QualifiedGoIdent(pluginWasmPackage.Ident("FreePtr")), 137 | g.QualifiedGoIdent(pluginWasmPackage.Ident("PtrToByte")), 138 | g.QualifiedGoIdent(method.Output.GoIdent), 139 | )) 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /genid/struct_gen.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Code generated by generate-protos. DO NOT EDIT. 6 | 7 | package genid 8 | 9 | import ( 10 | protoreflect "google.golang.org/protobuf/reflect/protoreflect" 11 | ) 12 | 13 | const File_google_protobuf_struct_proto = "google/protobuf/struct.proto" 14 | 15 | // Full and short names for google.protobuf.NullValue. 16 | const ( 17 | NullValue_enum_fullname = "google.protobuf.NullValue" 18 | NullValue_enum_name = "NullValue" 19 | ) 20 | 21 | // Names for google.protobuf.Struct. 22 | const ( 23 | Struct_message_name protoreflect.Name = "Struct" 24 | Struct_message_fullname protoreflect.FullName = "google.protobuf.Struct" 25 | ) 26 | 27 | // Field names for google.protobuf.Struct. 28 | const ( 29 | Struct_Fields_field_name protoreflect.Name = "fields" 30 | 31 | Struct_Fields_field_fullname protoreflect.FullName = "google.protobuf.Struct.fields" 32 | ) 33 | 34 | // Field numbers for google.protobuf.Struct. 35 | const ( 36 | Struct_Fields_field_number protoreflect.FieldNumber = 1 37 | ) 38 | 39 | // Names for google.protobuf.Struct.FieldsEntry. 40 | const ( 41 | Struct_FieldsEntry_message_name protoreflect.Name = "FieldsEntry" 42 | Struct_FieldsEntry_message_fullname protoreflect.FullName = "google.protobuf.Struct.FieldsEntry" 43 | ) 44 | 45 | // Field names for google.protobuf.Struct.FieldsEntry. 46 | const ( 47 | Struct_FieldsEntry_Key_field_name protoreflect.Name = "key" 48 | Struct_FieldsEntry_Value_field_name protoreflect.Name = "value" 49 | 50 | Struct_FieldsEntry_Key_field_fullname protoreflect.FullName = "google.protobuf.Struct.FieldsEntry.key" 51 | Struct_FieldsEntry_Value_field_fullname protoreflect.FullName = "google.protobuf.Struct.FieldsEntry.value" 52 | ) 53 | 54 | // Field numbers for google.protobuf.Struct.FieldsEntry. 55 | const ( 56 | Struct_FieldsEntry_Key_field_number protoreflect.FieldNumber = 1 57 | Struct_FieldsEntry_Value_field_number protoreflect.FieldNumber = 2 58 | ) 59 | 60 | // Names for google.protobuf.Value. 61 | const ( 62 | Value_message_name protoreflect.Name = "Value" 63 | Value_message_fullname protoreflect.FullName = "google.protobuf.Value" 64 | ) 65 | 66 | // Field names for google.protobuf.Value. 67 | const ( 68 | Value_NullValue_field_name protoreflect.Name = "null_value" 69 | Value_NumberValue_field_name protoreflect.Name = "number_value" 70 | Value_StringValue_field_name protoreflect.Name = "string_value" 71 | Value_BoolValue_field_name protoreflect.Name = "bool_value" 72 | Value_StructValue_field_name protoreflect.Name = "struct_value" 73 | Value_ListValue_field_name protoreflect.Name = "list_value" 74 | 75 | Value_NullValue_field_fullname protoreflect.FullName = "google.protobuf.Value.null_value" 76 | Value_NumberValue_field_fullname protoreflect.FullName = "google.protobuf.Value.number_value" 77 | Value_StringValue_field_fullname protoreflect.FullName = "google.protobuf.Value.string_value" 78 | Value_BoolValue_field_fullname protoreflect.FullName = "google.protobuf.Value.bool_value" 79 | Value_StructValue_field_fullname protoreflect.FullName = "google.protobuf.Value.struct_value" 80 | Value_ListValue_field_fullname protoreflect.FullName = "google.protobuf.Value.list_value" 81 | ) 82 | 83 | // Field numbers for google.protobuf.Value. 84 | const ( 85 | Value_NullValue_field_number protoreflect.FieldNumber = 1 86 | Value_NumberValue_field_number protoreflect.FieldNumber = 2 87 | Value_StringValue_field_number protoreflect.FieldNumber = 3 88 | Value_BoolValue_field_number protoreflect.FieldNumber = 4 89 | Value_StructValue_field_number protoreflect.FieldNumber = 5 90 | Value_ListValue_field_number protoreflect.FieldNumber = 6 91 | ) 92 | 93 | // Oneof names for google.protobuf.Value. 94 | const ( 95 | Value_Kind_oneof_name protoreflect.Name = "kind" 96 | 97 | Value_Kind_oneof_fullname protoreflect.FullName = "google.protobuf.Value.kind" 98 | ) 99 | 100 | // Names for google.protobuf.ListValue. 101 | const ( 102 | ListValue_message_name protoreflect.Name = "ListValue" 103 | ListValue_message_fullname protoreflect.FullName = "google.protobuf.ListValue" 104 | ) 105 | 106 | // Field names for google.protobuf.ListValue. 107 | const ( 108 | ListValue_Values_field_name protoreflect.Name = "values" 109 | 110 | ListValue_Values_field_fullname protoreflect.FullName = "google.protobuf.ListValue.values" 111 | ) 112 | 113 | // Field numbers for google.protobuf.ListValue. 114 | const ( 115 | ListValue_Values_field_number protoreflect.FieldNumber = 1 116 | ) 117 | -------------------------------------------------------------------------------- /types/known/anypb/any.proto: -------------------------------------------------------------------------------- 1 | // Protocol Buffers - Google's data interchange format 2 | // Copyright 2008 Google Inc. All rights reserved. 3 | // Copyright 2022 Teppei Fukuda. All rights reserved. 4 | // https://developers.google.com/protocol-buffers/ 5 | 6 | syntax = "proto3"; 7 | 8 | option go_package = "github.com/knqyf263/go-plugin/types/known/anypb"; 9 | 10 | // `Any` contains an arbitrary serialized protocol buffer message along with a 11 | // URL that describes the type of the serialized message. 12 | // 13 | // Protobuf library provides support to pack/unpack Any values in the form 14 | // of utility functions or additional generated methods of the Any type. 15 | // 16 | // Example 1: Pack and unpack a message in C++. 17 | // 18 | // Foo foo = ...; 19 | // Any any; 20 | // any.PackFrom(foo); 21 | // ... 22 | // if (any.UnpackTo(&foo)) { 23 | // ... 24 | // } 25 | // 26 | // Example 2: Pack and unpack a message in Java. 27 | // 28 | // Foo foo = ...; 29 | // Any any = Any.pack(foo); 30 | // ... 31 | // if (any.is(Foo.class)) { 32 | // foo = any.unpack(Foo.class); 33 | // } 34 | // 35 | // Example 3: Pack and unpack a message in Python. 36 | // 37 | // foo = Foo(...) 38 | // any = Any() 39 | // any.Pack(foo) 40 | // ... 41 | // if any.Is(Foo.DESCRIPTOR): 42 | // any.Unpack(foo) 43 | // ... 44 | // 45 | // Example 4: Pack and unpack a message in Go 46 | // 47 | // foo := &pb.Foo{...} 48 | // any, err := anypb.New(foo) 49 | // if err != nil { 50 | // ... 51 | // } 52 | // ... 53 | // foo := &pb.Foo{} 54 | // if err := any.UnmarshalTo(foo); err != nil { 55 | // ... 56 | // } 57 | // 58 | // The pack methods provided by protobuf library will by default use 59 | // 'type.googleapis.com/full.type.name' as the type URL and the unpack 60 | // methods only use the fully qualified type name after the last '/' 61 | // in the type URL, for example "foo.bar.com/x/y.z" will yield type 62 | // name "y.z". 63 | // 64 | // 65 | // JSON 66 | // 67 | // The JSON representation of an `Any` value uses the regular 68 | // representation of the deserialized, embedded message, with an 69 | // additional field `@type` which contains the type URL. Example: 70 | // 71 | // package google.profile; 72 | // message Person { 73 | // string first_name = 1; 74 | // string last_name = 2; 75 | // } 76 | // 77 | // { 78 | // "@type": "type.googleapis.com/google.profile.Person", 79 | // "firstName": , 80 | // "lastName": 81 | // } 82 | // 83 | // If the embedded message type is well-known and has a custom JSON 84 | // representation, that representation will be embedded adding a field 85 | // `value` which holds the custom JSON in addition to the `@type` 86 | // field. Example (for message [google.protobuf.Duration][]): 87 | // 88 | // { 89 | // "@type": "type.googleapis.com/google.protobuf.Duration", 90 | // "value": "1.212s" 91 | // } 92 | // 93 | message Any { 94 | // A URL/resource name that uniquely identifies the type of the serialized 95 | // protocol buffer message. This string must contain at least 96 | // one "/" character. The last segment of the URL's path must represent 97 | // the fully qualified name of the type (as in 98 | // `path/google.protobuf.Duration`). The name should be in a canonical form 99 | // (e.g., leading "." is not accepted). 100 | // 101 | // In practice, teams usually precompile into the binary all types that they 102 | // expect it to use in the context of Any. However, for URLs which use the 103 | // scheme `http`, `https`, or no scheme, one can optionally set up a type 104 | // server that maps type URLs to message definitions as follows: 105 | // 106 | // * If no scheme is provided, `https` is assumed. 107 | // * An HTTP GET on the URL must yield a [google.protobuf.Type][] 108 | // value in binary format, or produce an error. 109 | // * Applications are allowed to cache lookup results based on the 110 | // URL, or have them precompiled into a binary to avoid any 111 | // lookup. Therefore, binary compatibility needs to be preserved 112 | // on changes to types. (Use versioned type names to manage 113 | // breaking changes.) 114 | // 115 | // Note: this functionality is not currently available in the official 116 | // protobuf release, and it is not used for type URLs beginning with 117 | // type.googleapis.com. 118 | // 119 | // Schemes other than `http`, `https` (or the empty scheme) might be 120 | // used with implementation specific semantics. 121 | // 122 | string type_url = 1; 123 | 124 | // Must be a valid serialized protocol buffer of the above specified type. 125 | bytes value = 2; 126 | } 127 | -------------------------------------------------------------------------------- /types/known/typepb/type.proto: -------------------------------------------------------------------------------- 1 | // Protocol Buffers - Google's data interchange format 2 | // Copyright 2008 Google Inc. All rights reserved. 3 | // Copyright 2022 Teppei Fukuda. All rights reserved. 4 | // https://developers.google.com/protocol-buffers/ 5 | 6 | syntax = "proto3"; 7 | 8 | package google.protobuf; 9 | 10 | import "google/protobuf/any.proto"; 11 | import "google/protobuf/source_context.proto"; 12 | 13 | option go_package = "github.com/knqyf263/go-plugin/types/known/typepb"; 14 | 15 | // A protocol buffer message type. 16 | message Type { 17 | // The fully qualified message name. 18 | string name = 1; 19 | // The list of fields. 20 | repeated Field fields = 2; 21 | // The list of types appearing in `oneof` definitions in this type. 22 | repeated string oneofs = 3; 23 | // The protocol buffer options. 24 | repeated Option options = 4; 25 | // The source context. 26 | SourceContext source_context = 5; 27 | // The source syntax. 28 | Syntax syntax = 6; 29 | } 30 | 31 | // A single field of a message type. 32 | message Field { 33 | // Basic field types. 34 | enum Kind { 35 | // Field type unknown. 36 | TYPE_UNKNOWN = 0; 37 | // Field type double. 38 | TYPE_DOUBLE = 1; 39 | // Field type float. 40 | TYPE_FLOAT = 2; 41 | // Field type int64. 42 | TYPE_INT64 = 3; 43 | // Field type uint64. 44 | TYPE_UINT64 = 4; 45 | // Field type int32. 46 | TYPE_INT32 = 5; 47 | // Field type fixed64. 48 | TYPE_FIXED64 = 6; 49 | // Field type fixed32. 50 | TYPE_FIXED32 = 7; 51 | // Field type bool. 52 | TYPE_BOOL = 8; 53 | // Field type string. 54 | TYPE_STRING = 9; 55 | // Field type group. Proto2 syntax only, and deprecated. 56 | TYPE_GROUP = 10; 57 | // Field type message. 58 | TYPE_MESSAGE = 11; 59 | // Field type bytes. 60 | TYPE_BYTES = 12; 61 | // Field type uint32. 62 | TYPE_UINT32 = 13; 63 | // Field type enum. 64 | TYPE_ENUM = 14; 65 | // Field type sfixed32. 66 | TYPE_SFIXED32 = 15; 67 | // Field type sfixed64. 68 | TYPE_SFIXED64 = 16; 69 | // Field type sint32. 70 | TYPE_SINT32 = 17; 71 | // Field type sint64. 72 | TYPE_SINT64 = 18; 73 | } 74 | 75 | // Whether a field is optional, required, or repeated. 76 | enum Cardinality { 77 | // For fields with unknown cardinality. 78 | CARDINALITY_UNKNOWN = 0; 79 | // For optional fields. 80 | CARDINALITY_OPTIONAL = 1; 81 | // For required fields. Proto2 syntax only. 82 | CARDINALITY_REQUIRED = 2; 83 | // For repeated fields. 84 | CARDINALITY_REPEATED = 3; 85 | } 86 | 87 | // The field type. 88 | Kind kind = 1; 89 | // The field cardinality. 90 | Cardinality cardinality = 2; 91 | // The field number. 92 | int32 number = 3; 93 | // The field name. 94 | string name = 4; 95 | // The field type URL, without the scheme, for message or enumeration 96 | // types. Example: `"type.googleapis.com/google.protobuf.Timestamp"`. 97 | string type_url = 6; 98 | // The index of the field type in `Type.oneofs`, for message or enumeration 99 | // types. The first type has index 1; zero means the type is not in the list. 100 | int32 oneof_index = 7; 101 | // Whether to use alternative packed wire representation. 102 | bool packed = 8; 103 | // The protocol buffer options. 104 | repeated Option options = 9; 105 | // The field JSON name. 106 | string json_name = 10; 107 | // The string value of the default value of this field. Proto2 syntax only. 108 | string default_value = 11; 109 | } 110 | 111 | // Enum type definition. 112 | message Enum { 113 | // Enum type name. 114 | string name = 1; 115 | // Enum value definitions. 116 | repeated EnumValue enumvalue = 2; 117 | // Protocol buffer options. 118 | repeated Option options = 3; 119 | // The source context. 120 | SourceContext source_context = 4; 121 | // The source syntax. 122 | Syntax syntax = 5; 123 | } 124 | 125 | // Enum value definition. 126 | message EnumValue { 127 | // Enum value name. 128 | string name = 1; 129 | // Enum value number. 130 | int32 number = 2; 131 | // Protocol buffer options. 132 | repeated Option options = 3; 133 | } 134 | 135 | // A protocol buffer option, which can be attached to a message, field, 136 | // enumeration, etc. 137 | message Option { 138 | // The option's name. For protobuf built-in options (options defined in 139 | // descriptor.proto), this is the short name. For example, `"map_entry"`. 140 | // For custom options, it should be the fully-qualified name. For example, 141 | // `"google.api.http"`. 142 | string name = 1; 143 | // The option's value packed in an Any message. If the value is a primitive, 144 | // the corresponding wrapper type defined in google/protobuf/wrappers.proto 145 | // should be used. If the value is an enum, it should be stored as an int32 146 | // value using the google.protobuf.Int32Value type. 147 | Any value = 2; 148 | } 149 | 150 | // The syntax in which a protocol buffer element is defined. 151 | enum Syntax { 152 | // Syntax `proto2`. 153 | SYNTAX_PROTO2 = 0; 154 | // Syntax `proto3`. 155 | SYNTAX_PROTO3 = 1; 156 | } 157 | -------------------------------------------------------------------------------- /genid/api_gen.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Code generated by generate-protos. DO NOT EDIT. 6 | 7 | package genid 8 | 9 | import ( 10 | protoreflect "google.golang.org/protobuf/reflect/protoreflect" 11 | ) 12 | 13 | const File_google_protobuf_api_proto = "google/protobuf/api.proto" 14 | 15 | // Names for google.protobuf.Api. 16 | const ( 17 | Api_message_name protoreflect.Name = "Api" 18 | Api_message_fullname protoreflect.FullName = "google.protobuf.Api" 19 | ) 20 | 21 | // Field names for google.protobuf.Api. 22 | const ( 23 | Api_Name_field_name protoreflect.Name = "name" 24 | Api_Methods_field_name protoreflect.Name = "methods" 25 | Api_Options_field_name protoreflect.Name = "options" 26 | Api_Version_field_name protoreflect.Name = "version" 27 | Api_SourceContext_field_name protoreflect.Name = "source_context" 28 | Api_Mixins_field_name protoreflect.Name = "mixins" 29 | Api_Syntax_field_name protoreflect.Name = "syntax" 30 | 31 | Api_Name_field_fullname protoreflect.FullName = "google.protobuf.Api.name" 32 | Api_Methods_field_fullname protoreflect.FullName = "google.protobuf.Api.methods" 33 | Api_Options_field_fullname protoreflect.FullName = "google.protobuf.Api.options" 34 | Api_Version_field_fullname protoreflect.FullName = "google.protobuf.Api.version" 35 | Api_SourceContext_field_fullname protoreflect.FullName = "google.protobuf.Api.source_context" 36 | Api_Mixins_field_fullname protoreflect.FullName = "google.protobuf.Api.mixins" 37 | Api_Syntax_field_fullname protoreflect.FullName = "google.protobuf.Api.syntax" 38 | ) 39 | 40 | // Field numbers for google.protobuf.Api. 41 | const ( 42 | Api_Name_field_number protoreflect.FieldNumber = 1 43 | Api_Methods_field_number protoreflect.FieldNumber = 2 44 | Api_Options_field_number protoreflect.FieldNumber = 3 45 | Api_Version_field_number protoreflect.FieldNumber = 4 46 | Api_SourceContext_field_number protoreflect.FieldNumber = 5 47 | Api_Mixins_field_number protoreflect.FieldNumber = 6 48 | Api_Syntax_field_number protoreflect.FieldNumber = 7 49 | ) 50 | 51 | // Names for google.protobuf.Method. 52 | const ( 53 | Method_message_name protoreflect.Name = "Method" 54 | Method_message_fullname protoreflect.FullName = "google.protobuf.Method" 55 | ) 56 | 57 | // Field names for google.protobuf.Method. 58 | const ( 59 | Method_Name_field_name protoreflect.Name = "name" 60 | Method_RequestTypeUrl_field_name protoreflect.Name = "request_type_url" 61 | Method_RequestStreaming_field_name protoreflect.Name = "request_streaming" 62 | Method_ResponseTypeUrl_field_name protoreflect.Name = "response_type_url" 63 | Method_ResponseStreaming_field_name protoreflect.Name = "response_streaming" 64 | Method_Options_field_name protoreflect.Name = "options" 65 | Method_Syntax_field_name protoreflect.Name = "syntax" 66 | 67 | Method_Name_field_fullname protoreflect.FullName = "google.protobuf.Method.name" 68 | Method_RequestTypeUrl_field_fullname protoreflect.FullName = "google.protobuf.Method.request_type_url" 69 | Method_RequestStreaming_field_fullname protoreflect.FullName = "google.protobuf.Method.request_streaming" 70 | Method_ResponseTypeUrl_field_fullname protoreflect.FullName = "google.protobuf.Method.response_type_url" 71 | Method_ResponseStreaming_field_fullname protoreflect.FullName = "google.protobuf.Method.response_streaming" 72 | Method_Options_field_fullname protoreflect.FullName = "google.protobuf.Method.options" 73 | Method_Syntax_field_fullname protoreflect.FullName = "google.protobuf.Method.syntax" 74 | ) 75 | 76 | // Field numbers for google.protobuf.Method. 77 | const ( 78 | Method_Name_field_number protoreflect.FieldNumber = 1 79 | Method_RequestTypeUrl_field_number protoreflect.FieldNumber = 2 80 | Method_RequestStreaming_field_number protoreflect.FieldNumber = 3 81 | Method_ResponseTypeUrl_field_number protoreflect.FieldNumber = 4 82 | Method_ResponseStreaming_field_number protoreflect.FieldNumber = 5 83 | Method_Options_field_number protoreflect.FieldNumber = 6 84 | Method_Syntax_field_number protoreflect.FieldNumber = 7 85 | ) 86 | 87 | // Names for google.protobuf.Mixin. 88 | const ( 89 | Mixin_message_name protoreflect.Name = "Mixin" 90 | Mixin_message_fullname protoreflect.FullName = "google.protobuf.Mixin" 91 | ) 92 | 93 | // Field names for google.protobuf.Mixin. 94 | const ( 95 | Mixin_Name_field_name protoreflect.Name = "name" 96 | Mixin_Root_field_name protoreflect.Name = "root" 97 | 98 | Mixin_Name_field_fullname protoreflect.FullName = "google.protobuf.Mixin.name" 99 | Mixin_Root_field_fullname protoreflect.FullName = "google.protobuf.Mixin.root" 100 | ) 101 | 102 | // Field numbers for google.protobuf.Mixin. 103 | const ( 104 | Mixin_Name_field_number protoreflect.FieldNumber = 1 105 | Mixin_Root_field_number protoreflect.FieldNumber = 2 106 | ) 107 | -------------------------------------------------------------------------------- /types/known/anypb/any.pb.go: -------------------------------------------------------------------------------- 1 | // Protocol Buffers - Google's data interchange format 2 | // Copyright 2008 Google Inc. All rights reserved. 3 | // Copyright 2022 Teppei Fukuda. All rights reserved. 4 | // https://developers.google.com/protocol-buffers/ 5 | 6 | // Code generated by protoc-gen-go-plugin. DO NOT EDIT. 7 | // versions: 8 | // protoc-gen-go-plugin v0.1.0 9 | // protoc v3.21.12 10 | // source: types/known/anypb/any.proto 11 | 12 | package anypb 13 | 14 | import ( 15 | protoreflect "google.golang.org/protobuf/reflect/protoreflect" 16 | protoimpl "google.golang.org/protobuf/runtime/protoimpl" 17 | ) 18 | 19 | const ( 20 | // Verify that this generated code is sufficiently up-to-date. 21 | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) 22 | // Verify that runtime/protoimpl is sufficiently up-to-date. 23 | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) 24 | ) 25 | 26 | // `Any` contains an arbitrary serialized protocol buffer message along with a 27 | // URL that describes the type of the serialized message. 28 | // 29 | // Protobuf library provides support to pack/unpack Any values in the form 30 | // of utility functions or additional generated methods of the Any type. 31 | // 32 | // Example 1: Pack and unpack a message in C++. 33 | // 34 | // Foo foo = ...; 35 | // Any any; 36 | // any.PackFrom(foo); 37 | // ... 38 | // if (any.UnpackTo(&foo)) { 39 | // ... 40 | // } 41 | // 42 | // Example 2: Pack and unpack a message in Java. 43 | // 44 | // Foo foo = ...; 45 | // Any any = Any.pack(foo); 46 | // ... 47 | // if (any.is(Foo.class)) { 48 | // foo = any.unpack(Foo.class); 49 | // } 50 | // 51 | // Example 3: Pack and unpack a message in Python. 52 | // 53 | // foo = Foo(...) 54 | // any = Any() 55 | // any.Pack(foo) 56 | // ... 57 | // if any.Is(Foo.DESCRIPTOR): 58 | // any.Unpack(foo) 59 | // ... 60 | // 61 | // Example 4: Pack and unpack a message in Go 62 | // 63 | // foo := &pb.Foo{...} 64 | // any, err := anypb.New(foo) 65 | // if err != nil { 66 | // ... 67 | // } 68 | // ... 69 | // foo := &pb.Foo{} 70 | // if err := any.UnmarshalTo(foo); err != nil { 71 | // ... 72 | // } 73 | // 74 | // The pack methods provided by protobuf library will by default use 75 | // 'type.googleapis.com/full.type.name' as the type URL and the unpack 76 | // methods only use the fully qualified type name after the last '/' 77 | // in the type URL, for example "foo.bar.com/x/y.z" will yield type 78 | // name "y.z". 79 | // 80 | // # JSON 81 | // 82 | // The JSON representation of an `Any` value uses the regular 83 | // representation of the deserialized, embedded message, with an 84 | // additional field `@type` which contains the type URL. Example: 85 | // 86 | // package google.profile; 87 | // message Person { 88 | // string first_name = 1; 89 | // string last_name = 2; 90 | // } 91 | // 92 | // { 93 | // "@type": "type.googleapis.com/google.profile.Person", 94 | // "firstName": , 95 | // "lastName": 96 | // } 97 | // 98 | // If the embedded message type is well-known and has a custom JSON 99 | // representation, that representation will be embedded adding a field 100 | // `value` which holds the custom JSON in addition to the `@type` 101 | // field. Example (for message [google.protobuf.Duration][]): 102 | // 103 | // { 104 | // "@type": "type.googleapis.com/google.protobuf.Duration", 105 | // "value": "1.212s" 106 | // } 107 | type Any struct { 108 | state protoimpl.MessageState 109 | sizeCache protoimpl.SizeCache 110 | unknownFields protoimpl.UnknownFields 111 | 112 | // A URL/resource name that uniquely identifies the type of the serialized 113 | // protocol buffer message. This string must contain at least 114 | // one "/" character. The last segment of the URL's path must represent 115 | // the fully qualified name of the type (as in 116 | // `path/google.protobuf.Duration`). The name should be in a canonical form 117 | // (e.g., leading "." is not accepted). 118 | // 119 | // In practice, teams usually precompile into the binary all types that they 120 | // expect it to use in the context of Any. However, for URLs which use the 121 | // scheme `http`, `https`, or no scheme, one can optionally set up a type 122 | // server that maps type URLs to message definitions as follows: 123 | // 124 | // - If no scheme is provided, `https` is assumed. 125 | // - An HTTP GET on the URL must yield a [google.protobuf.Type][] 126 | // value in binary format, or produce an error. 127 | // - Applications are allowed to cache lookup results based on the 128 | // URL, or have them precompiled into a binary to avoid any 129 | // lookup. Therefore, binary compatibility needs to be preserved 130 | // on changes to types. (Use versioned type names to manage 131 | // breaking changes.) 132 | // 133 | // Note: this functionality is not currently available in the official 134 | // protobuf release, and it is not used for type URLs beginning with 135 | // type.googleapis.com. 136 | // 137 | // Schemes other than `http`, `https` (or the empty scheme) might be 138 | // used with implementation specific semantics. 139 | TypeUrl string `protobuf:"bytes,1,opt,name=type_url,json=typeUrl,proto3" json:"type_url,omitempty"` 140 | // Must be a valid serialized protocol buffer of the above specified type. 141 | Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` 142 | } 143 | 144 | func (x *Any) ProtoReflect() protoreflect.Message { 145 | panic(`not implemented`) 146 | } 147 | 148 | func (x *Any) GetTypeUrl() string { 149 | if x != nil { 150 | return x.TypeUrl 151 | } 152 | return "" 153 | } 154 | 155 | func (x *Any) GetValue() []byte { 156 | if x != nil { 157 | return x.Value 158 | } 159 | return nil 160 | } 161 | -------------------------------------------------------------------------------- /types/known/emptypb/empty_vtproto.pb.go: -------------------------------------------------------------------------------- 1 | // Protocol Buffers - Google's data interchange format 2 | // Copyright 2008 Google Inc. All rights reserved. 3 | // Copyright 2022 Teppei Fukuda. All rights reserved. 4 | // https://developers.google.com/protocol-buffers/ 5 | 6 | // Code generated by protoc-gen-go-plugin. DO NOT EDIT. 7 | // versions: 8 | // protoc-gen-go-plugin v0.1.0 9 | // protoc v3.21.12 10 | // source: types/known/emptypb/empty.proto 11 | 12 | package emptypb 13 | 14 | import ( 15 | fmt "fmt" 16 | protoimpl "google.golang.org/protobuf/runtime/protoimpl" 17 | io "io" 18 | bits "math/bits" 19 | ) 20 | 21 | const ( 22 | // Verify that this generated code is sufficiently up-to-date. 23 | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) 24 | // Verify that runtime/protoimpl is sufficiently up-to-date. 25 | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) 26 | ) 27 | 28 | func (m *Empty) MarshalVT() (dAtA []byte, err error) { 29 | if m == nil { 30 | return nil, nil 31 | } 32 | size := m.SizeVT() 33 | dAtA = make([]byte, size) 34 | n, err := m.MarshalToSizedBufferVT(dAtA[:size]) 35 | if err != nil { 36 | return nil, err 37 | } 38 | return dAtA[:n], nil 39 | } 40 | 41 | func (m *Empty) MarshalToVT(dAtA []byte) (int, error) { 42 | size := m.SizeVT() 43 | return m.MarshalToSizedBufferVT(dAtA[:size]) 44 | } 45 | 46 | func (m *Empty) MarshalToSizedBufferVT(dAtA []byte) (int, error) { 47 | if m == nil { 48 | return 0, nil 49 | } 50 | i := len(dAtA) 51 | _ = i 52 | var l int 53 | _ = l 54 | if m.unknownFields != nil { 55 | i -= len(m.unknownFields) 56 | copy(dAtA[i:], m.unknownFields) 57 | } 58 | return len(dAtA) - i, nil 59 | } 60 | 61 | func encodeVarint(dAtA []byte, offset int, v uint64) int { 62 | offset -= sov(v) 63 | base := offset 64 | for v >= 1<<7 { 65 | dAtA[offset] = uint8(v&0x7f | 0x80) 66 | v >>= 7 67 | offset++ 68 | } 69 | dAtA[offset] = uint8(v) 70 | return base 71 | } 72 | func (m *Empty) SizeVT() (n int) { 73 | if m == nil { 74 | return 0 75 | } 76 | var l int 77 | _ = l 78 | if m.unknownFields != nil { 79 | n += len(m.unknownFields) 80 | } 81 | return n 82 | } 83 | 84 | func sov(x uint64) (n int) { 85 | return (bits.Len64(x|1) + 6) / 7 86 | } 87 | func soz(x uint64) (n int) { 88 | return sov(uint64((x << 1) ^ uint64((int64(x) >> 63)))) 89 | } 90 | func (m *Empty) UnmarshalVT(dAtA []byte) error { 91 | l := len(dAtA) 92 | iNdEx := 0 93 | for iNdEx < l { 94 | preIndex := iNdEx 95 | var wire uint64 96 | for shift := uint(0); ; shift += 7 { 97 | if shift >= 64 { 98 | return ErrIntOverflow 99 | } 100 | if iNdEx >= l { 101 | return io.ErrUnexpectedEOF 102 | } 103 | b := dAtA[iNdEx] 104 | iNdEx++ 105 | wire |= uint64(b&0x7F) << shift 106 | if b < 0x80 { 107 | break 108 | } 109 | } 110 | fieldNum := int32(wire >> 3) 111 | wireType := int(wire & 0x7) 112 | if wireType == 4 { 113 | return fmt.Errorf("proto: Empty: wiretype end group for non-group") 114 | } 115 | if fieldNum <= 0 { 116 | return fmt.Errorf("proto: Empty: illegal tag %d (wire type %d)", fieldNum, wire) 117 | } 118 | switch fieldNum { 119 | default: 120 | iNdEx = preIndex 121 | skippy, err := skip(dAtA[iNdEx:]) 122 | if err != nil { 123 | return err 124 | } 125 | if (skippy < 0) || (iNdEx+skippy) < 0 { 126 | return ErrInvalidLength 127 | } 128 | if (iNdEx + skippy) > l { 129 | return io.ErrUnexpectedEOF 130 | } 131 | m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) 132 | iNdEx += skippy 133 | } 134 | } 135 | 136 | if iNdEx > l { 137 | return io.ErrUnexpectedEOF 138 | } 139 | return nil 140 | } 141 | func skip(dAtA []byte) (n int, err error) { 142 | l := len(dAtA) 143 | iNdEx := 0 144 | depth := 0 145 | for iNdEx < l { 146 | var wire uint64 147 | for shift := uint(0); ; shift += 7 { 148 | if shift >= 64 { 149 | return 0, ErrIntOverflow 150 | } 151 | if iNdEx >= l { 152 | return 0, io.ErrUnexpectedEOF 153 | } 154 | b := dAtA[iNdEx] 155 | iNdEx++ 156 | wire |= (uint64(b) & 0x7F) << shift 157 | if b < 0x80 { 158 | break 159 | } 160 | } 161 | wireType := int(wire & 0x7) 162 | switch wireType { 163 | case 0: 164 | for shift := uint(0); ; shift += 7 { 165 | if shift >= 64 { 166 | return 0, ErrIntOverflow 167 | } 168 | if iNdEx >= l { 169 | return 0, io.ErrUnexpectedEOF 170 | } 171 | iNdEx++ 172 | if dAtA[iNdEx-1] < 0x80 { 173 | break 174 | } 175 | } 176 | case 1: 177 | iNdEx += 8 178 | case 2: 179 | var length int 180 | for shift := uint(0); ; shift += 7 { 181 | if shift >= 64 { 182 | return 0, ErrIntOverflow 183 | } 184 | if iNdEx >= l { 185 | return 0, io.ErrUnexpectedEOF 186 | } 187 | b := dAtA[iNdEx] 188 | iNdEx++ 189 | length |= (int(b) & 0x7F) << shift 190 | if b < 0x80 { 191 | break 192 | } 193 | } 194 | if length < 0 { 195 | return 0, ErrInvalidLength 196 | } 197 | iNdEx += length 198 | case 3: 199 | depth++ 200 | case 4: 201 | if depth == 0 { 202 | return 0, ErrUnexpectedEndOfGroup 203 | } 204 | depth-- 205 | case 5: 206 | iNdEx += 4 207 | default: 208 | return 0, fmt.Errorf("proto: illegal wireType %d", wireType) 209 | } 210 | if iNdEx < 0 { 211 | return 0, ErrInvalidLength 212 | } 213 | if depth == 0 { 214 | return iNdEx, nil 215 | } 216 | } 217 | return 0, io.ErrUnexpectedEOF 218 | } 219 | 220 | var ( 221 | ErrInvalidLength = fmt.Errorf("proto: negative length found during unmarshaling") 222 | ErrIntOverflow = fmt.Errorf("proto: integer overflow") 223 | ErrUnexpectedEndOfGroup = fmt.Errorf("proto: unexpected end of group") 224 | ) 225 | --------------------------------------------------------------------------------