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 gc 6 7 import ( 8 "cmd/compile/internal/syntax" 9 "cmd/internal/obj" 10 "fmt" 11 "strings" 12 ) 13 14 // lexlineno is the line number _after_ the most recently read rune. 15 // In particular, it's advanced (or rewound) as newlines are read (or unread). 16 var lexlineno int32 17 18 // lineno is the line number at the start of the most recently lexed token. 19 var lineno int32 20 21 func isSpace(c rune) bool { 22 return c == ' ' || c == '\t' || c == '\n' || c == '\r' 23 } 24 25 func isQuoted(s string) bool { 26 return len(s) >= 2 && s[0] == '"' && s[len(s)-1] == '"' 27 } 28 29 func plan9quote(s string) string { 30 if s == "" { 31 return "''" 32 } 33 for _, c := range s { 34 if c <= ' ' || c == '\'' { 35 return "'" + strings.Replace(s, "'", "''", -1) + "'" 36 } 37 } 38 return s 39 } 40 41 type Pragma syntax.Pragma 42 43 const ( 44 // Func pragmas. 45 Nointerface Pragma = 1 << iota 46 Noescape // func parameters don't escape 47 Norace // func must not have race detector annotations 48 Nosplit // func should not execute on separate stack 49 Noinline // func should not be inlined 50 CgoUnsafeArgs // treat a pointer to one arg as a pointer to them all 51 UintptrEscapes // pointers converted to uintptr escape 52 53 // Runtime-only func pragmas. 54 // See ../../../../runtime/README.md for detailed descriptions. 55 Systemstack // func must run on system stack 56 Nowritebarrier // emit compiler error instead of write barrier 57 Nowritebarrierrec // error on write barrier in this or recursive callees 58 Yeswritebarrierrec // cancels Nowritebarrierrec in this function and callees 59 60 // Runtime-only type pragmas 61 NotInHeap // values of this type must not be heap allocated 62 ) 63 64 func pragmaValue(verb string) Pragma { 65 switch verb { 66 case "go:nointerface": 67 if obj.Fieldtrack_enabled != 0 { 68 return Nointerface 69 } 70 case "go:noescape": 71 return Noescape 72 case "go:norace": 73 return Norace 74 case "go:nosplit": 75 return Nosplit 76 case "go:noinline": 77 return Noinline 78 case "go:systemstack": 79 if !compiling_runtime { 80 yyerror("//go:systemstack only allowed in runtime") 81 } 82 return Systemstack 83 case "go:nowritebarrier": 84 if !compiling_runtime { 85 yyerror("//go:nowritebarrier only allowed in runtime") 86 } 87 return Nowritebarrier 88 case "go:nowritebarrierrec": 89 if !compiling_runtime { 90 yyerror("//go:nowritebarrierrec only allowed in runtime") 91 } 92 return Nowritebarrierrec | Nowritebarrier // implies Nowritebarrier 93 case "go:yeswritebarrierrec": 94 if !compiling_runtime { 95 yyerror("//go:yeswritebarrierrec only allowed in runtime") 96 } 97 return Yeswritebarrierrec 98 case "go:cgo_unsafe_args": 99 return CgoUnsafeArgs 100 case "go:uintptrescapes": 101 // For the next function declared in the file 102 // any uintptr arguments may be pointer values 103 // converted to uintptr. This directive 104 // ensures that the referenced allocated 105 // object, if any, is retained and not moved 106 // until the call completes, even though from 107 // the types alone it would appear that the 108 // object is no longer needed during the 109 // call. The conversion to uintptr must appear 110 // in the argument list. 111 // Used in syscall/dll_windows.go. 112 return UintptrEscapes 113 case "go:notinheap": 114 return NotInHeap 115 } 116 return 0 117 } 118 119 var internedStrings = map[string]string{} 120 121 func internString(b []byte) string { 122 s, ok := internedStrings[string(b)] // string(b) here doesn't allocate 123 if !ok { 124 s = string(b) 125 internedStrings[s] = s 126 } 127 return s 128 } 129 130 func pragcgo(text string) string { 131 f := pragmaFields(text) 132 133 verb := f[0][3:] // skip "go:" 134 switch verb { 135 case "cgo_export_static", "cgo_export_dynamic": 136 switch { 137 case len(f) == 2 && !isQuoted(f[1]): 138 local := plan9quote(f[1]) 139 return fmt.Sprintln(verb, local) 140 141 case len(f) == 3 && !isQuoted(f[1]) && !isQuoted(f[2]): 142 local := plan9quote(f[1]) 143 remote := plan9quote(f[2]) 144 return fmt.Sprintln(verb, local, remote) 145 146 default: 147 yyerror(`usage: //go:%s local [remote]`, verb) 148 } 149 case "cgo_import_dynamic": 150 switch { 151 case len(f) == 2 && !isQuoted(f[1]): 152 local := plan9quote(f[1]) 153 return fmt.Sprintln(verb, local) 154 155 case len(f) == 3 && !isQuoted(f[1]) && !isQuoted(f[2]): 156 local := plan9quote(f[1]) 157 remote := plan9quote(f[2]) 158 return fmt.Sprintln(verb, local, remote) 159 160 case len(f) == 4 && !isQuoted(f[1]) && !isQuoted(f[2]) && isQuoted(f[3]): 161 local := plan9quote(f[1]) 162 remote := plan9quote(f[2]) 163 library := plan9quote(strings.Trim(f[3], `"`)) 164 return fmt.Sprintln(verb, local, remote, library) 165 166 default: 167 yyerror(`usage: //go:cgo_import_dynamic local [remote ["library"]]`) 168 } 169 case "cgo_import_static": 170 switch { 171 case len(f) == 2 && !isQuoted(f[1]): 172 local := plan9quote(f[1]) 173 return fmt.Sprintln(verb, local) 174 175 default: 176 yyerror(`usage: //go:cgo_import_static local`) 177 } 178 case "cgo_dynamic_linker": 179 switch { 180 case len(f) == 2 && isQuoted(f[1]): 181 path := plan9quote(strings.Trim(f[1], `"`)) 182 return fmt.Sprintln(verb, path) 183 184 default: 185 yyerror(`usage: //go:cgo_dynamic_linker "path"`) 186 } 187 case "cgo_ldflag": 188 switch { 189 case len(f) == 2 && isQuoted(f[1]): 190 arg := plan9quote(strings.Trim(f[1], `"`)) 191 return fmt.Sprintln(verb, arg) 192 193 default: 194 yyerror(`usage: //go:cgo_ldflag "arg"`) 195 } 196 } 197 return "" 198 } 199 200 // pragmaFields is similar to strings.FieldsFunc(s, isSpace) 201 // but does not split when inside double quoted regions and always 202 // splits before the start and after the end of a double quoted region. 203 // pragmaFields does not recognize escaped quotes. If a quote in s is not 204 // closed the part after the opening quote will not be returned as a field. 205 func pragmaFields(s string) []string { 206 var a []string 207 inQuote := false 208 fieldStart := -1 // Set to -1 when looking for start of field. 209 for i, c := range s { 210 switch { 211 case c == '"': 212 if inQuote { 213 inQuote = false 214 a = append(a, s[fieldStart:i+1]) 215 fieldStart = -1 216 } else { 217 inQuote = true 218 if fieldStart >= 0 { 219 a = append(a, s[fieldStart:i]) 220 } 221 fieldStart = i 222 } 223 case !inQuote && isSpace(c): 224 if fieldStart >= 0 { 225 a = append(a, s[fieldStart:i]) 226 fieldStart = -1 227 } 228 default: 229 if fieldStart == -1 { 230 fieldStart = i 231 } 232 } 233 } 234 if !inQuote && fieldStart >= 0 { // Last field might end at the end of the string. 235 a = append(a, s[fieldStart:]) 236 } 237 return a 238 } 239