Skip to content

Commit d1b7238

Browse files
authored
Truly ignore //export when //go:wasmexport is used (#4500)
1 parent 453a1d3 commit d1b7238

File tree

1 file changed

+115
-112
lines changed

1 file changed

+115
-112
lines changed

compiler/symbol.go

Lines changed: 115 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -273,129 +273,132 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
273273
if syntax == nil {
274274
return
275275
}
276+
277+
// Read all pragmas of this function.
278+
var pragmas []*ast.Comment
279+
hasWasmExport := false
276280
if decl, ok := syntax.(*ast.FuncDecl); ok && decl.Doc != nil {
277281
for _, comment := range decl.Doc.List {
278282
text := comment.Text
279-
if strings.HasPrefix(text, "//export ") {
280-
// Rewrite '//export' to '//go:export' for compatibility with
281-
// gc.
282-
text = "//go:" + text[2:]
283+
if strings.HasPrefix(text, "//go:") || strings.HasPrefix(text, "//export ") {
284+
pragmas = append(pragmas, comment)
285+
if strings.HasPrefix(comment.Text, "//go:wasmexport ") {
286+
hasWasmExport = true
287+
}
283288
}
284-
if !strings.HasPrefix(text, "//go:") {
289+
}
290+
}
291+
292+
// Parse each pragma.
293+
for _, comment := range pragmas {
294+
parts := strings.Fields(comment.Text)
295+
switch parts[0] {
296+
case "//export", "//go:export":
297+
if len(parts) != 2 {
298+
continue
299+
}
300+
if hasWasmExport {
301+
// //go:wasmexport overrides //export.
285302
continue
286303
}
287-
parts := strings.Fields(text)
288-
switch parts[0] {
289-
case "//go:export":
290-
if len(parts) != 2 {
291-
continue
292-
}
293304

294-
info.linkName = parts[1]
295-
info.wasmName = info.linkName
296-
info.exported = true
297-
case "//go:interrupt":
298-
if hasUnsafeImport(f.Pkg.Pkg) {
299-
info.interrupt = true
300-
}
301-
case "//go:wasm-module":
302-
// Alternative comment for setting the import module.
303-
// This is deprecated, use //go:wasmimport instead.
304-
if len(parts) != 2 {
305-
continue
306-
}
307-
info.wasmModule = parts[1]
308-
case "//go:wasmimport":
309-
// Import a WebAssembly function, for example a WASI function.
310-
// Original proposal: https://github.com/golang/go/issues/38248
311-
// Allow globally: https://github.com/golang/go/issues/59149
312-
if len(parts) != 3 {
313-
continue
314-
}
315-
if f.Blocks != nil {
316-
// Defined functions cannot be exported.
317-
c.addError(f.Pos(), "can only use //go:wasmimport on declarations")
318-
continue
319-
}
320-
c.checkWasmImportExport(f, comment.Text)
321-
info.exported = true
322-
info.wasmModule = parts[1]
323-
info.wasmName = parts[2]
324-
case "//go:wasmexport":
325-
if f.Blocks == nil {
326-
c.addError(f.Pos(), "can only use //go:wasmexport on definitions")
327-
continue
328-
}
329-
if len(parts) != 2 {
330-
c.addError(f.Pos(), fmt.Sprintf("expected one parameter to //go:wasmimport, not %d", len(parts)-1))
331-
continue
332-
}
333-
name := parts[1]
334-
if name == "_start" || name == "_initialize" {
335-
c.addError(f.Pos(), fmt.Sprintf("//go:wasmexport does not allow %#v", name))
336-
continue
337-
}
338-
if c.BuildMode != "c-shared" && f.RelString(nil) == "main.main" {
339-
c.addError(f.Pos(), fmt.Sprintf("//go:wasmexport does not allow main.main to be exported with -buildmode=%s", c.BuildMode))
340-
continue
341-
}
342-
if c.archFamily() != "wasm32" {
343-
c.addError(f.Pos(), "//go:wasmexport is only supported on wasm")
344-
}
345-
c.checkWasmImportExport(f, comment.Text)
346-
info.wasmExport = name
347-
info.wasmExportPos = comment.Slash
348-
case "//go:inline":
349-
info.inline = inlineHint
350-
case "//go:noinline":
305+
info.linkName = parts[1]
306+
info.wasmName = info.linkName
307+
info.exported = true
308+
case "//go:interrupt":
309+
if hasUnsafeImport(f.Pkg.Pkg) {
310+
info.interrupt = true
311+
}
312+
case "//go:wasm-module":
313+
// Alternative comment for setting the import module.
314+
// This is deprecated, use //go:wasmimport instead.
315+
if len(parts) != 2 {
316+
continue
317+
}
318+
info.wasmModule = parts[1]
319+
case "//go:wasmimport":
320+
// Import a WebAssembly function, for example a WASI function.
321+
// Original proposal: https://github.com/golang/go/issues/38248
322+
// Allow globally: https://github.com/golang/go/issues/59149
323+
if len(parts) != 3 {
324+
continue
325+
}
326+
if f.Blocks != nil {
327+
// Defined functions cannot be exported.
328+
c.addError(f.Pos(), "can only use //go:wasmimport on declarations")
329+
continue
330+
}
331+
c.checkWasmImportExport(f, comment.Text)
332+
info.exported = true
333+
info.wasmModule = parts[1]
334+
info.wasmName = parts[2]
335+
case "//go:wasmexport":
336+
if f.Blocks == nil {
337+
c.addError(f.Pos(), "can only use //go:wasmexport on definitions")
338+
continue
339+
}
340+
if len(parts) != 2 {
341+
c.addError(f.Pos(), fmt.Sprintf("expected one parameter to //go:wasmimport, not %d", len(parts)-1))
342+
continue
343+
}
344+
name := parts[1]
345+
if name == "_start" || name == "_initialize" {
346+
c.addError(f.Pos(), fmt.Sprintf("//go:wasmexport does not allow %#v", name))
347+
continue
348+
}
349+
if c.BuildMode != "c-shared" && f.RelString(nil) == "main.main" {
350+
c.addError(f.Pos(), fmt.Sprintf("//go:wasmexport does not allow main.main to be exported with -buildmode=%s", c.BuildMode))
351+
continue
352+
}
353+
if c.archFamily() != "wasm32" {
354+
c.addError(f.Pos(), "//go:wasmexport is only supported on wasm")
355+
}
356+
c.checkWasmImportExport(f, comment.Text)
357+
info.wasmExport = name
358+
info.wasmExportPos = comment.Slash
359+
case "//go:inline":
360+
info.inline = inlineHint
361+
case "//go:noinline":
362+
info.inline = inlineNone
363+
case "//go:linkname":
364+
if len(parts) != 3 || parts[1] != f.Name() {
365+
continue
366+
}
367+
// Only enable go:linkname when the package imports "unsafe".
368+
// This is a slightly looser requirement than what gc uses: gc
369+
// requires the file to import "unsafe", not the package as a
370+
// whole.
371+
if hasUnsafeImport(f.Pkg.Pkg) {
372+
info.linkName = parts[2]
373+
}
374+
case "//go:section":
375+
// Only enable go:section when the package imports "unsafe".
376+
// go:section also implies go:noinline since inlining could
377+
// move the code to a different section than that requested.
378+
if len(parts) == 2 && hasUnsafeImport(f.Pkg.Pkg) {
379+
info.section = parts[1]
351380
info.inline = inlineNone
352-
case "//go:linkname":
353-
if len(parts) != 3 || parts[1] != f.Name() {
354-
continue
355-
}
356-
// Only enable go:linkname when the package imports "unsafe".
357-
// This is a slightly looser requirement than what gc uses: gc
358-
// requires the file to import "unsafe", not the package as a
359-
// whole.
360-
if hasUnsafeImport(f.Pkg.Pkg) {
361-
info.linkName = parts[2]
362-
}
363-
case "//go:section":
364-
// Only enable go:section when the package imports "unsafe".
365-
// go:section also implies go:noinline since inlining could
366-
// move the code to a different section than that requested.
367-
if len(parts) == 2 && hasUnsafeImport(f.Pkg.Pkg) {
368-
info.section = parts[1]
369-
info.inline = inlineNone
370-
}
371-
case "//go:nobounds":
372-
// Skip bounds checking in this function. Useful for some
373-
// runtime functions.
374-
// This is somewhat dangerous and thus only imported in packages
375-
// that import unsafe.
376-
if hasUnsafeImport(f.Pkg.Pkg) {
377-
info.nobounds = true
378-
}
379-
case "//go:variadic":
380-
// The //go:variadic pragma is emitted by the CGo preprocessing
381-
// pass for C variadic functions. This includes both explicit
382-
// (with ...) and implicit (no parameters in signature)
383-
// functions.
384-
if strings.HasPrefix(f.Name(), "C.") {
385-
// This prefix cannot naturally be created, it must have
386-
// been created as a result of CGo preprocessing.
387-
info.variadic = true
388-
}
381+
}
382+
case "//go:nobounds":
383+
// Skip bounds checking in this function. Useful for some
384+
// runtime functions.
385+
// This is somewhat dangerous and thus only imported in packages
386+
// that import unsafe.
387+
if hasUnsafeImport(f.Pkg.Pkg) {
388+
info.nobounds = true
389+
}
390+
case "//go:variadic":
391+
// The //go:variadic pragma is emitted by the CGo preprocessing
392+
// pass for C variadic functions. This includes both explicit
393+
// (with ...) and implicit (no parameters in signature)
394+
// functions.
395+
if strings.HasPrefix(f.Name(), "C.") {
396+
// This prefix cannot naturally be created, it must have
397+
// been created as a result of CGo preprocessing.
398+
info.variadic = true
389399
}
390400
}
391401
}
392-
393-
// If both //go:wasmexport and //go:export or //export are declared,
394-
// only honor go:wasmexport.
395-
if info.wasmExport != "" {
396-
// TODO: log warning?
397-
info.exported = false
398-
}
399402
}
400403

401404
// Check whether this function can be used in //go:wasmimport or

0 commit comments

Comments
 (0)