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

Truly ignore //export when //go:wasmexport is used #4500

Merged
merged 2 commits into from
Oct 5, 2024
Merged
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
227 changes: 115 additions & 112 deletions compiler/symbol.go
Original file line number Diff line number Diff line change
Expand Up @@ -273,129 +273,132 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
if syntax == nil {
return
}

// Read all pragmas of this function.
var pragmas []*ast.Comment
hasWasmExport := false
if decl, ok := syntax.(*ast.FuncDecl); ok && decl.Doc != nil {
for _, comment := range decl.Doc.List {
text := comment.Text
if strings.HasPrefix(text, "//export ") {
// Rewrite '//export' to '//go:export' for compatibility with
// gc.
text = "//go:" + text[2:]
if strings.HasPrefix(text, "//go:") || strings.HasPrefix(text, "//export ") {
pragmas = append(pragmas, comment)
if strings.HasPrefix(comment.Text, "//go:wasmexport ") {
hasWasmExport = true
}
}
if !strings.HasPrefix(text, "//go:") {
}
}

// Parse each pragma.
for _, comment := range pragmas {
parts := strings.Fields(comment.Text)
switch parts[0] {
case "//export", "//go:export":
if len(parts) != 2 {
continue
}
if hasWasmExport {
// //go:wasmexport overrides //export.
continue
}
parts := strings.Fields(text)
switch parts[0] {
case "//go:export":
if len(parts) != 2 {
continue
}

info.linkName = parts[1]
info.wasmName = info.linkName
info.exported = true
case "//go:interrupt":
if hasUnsafeImport(f.Pkg.Pkg) {
info.interrupt = true
}
case "//go:wasm-module":
// Alternative comment for setting the import module.
// This is deprecated, use //go:wasmimport instead.
if len(parts) != 2 {
continue
}
info.wasmModule = parts[1]
case "//go:wasmimport":
// Import a WebAssembly function, for example a WASI function.
// Original proposal: https://github.com/golang/go/issues/38248
// Allow globally: https://github.com/golang/go/issues/59149
if len(parts) != 3 {
continue
}
if f.Blocks != nil {
// Defined functions cannot be exported.
c.addError(f.Pos(), "can only use //go:wasmimport on declarations")
continue
}
c.checkWasmImportExport(f, comment.Text)
info.exported = true
info.wasmModule = parts[1]
info.wasmName = parts[2]
case "//go:wasmexport":
if f.Blocks == nil {
c.addError(f.Pos(), "can only use //go:wasmexport on definitions")
continue
}
if len(parts) != 2 {
c.addError(f.Pos(), fmt.Sprintf("expected one parameter to //go:wasmimport, not %d", len(parts)-1))
continue
}
name := parts[1]
if name == "_start" || name == "_initialize" {
c.addError(f.Pos(), fmt.Sprintf("//go:wasmexport does not allow %#v", name))
continue
}
if c.BuildMode != "c-shared" && f.RelString(nil) == "main.main" {
c.addError(f.Pos(), fmt.Sprintf("//go:wasmexport does not allow main.main to be exported with -buildmode=%s", c.BuildMode))
continue
}
if c.archFamily() != "wasm32" {
c.addError(f.Pos(), "//go:wasmexport is only supported on wasm")
}
c.checkWasmImportExport(f, comment.Text)
info.wasmExport = name
info.wasmExportPos = comment.Slash
case "//go:inline":
info.inline = inlineHint
case "//go:noinline":
info.linkName = parts[1]
info.wasmName = info.linkName
info.exported = true
case "//go:interrupt":
if hasUnsafeImport(f.Pkg.Pkg) {
info.interrupt = true
}
case "//go:wasm-module":
// Alternative comment for setting the import module.
// This is deprecated, use //go:wasmimport instead.
if len(parts) != 2 {
continue
}
info.wasmModule = parts[1]
case "//go:wasmimport":
// Import a WebAssembly function, for example a WASI function.
// Original proposal: https://github.com/golang/go/issues/38248
// Allow globally: https://github.com/golang/go/issues/59149
if len(parts) != 3 {
continue
}
if f.Blocks != nil {
// Defined functions cannot be exported.
c.addError(f.Pos(), "can only use //go:wasmimport on declarations")
continue
}
c.checkWasmImportExport(f, comment.Text)
info.exported = true
info.wasmModule = parts[1]
info.wasmName = parts[2]
case "//go:wasmexport":
if f.Blocks == nil {
c.addError(f.Pos(), "can only use //go:wasmexport on definitions")
continue
}
if len(parts) != 2 {
c.addError(f.Pos(), fmt.Sprintf("expected one parameter to //go:wasmimport, not %d", len(parts)-1))
continue
}
name := parts[1]
if name == "_start" || name == "_initialize" {
c.addError(f.Pos(), fmt.Sprintf("//go:wasmexport does not allow %#v", name))
continue
}
if c.BuildMode != "c-shared" && f.RelString(nil) == "main.main" {
c.addError(f.Pos(), fmt.Sprintf("//go:wasmexport does not allow main.main to be exported with -buildmode=%s", c.BuildMode))
continue
}
if c.archFamily() != "wasm32" {
c.addError(f.Pos(), "//go:wasmexport is only supported on wasm")
}
c.checkWasmImportExport(f, comment.Text)
info.wasmExport = name
info.wasmExportPos = comment.Slash
case "//go:inline":
info.inline = inlineHint
case "//go:noinline":
info.inline = inlineNone
case "//go:linkname":
if len(parts) != 3 || parts[1] != f.Name() {
continue
}
// Only enable go:linkname when the package imports "unsafe".
// This is a slightly looser requirement than what gc uses: gc
// requires the file to import "unsafe", not the package as a
// whole.
if hasUnsafeImport(f.Pkg.Pkg) {
info.linkName = parts[2]
}
case "//go:section":
// Only enable go:section when the package imports "unsafe".
// go:section also implies go:noinline since inlining could
// move the code to a different section than that requested.
if len(parts) == 2 && hasUnsafeImport(f.Pkg.Pkg) {
info.section = parts[1]
info.inline = inlineNone
case "//go:linkname":
if len(parts) != 3 || parts[1] != f.Name() {
continue
}
// Only enable go:linkname when the package imports "unsafe".
// This is a slightly looser requirement than what gc uses: gc
// requires the file to import "unsafe", not the package as a
// whole.
if hasUnsafeImport(f.Pkg.Pkg) {
info.linkName = parts[2]
}
case "//go:section":
// Only enable go:section when the package imports "unsafe".
// go:section also implies go:noinline since inlining could
// move the code to a different section than that requested.
if len(parts) == 2 && hasUnsafeImport(f.Pkg.Pkg) {
info.section = parts[1]
info.inline = inlineNone
}
case "//go:nobounds":
// Skip bounds checking in this function. Useful for some
// runtime functions.
// This is somewhat dangerous and thus only imported in packages
// that import unsafe.
if hasUnsafeImport(f.Pkg.Pkg) {
info.nobounds = true
}
case "//go:variadic":
// The //go:variadic pragma is emitted by the CGo preprocessing
// pass for C variadic functions. This includes both explicit
// (with ...) and implicit (no parameters in signature)
// functions.
if strings.HasPrefix(f.Name(), "C.") {
// This prefix cannot naturally be created, it must have
// been created as a result of CGo preprocessing.
info.variadic = true
}
}
case "//go:nobounds":
// Skip bounds checking in this function. Useful for some
// runtime functions.
// This is somewhat dangerous and thus only imported in packages
// that import unsafe.
if hasUnsafeImport(f.Pkg.Pkg) {
info.nobounds = true
}
case "//go:variadic":
// The //go:variadic pragma is emitted by the CGo preprocessing
// pass for C variadic functions. This includes both explicit
// (with ...) and implicit (no parameters in signature)
// functions.
if strings.HasPrefix(f.Name(), "C.") {
// This prefix cannot naturally be created, it must have
// been created as a result of CGo preprocessing.
info.variadic = true
}
}
}

// If both //go:wasmexport and //go:export or //export are declared,
// only honor go:wasmexport.
if info.wasmExport != "" {
// TODO: log warning?
info.exported = false
}
}

// Check whether this function can be used in //go:wasmimport or
Expand Down
Loading