diff options
Diffstat (limited to 'src/cmd/compile/internal/noder/lex.go')
-rw-r--r-- | src/cmd/compile/internal/noder/lex.go | 193 |
1 files changed, 193 insertions, 0 deletions
diff --git a/src/cmd/compile/internal/noder/lex.go b/src/cmd/compile/internal/noder/lex.go new file mode 100644 index 0000000..66a56a5 --- /dev/null +++ b/src/cmd/compile/internal/noder/lex.go @@ -0,0 +1,193 @@ +// 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 +} |