// Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package noder import ( "fmt" "internal/buildcfg" "strings" "cmd/compile/internal/ir" "cmd/compile/internal/syntax" ) func isSpace(c rune) bool { return c == ' ' || c == '\t' || c == '\n' || c == '\r' } func isQuoted(s string) bool { return len(s) >= 2 && s[0] == '"' && s[len(s)-1] == '"' } const ( funcPragmas = ir.Nointerface | ir.Noescape | ir.Norace | ir.Nosplit | ir.Noinline | ir.NoCheckPtr | ir.RegisterParams | // TODO(register args) remove after register abi is working ir.CgoUnsafeArgs | ir.UintptrEscapes | ir.Systemstack | ir.Nowritebarrier | ir.Nowritebarrierrec | ir.Yeswritebarrierrec typePragmas = ir.NotInHeap ) func pragmaFlag(verb string) ir.PragmaFlag { switch verb { case "go:build": return ir.GoBuildPragma case "go:nointerface": if buildcfg.Experiment.FieldTrack { return ir.Nointerface } case "go:noescape": return ir.Noescape case "go:norace": return ir.Norace case "go:nosplit": return ir.Nosplit | ir.NoCheckPtr // implies NoCheckPtr (see #34972) case "go:noinline": return ir.Noinline case "go:nocheckptr": return ir.NoCheckPtr case "go:systemstack": return ir.Systemstack case "go:nowritebarrier": return ir.Nowritebarrier case "go:nowritebarrierrec": return ir.Nowritebarrierrec | ir.Nowritebarrier // implies Nowritebarrier case "go:yeswritebarrierrec": return ir.Yeswritebarrierrec case "go:cgo_unsafe_args": return ir.CgoUnsafeArgs | ir.NoCheckPtr // implies NoCheckPtr (see #34968) case "go:uintptrescapes": // For the next function declared in the file // any uintptr arguments may be pointer values // converted to uintptr. This directive // ensures that the referenced allocated // object, if any, is retained and not moved // until the call completes, even though from // the types alone it would appear that the // object is no longer needed during the // call. The conversion to uintptr must appear // in the argument list. // Used in syscall/dll_windows.go. return ir.UintptrEscapes case "go:registerparams": // TODO(register args) remove after register abi is working return ir.RegisterParams case "go:notinheap": return ir.NotInHeap } return 0 } // pragcgo is called concurrently if files are parsed concurrently. func (p *noder) pragcgo(pos syntax.Pos, text string) { f := pragmaFields(text) verb := strings.TrimPrefix(f[0], "go:") f[0] = verb switch verb { case "cgo_export_static", "cgo_export_dynamic": switch { case len(f) == 2 && !isQuoted(f[1]): case len(f) == 3 && !isQuoted(f[1]) && !isQuoted(f[2]): default: p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf(`usage: //go:%s local [remote]`, verb)}) return } case "cgo_import_dynamic": switch { case len(f) == 2 && !isQuoted(f[1]): case len(f) == 3 && !isQuoted(f[1]) && !isQuoted(f[2]): case len(f) == 4 && !isQuoted(f[1]) && !isQuoted(f[2]) && isQuoted(f[3]): f[3] = strings.Trim(f[3], `"`) if buildcfg.GOOS == "aix" && f[3] != "" { // On Aix, library pattern must be "lib.a/object.o" // or "lib.a/libname.so.X" n := strings.Split(f[3], "/") if len(n) != 2 || !strings.HasSuffix(n[0], ".a") || (!strings.HasSuffix(n[1], ".o") && !strings.Contains(n[1], ".so.")) { p.error(syntax.Error{Pos: pos, Msg: `usage: //go:cgo_import_dynamic local [remote ["lib.a/object.o"]]`}) return } } default: p.error(syntax.Error{Pos: pos, Msg: `usage: //go:cgo_import_dynamic local [remote ["library"]]`}) return } case "cgo_import_static": switch { case len(f) == 2 && !isQuoted(f[1]): default: p.error(syntax.Error{Pos: pos, Msg: `usage: //go:cgo_import_static local`}) return } case "cgo_dynamic_linker": switch { case len(f) == 2 && isQuoted(f[1]): f[1] = strings.Trim(f[1], `"`) default: p.error(syntax.Error{Pos: pos, Msg: `usage: //go:cgo_dynamic_linker "path"`}) return } case "cgo_ldflag": switch { case len(f) == 2 && isQuoted(f[1]): f[1] = strings.Trim(f[1], `"`) default: p.error(syntax.Error{Pos: pos, Msg: `usage: //go:cgo_ldflag "arg"`}) return } default: return } p.pragcgobuf = append(p.pragcgobuf, f) } // pragmaFields is similar to strings.FieldsFunc(s, isSpace) // but does not split when inside double quoted regions and always // splits before the start and after the end of a double quoted region. // pragmaFields does not recognize escaped quotes. If a quote in s is not // closed the part after the opening quote will not be returned as a field. func pragmaFields(s string) []string { var a []string inQuote := false fieldStart := -1 // Set to -1 when looking for start of field. for i, c := range s { switch { case c == '"': if inQuote { inQuote = false a = append(a, s[fieldStart:i+1]) fieldStart = -1 } else { inQuote = true if fieldStart >= 0 { a = append(a, s[fieldStart:i]) } fieldStart = i } case !inQuote && isSpace(c): if fieldStart >= 0 { a = append(a, s[fieldStart:i]) fieldStart = -1 } default: if fieldStart == -1 { fieldStart = i } } } if !inQuote && fieldStart >= 0 { // Last field might end at the end of the string. a = append(a, s[fieldStart:]) } return a }