Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Open and LoadWithCompiled to Host methods #51

Closed
wants to merge 2 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 140 additions & 1 deletion gen/host.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,145 @@ func genHost(g *protogen.GeneratedFile, f *fileInfo, service *serviceInfo) {
service.GoName,
))

// Open and LoadWithCompiled Functions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Ice3man543 , it's seem to applicable approach, but for common functionality Load can be combination of the Open and LoadWithCompile

Also cache option recommenced by @codefromthecrypt can be validated to choose this approach or compilation cache.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tried the cache approach and it's working fine. Closing!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recently came to more or less same issue, could you please share some links or docs on what is "cache approach" in context of go-plugin that you refer to?

g.P(fmt.Sprintf("func (p *%s) Open(ctx %s, pluginPath string %s) (%s, %s, error) {",
pluginName,
g.QualifiedGoIdent(contextPackage.Ident("Context")),
hostFunctionsArg,
g.QualifiedGoIdent(wazeroPackage.Ident("Runtime")),
g.QualifiedGoIdent(wazeroPackage.Ident("CompiledModule")),
))

g.P(fmt.Sprintf(`b, err := %s(pluginPath)
if err != nil {
return nil, nil, err
}

// Create a new runtime so that multiple modules will not conflict
r, err := p.newRuntime(ctx)
if err != nil {
return nil, nil, err
}
%s

// Compile the WebAssembly module using the default configuration.
code, err := r.CompileModule(ctx, b)
if err != nil {
return nil, nil, err
}

// InstantiateModule runs the "_start" function, WASI's "main".
module, err := r.InstantiateModule(ctx, code, p.moduleConfig)
if err != nil {
// Note: Most compilers do not exit the module after running "_start",
// unless there was an Error. This allows you to call exported functions.
if exitErr, ok := err.(*%s); ok && exitErr.ExitCode() != 0 {
return nil, nil, %s("unexpected exit_code: %%d", exitErr.ExitCode())
} else if !ok {
return nil, nil, err
}
}
defer module.Close(ctx)


// Compare API versions with the loading plugin
apiVersion := module.ExportedFunction("%s_api_version")
if apiVersion == nil {
return nil, nil, %s("%s_api_version is not exported")
}
results, err := apiVersion.Call(ctx)
if err != nil {
return nil, nil, err
} else if len(results) != 1 {
return nil, nil, %s("invalid %s_api_version signature")
}
if results[0] != %sAPIVersion {
return nil, nil, fmt.Errorf("API version mismatch, host: %%d, plugin: %%d", %sAPIVersion, results[0])
}
`,
g.QualifiedGoIdent(osPackage.Ident("ReadFile")),
exportHostFunctions,
g.QualifiedGoIdent(wazeroSysPackage.Ident("ExitError")),
g.QualifiedGoIdent(fmtPackage.Ident("Errorf")),
toSnakeCase(service.GoName),
g.QualifiedGoIdent(errorsPackage.Ident("New")),
toSnakeCase(service.GoName),
g.QualifiedGoIdent(errorsPackage.Ident("New")),
toSnakeCase(service.GoName),
pluginName, pluginName,
))
errorsNew := g.QualifiedGoIdent(errorsPackage.Ident("New"))
for _, method := range service.Methods {
varName := strings.ToLower(method.GoName[:1] + method.GoName[1:])
funcName := toSnakeCase(service.GoName + method.GoName)
g.P(varName, `:= module.ExportedFunction("`, funcName, `")`)
g.P("if ", varName, `== nil { return nil, nil, `, errorsNew, `("`, funcName, ` is not exported")}`)
}
g.P("return r, code, nil")
g.P("}")
g.P()

g.P(fmt.Sprintf("func (p *%s) LoadWithCompiled(ctx %s, r %s, code %s %s) (%s, error) {",
pluginName,
g.QualifiedGoIdent(contextPackage.Ident("Context")),
g.QualifiedGoIdent(wazeroPackage.Ident("Runtime")),
g.QualifiedGoIdent(wazeroPackage.Ident("CompiledModule")),
hostFunctionsArg,
structName,
))

g.P(fmt.Sprintf(`// InstantiateModule runs the "_start" function, WASI's "main".
module, err := r.InstantiateModule(ctx, code, p.moduleConfig)
if err != nil {
// Note: Most compilers do not exit the module after running "_start",
// unless there was an Error. This allows you to call exported functions.
if exitErr, ok := err.(*%s); ok && exitErr.ExitCode() != 0 {
return nil, %s("unexpected exit_code: %%d", exitErr.ExitCode())
} else if !ok {
return nil, err
}
}
`,
g.QualifiedGoIdent(wazeroSysPackage.Ident("ExitError")),
g.QualifiedGoIdent(fmtPackage.Ident("Errorf")),
))

errorsNew = g.QualifiedGoIdent(errorsPackage.Ident("New"))
for _, method := range service.Methods {
varName := strings.ToLower(method.GoName[:1] + method.GoName[1:])
funcName := toSnakeCase(service.GoName + method.GoName)
g.P(varName, `:= module.ExportedFunction("`, funcName, `")`)
g.P("if ", varName, `== nil { return nil, `, errorsNew, `("`, funcName, ` is not exported")}`)
}

g.P(fmt.Sprintf(`
malloc := module.ExportedFunction("malloc")
if malloc == nil {
return nil, %s("malloc is not exported")
}

free := module.ExportedFunction("free")
if free == nil {
return nil, %s("free is not exported")
}`,
errorsNew, errorsNew))

g.P("return &", structName, "Plugin {",
`
runtime: r,
module: module,
malloc: malloc,
free: free,`)

for _, method := range service.Methods {
varName := strings.ToLower(method.GoName[:1] + method.GoName[1:])
g.P(varName, ": ", varName, ",")
}
g.P("}, nil")
g.P("}")
g.P()

// Load Function
g.P(fmt.Sprintf("func (p *%s) Load(ctx %s, pluginPath string %s) (%s, error) {",
pluginName,
g.QualifiedGoIdent(contextPackage.Ident("Context")),
Expand Down Expand Up @@ -247,7 +386,7 @@ func genHost(g *protogen.GeneratedFile, f *fileInfo, service *serviceInfo) {
pluginName, pluginName,
))

errorsNew := g.QualifiedGoIdent(errorsPackage.Ident("New"))
errorsNew = g.QualifiedGoIdent(errorsPackage.Ident("New"))
for _, method := range service.Methods {
varName := strings.ToLower(method.GoName[:1] + method.GoName[1:])
funcName := toSnakeCase(service.GoName + method.GoName)
Expand Down