github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/compile/noder/lex.go (about) 1 // Copyright 2009 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package noder 6 7 import ( 8 "fmt" 9 "strings" 10 11 "github.com/go-asm/go/buildcfg" 12 13 "github.com/go-asm/go/cmd/compile/ir" 14 "github.com/go-asm/go/cmd/compile/syntax" 15 ) 16 17 func isSpace(c rune) bool { 18 return c == ' ' || c == '\t' || c == '\n' || c == '\r' 19 } 20 21 func isQuoted(s string) bool { 22 return len(s) >= 2 && s[0] == '"' && s[len(s)-1] == '"' 23 } 24 25 const ( 26 funcPragmas = ir.Nointerface | 27 ir.Noescape | 28 ir.Norace | 29 ir.Nosplit | 30 ir.Noinline | 31 ir.NoCheckPtr | 32 ir.RegisterParams | // TODO(register args) remove after register abi is working 33 ir.CgoUnsafeArgs | 34 ir.UintptrKeepAlive | 35 ir.UintptrEscapes | 36 ir.Systemstack | 37 ir.Nowritebarrier | 38 ir.Nowritebarrierrec | 39 ir.Yeswritebarrierrec 40 ) 41 42 func pragmaFlag(verb string) ir.PragmaFlag { 43 switch verb { 44 case "go:build": 45 return ir.GoBuildPragma 46 case "go:nointerface": 47 if buildcfg.Experiment.FieldTrack { 48 return ir.Nointerface 49 } 50 case "go:noescape": 51 return ir.Noescape 52 case "go:norace": 53 return ir.Norace 54 case "go:nosplit": 55 return ir.Nosplit | ir.NoCheckPtr // implies NoCheckPtr (see #34972) 56 case "go:noinline": 57 return ir.Noinline 58 case "go:nocheckptr": 59 return ir.NoCheckPtr 60 case "go:systemstack": 61 return ir.Systemstack 62 case "go:nowritebarrier": 63 return ir.Nowritebarrier 64 case "go:nowritebarrierrec": 65 return ir.Nowritebarrierrec | ir.Nowritebarrier // implies Nowritebarrier 66 case "go:yeswritebarrierrec": 67 return ir.Yeswritebarrierrec 68 case "go:cgo_unsafe_args": 69 return ir.CgoUnsafeArgs | ir.NoCheckPtr // implies NoCheckPtr (see #34968) 70 case "go:uintptrkeepalive": 71 return ir.UintptrKeepAlive 72 case "go:uintptrescapes": 73 // This directive extends //go:uintptrkeepalive by forcing 74 // uintptr arguments to escape to the heap, which makes stack 75 // growth safe. 76 return ir.UintptrEscapes | ir.UintptrKeepAlive // implies UintptrKeepAlive 77 case "go:registerparams": // TODO(register args) remove after register abi is working 78 return ir.RegisterParams 79 } 80 return 0 81 } 82 83 // pragcgo is called concurrently if files are parsed concurrently. 84 func (p *noder) pragcgo(pos syntax.Pos, text string) { 85 f := pragmaFields(text) 86 87 verb := strings.TrimPrefix(f[0], "go:") 88 f[0] = verb 89 90 switch verb { 91 case "cgo_export_static", "cgo_export_dynamic": 92 switch { 93 case len(f) == 2 && !isQuoted(f[1]): 94 case len(f) == 3 && !isQuoted(f[1]) && !isQuoted(f[2]): 95 default: 96 p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf(`usage: //go:%s local [remote]`, verb)}) 97 return 98 } 99 case "cgo_import_dynamic": 100 switch { 101 case len(f) == 2 && !isQuoted(f[1]): 102 case len(f) == 3 && !isQuoted(f[1]) && !isQuoted(f[2]): 103 case len(f) == 4 && !isQuoted(f[1]) && !isQuoted(f[2]) && isQuoted(f[3]): 104 f[3] = strings.Trim(f[3], `"`) 105 if buildcfg.GOOS == "aix" && f[3] != "" { 106 // On Aix, library pattern must be "lib.a/object.o" 107 // or "lib.a/libname.so.X" 108 n := strings.Split(f[3], "/") 109 if len(n) != 2 || !strings.HasSuffix(n[0], ".a") || (!strings.HasSuffix(n[1], ".o") && !strings.Contains(n[1], ".so.")) { 110 p.error(syntax.Error{Pos: pos, Msg: `usage: //go:cgo_import_dynamic local [remote ["lib.a/object.o"]]`}) 111 return 112 } 113 } 114 default: 115 p.error(syntax.Error{Pos: pos, Msg: `usage: //go:cgo_import_dynamic local [remote ["library"]]`}) 116 return 117 } 118 case "cgo_import_static": 119 switch { 120 case len(f) == 2 && !isQuoted(f[1]): 121 default: 122 p.error(syntax.Error{Pos: pos, Msg: `usage: //go:cgo_import_static local`}) 123 return 124 } 125 case "cgo_dynamic_linker": 126 switch { 127 case len(f) == 2 && isQuoted(f[1]): 128 f[1] = strings.Trim(f[1], `"`) 129 default: 130 p.error(syntax.Error{Pos: pos, Msg: `usage: //go:cgo_dynamic_linker "path"`}) 131 return 132 } 133 case "cgo_ldflag": 134 switch { 135 case len(f) == 2 && isQuoted(f[1]): 136 f[1] = strings.Trim(f[1], `"`) 137 default: 138 p.error(syntax.Error{Pos: pos, Msg: `usage: //go:cgo_ldflag "arg"`}) 139 return 140 } 141 default: 142 return 143 } 144 p.pragcgobuf = append(p.pragcgobuf, f) 145 } 146 147 // pragmaFields is similar to strings.FieldsFunc(s, isSpace) 148 // but does not split when inside double quoted regions and always 149 // splits before the start and after the end of a double quoted region. 150 // pragmaFields does not recognize escaped quotes. If a quote in s is not 151 // closed the part after the opening quote will not be returned as a field. 152 func pragmaFields(s string) []string { 153 var a []string 154 inQuote := false 155 fieldStart := -1 // Set to -1 when looking for start of field. 156 for i, c := range s { 157 switch { 158 case c == '"': 159 if inQuote { 160 inQuote = false 161 a = append(a, s[fieldStart:i+1]) 162 fieldStart = -1 163 } else { 164 inQuote = true 165 if fieldStart >= 0 { 166 a = append(a, s[fieldStart:i]) 167 } 168 fieldStart = i 169 } 170 case !inQuote && isSpace(c): 171 if fieldStart >= 0 { 172 a = append(a, s[fieldStart:i]) 173 fieldStart = -1 174 } 175 default: 176 if fieldStart == -1 { 177 fieldStart = i 178 } 179 } 180 } 181 if !inQuote && fieldStart >= 0 { // Last field might end at the end of the string. 182 a = append(a, s[fieldStart:]) 183 } 184 return a 185 }