github.com/bir3/gocompiler@v0.3.205/src/cmd/compile/internal/noder/noder.go (about) 1 // Copyright 2016 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 "errors" 9 "fmt" 10 "os" 11 "path/filepath" 12 "runtime" 13 "strconv" 14 "strings" 15 "unicode" 16 "unicode/utf8" 17 18 "github.com/bir3/gocompiler/src/cmd/compile/internal/base" 19 "github.com/bir3/gocompiler/src/cmd/compile/internal/ir" 20 "github.com/bir3/gocompiler/src/cmd/compile/internal/syntax" 21 "github.com/bir3/gocompiler/src/cmd/compile/internal/typecheck" 22 "github.com/bir3/gocompiler/src/cmd/compile/internal/types" 23 "github.com/bir3/gocompiler/src/cmd/internal/objabi" 24 "github.com/bir3/gocompiler/src/cmd/internal/src" 25 ) 26 27 func LoadPackage(filenames []string) { 28 base.Timer.Start("fe", "parse") 29 30 // Limit the number of simultaneously open files. 31 sem := make(chan struct{}, runtime.GOMAXPROCS(0)+10) 32 33 noders := make([]*noder, len(filenames)) 34 for i := range noders { 35 p := noder{ 36 err: make(chan syntax.Error), 37 } 38 noders[i] = &p 39 } 40 41 // Move the entire syntax processing logic into a separate goroutine to avoid blocking on the "sem". 42 go func() { 43 for i, filename := range filenames { 44 filename := filename 45 p := noders[i] 46 sem <- struct{}{} 47 go func() { 48 defer func() { <-sem }() 49 defer close(p.err) 50 fbase := syntax.NewFileBase(filename) 51 52 f, err := os.Open(filename) 53 if err != nil { 54 p.error(syntax.Error{Msg: err.Error()}) 55 return 56 } 57 defer f.Close() 58 59 p.file, _ = syntax.Parse(fbase, f, p.error, p.pragma, syntax.CheckBranches) // errors are tracked via p.error 60 }() 61 } 62 }() 63 64 var lines uint 65 for _, p := range noders { 66 for e := range p.err { 67 p.errorAt(e.Pos, "%s", e.Msg) 68 } 69 if p.file == nil { 70 base.ErrorExit() 71 } 72 lines += p.file.EOF.Line() 73 } 74 base.Timer.AddEvent(int64(lines), "lines") 75 76 if base.Debug.Unified != 0 { 77 unified(noders) 78 return 79 } 80 81 // Use types2 to type-check and generate IR. 82 check2(noders) 83 } 84 85 func (p *noder) errorAt(pos syntax.Pos, format string, args ...interface{}) { 86 base.ErrorfAt(p.makeXPos(pos), format, args...) 87 } 88 89 // trimFilename returns the "trimmed" filename of b, which is the 90 // absolute filename after applying -trimpath processing. This 91 // filename form is suitable for use in object files and export data. 92 // 93 // If b's filename has already been trimmed (i.e., because it was read 94 // in from an imported package's export data), then the filename is 95 // returned unchanged. 96 func trimFilename(b *syntax.PosBase) string { 97 filename := b.Filename() 98 if !b.Trimmed() { 99 dir := "" 100 if b.IsFileBase() { 101 dir = base.Ctxt.Pathname 102 } 103 filename = objabi.AbsFile(dir, filename, base.Flag.TrimPath) 104 } 105 return filename 106 } 107 108 // noder transforms package syntax's AST into a Node tree. 109 type noder struct { 110 posMap 111 112 file *syntax.File 113 linknames []linkname 114 pragcgobuf [][]string 115 err chan syntax.Error 116 importedUnsafe bool 117 importedEmbed bool 118 } 119 120 // linkname records a //go:linkname directive. 121 type linkname struct { 122 pos syntax.Pos 123 local string 124 remote string 125 } 126 127 func (p *noder) processPragmas() { 128 for _, l := range p.linknames { 129 if !p.importedUnsafe { 130 p.errorAt(l.pos, "//go:linkname only allowed in Go files that import \"unsafe\"") 131 continue 132 } 133 n := ir.AsNode(typecheck.Lookup(l.local).Def) 134 if n == nil || n.Op() != ir.ONAME { 135 if types.AllowsGoVersion(1, 18) { 136 p.errorAt(l.pos, "//go:linkname must refer to declared function or variable") 137 } 138 continue 139 } 140 if n.Sym().Linkname != "" { 141 p.errorAt(l.pos, "duplicate //go:linkname for %s", l.local) 142 continue 143 } 144 n.Sym().Linkname = l.remote 145 } 146 typecheck.Target.CgoPragmas = append(typecheck.Target.CgoPragmas, p.pragcgobuf...) 147 } 148 149 var unOps = [...]ir.Op{ 150 syntax.Recv: ir.ORECV, 151 syntax.Mul: ir.ODEREF, 152 syntax.And: ir.OADDR, 153 154 syntax.Not: ir.ONOT, 155 syntax.Xor: ir.OBITNOT, 156 syntax.Add: ir.OPLUS, 157 syntax.Sub: ir.ONEG, 158 } 159 160 var binOps = [...]ir.Op{ 161 syntax.OrOr: ir.OOROR, 162 syntax.AndAnd: ir.OANDAND, 163 164 syntax.Eql: ir.OEQ, 165 syntax.Neq: ir.ONE, 166 syntax.Lss: ir.OLT, 167 syntax.Leq: ir.OLE, 168 syntax.Gtr: ir.OGT, 169 syntax.Geq: ir.OGE, 170 171 syntax.Add: ir.OADD, 172 syntax.Sub: ir.OSUB, 173 syntax.Or: ir.OOR, 174 syntax.Xor: ir.OXOR, 175 176 syntax.Mul: ir.OMUL, 177 syntax.Div: ir.ODIV, 178 syntax.Rem: ir.OMOD, 179 syntax.And: ir.OAND, 180 syntax.AndNot: ir.OANDNOT, 181 syntax.Shl: ir.OLSH, 182 syntax.Shr: ir.ORSH, 183 } 184 185 func wrapname(pos src.XPos, x ir.Node) ir.Node { 186 // These nodes do not carry line numbers. 187 // Introduce a wrapper node to give them the correct line. 188 switch x.Op() { 189 case ir.OTYPE, ir.OLITERAL: 190 if x.Sym() == nil { 191 break 192 } 193 fallthrough 194 case ir.ONAME, ir.ONONAME: 195 p := ir.NewParenExpr(pos, x) 196 p.SetImplicit(true) 197 return p 198 } 199 return x 200 } 201 202 // error is called concurrently if files are parsed concurrently. 203 func (p *noder) error(err error) { 204 p.err <- err.(syntax.Error) 205 } 206 207 // pragmas that are allowed in the std lib, but don't have 208 // a syntax.Pragma value (see lex.go) associated with them. 209 var allowedStdPragmas = map[string]bool{ 210 "go:cgo_export_static": true, 211 "go:cgo_export_dynamic": true, 212 "go:cgo_import_static": true, 213 "go:cgo_import_dynamic": true, 214 "go:cgo_ldflag": true, 215 "go:cgo_dynamic_linker": true, 216 "go:embed": true, 217 "go:generate": true, 218 } 219 220 // *pragmas is the value stored in a syntax.pragmas during parsing. 221 type pragmas struct { 222 Flag ir.PragmaFlag // collected bits 223 Pos []pragmaPos // position of each individual flag 224 Embeds []pragmaEmbed 225 } 226 227 type pragmaPos struct { 228 Flag ir.PragmaFlag 229 Pos syntax.Pos 230 } 231 232 type pragmaEmbed struct { 233 Pos syntax.Pos 234 Patterns []string 235 } 236 237 func (p *noder) checkUnusedDuringParse(pragma *pragmas) { 238 for _, pos := range pragma.Pos { 239 if pos.Flag&pragma.Flag != 0 { 240 p.error(syntax.Error{Pos: pos.Pos, Msg: "misplaced compiler directive"}) 241 } 242 } 243 if len(pragma.Embeds) > 0 { 244 for _, e := range pragma.Embeds { 245 p.error(syntax.Error{Pos: e.Pos, Msg: "misplaced go:embed directive"}) 246 } 247 } 248 } 249 250 // pragma is called concurrently if files are parsed concurrently. 251 func (p *noder) pragma(pos syntax.Pos, blankLine bool, text string, old syntax.Pragma) syntax.Pragma { 252 pragma, _ := old.(*pragmas) 253 if pragma == nil { 254 pragma = new(pragmas) 255 } 256 257 if text == "" { 258 // unused pragma; only called with old != nil. 259 p.checkUnusedDuringParse(pragma) 260 return nil 261 } 262 263 if strings.HasPrefix(text, "line ") { 264 // line directives are handled by syntax package 265 panic("unreachable") 266 } 267 268 if !blankLine { 269 // directive must be on line by itself 270 p.error(syntax.Error{Pos: pos, Msg: "misplaced compiler directive"}) 271 return pragma 272 } 273 274 switch { 275 case strings.HasPrefix(text, "go:linkname "): 276 f := strings.Fields(text) 277 if !(2 <= len(f) && len(f) <= 3) { 278 p.error(syntax.Error{Pos: pos, Msg: "usage: //go:linkname localname [linkname]"}) 279 break 280 } 281 // The second argument is optional. If omitted, we use 282 // the default object symbol name for this and 283 // linkname only serves to mark this symbol as 284 // something that may be referenced via the object 285 // symbol name from another package. 286 var target string 287 if len(f) == 3 { 288 target = f[2] 289 } else if base.Ctxt.Pkgpath != "" { 290 // Use the default object symbol name if the 291 // user didn't provide one. 292 target = objabi.PathToPrefix(base.Ctxt.Pkgpath) + "." + f[1] 293 } else { 294 p.error(syntax.Error{Pos: pos, Msg: "//go:linkname requires linkname argument or -p compiler flag"}) 295 break 296 } 297 p.linknames = append(p.linknames, linkname{pos, f[1], target}) 298 299 case text == "go:embed", strings.HasPrefix(text, "go:embed "): 300 args, err := parseGoEmbed(text[len("go:embed"):]) 301 if err != nil { 302 p.error(syntax.Error{Pos: pos, Msg: err.Error()}) 303 } 304 if len(args) == 0 { 305 p.error(syntax.Error{Pos: pos, Msg: "usage: //go:embed pattern..."}) 306 break 307 } 308 pragma.Embeds = append(pragma.Embeds, pragmaEmbed{pos, args}) 309 310 case strings.HasPrefix(text, "go:cgo_import_dynamic "): 311 // This is permitted for general use because Solaris 312 // code relies on it in golang.org/x/sys/unix and others. 313 fields := pragmaFields(text) 314 if len(fields) >= 4 { 315 lib := strings.Trim(fields[3], `"`) 316 if lib != "" && !safeArg(lib) && !isCgoGeneratedFile(pos) { 317 p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("invalid library name %q in cgo_import_dynamic directive", lib)}) 318 } 319 p.pragcgo(pos, text) 320 pragma.Flag |= pragmaFlag("go:cgo_import_dynamic") 321 break 322 } 323 fallthrough 324 case strings.HasPrefix(text, "go:cgo_"): 325 // For security, we disallow //go:cgo_* directives other 326 // than cgo_import_dynamic outside cgo-generated files. 327 // Exception: they are allowed in the standard library, for runtime and syscall. 328 if !isCgoGeneratedFile(pos) && !base.Flag.Std { 329 p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("//%s only allowed in cgo-generated code", text)}) 330 } 331 p.pragcgo(pos, text) 332 fallthrough // because of //go:cgo_unsafe_args 333 default: 334 verb := text 335 if i := strings.Index(text, " "); i >= 0 { 336 verb = verb[:i] 337 } 338 flag := pragmaFlag(verb) 339 const runtimePragmas = ir.Systemstack | ir.Nowritebarrier | ir.Nowritebarrierrec | ir.Yeswritebarrierrec 340 if !base.Flag.CompilingRuntime && flag&runtimePragmas != 0 { 341 p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("//%s only allowed in runtime", verb)}) 342 } 343 if flag == ir.UintptrKeepAlive && !base.Flag.Std { 344 p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("//%s is only allowed in the standard library", verb)}) 345 } 346 if flag == 0 && !allowedStdPragmas[verb] && base.Flag.Std { 347 p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("//%s is not allowed in the standard library", verb)}) 348 } 349 pragma.Flag |= flag 350 pragma.Pos = append(pragma.Pos, pragmaPos{flag, pos}) 351 } 352 353 return pragma 354 } 355 356 // isCgoGeneratedFile reports whether pos is in a file 357 // generated by cgo, which is to say a file with name 358 // beginning with "_cgo_". Such files are allowed to 359 // contain cgo directives, and for security reasons 360 // (primarily misuse of linker flags), other files are not. 361 // See golang.org/issue/23672. 362 func isCgoGeneratedFile(pos syntax.Pos) bool { 363 return strings.HasPrefix(filepath.Base(trimFilename(pos.Base())), "_cgo_") 364 } 365 366 // safeArg reports whether arg is a "safe" command-line argument, 367 // meaning that when it appears in a command-line, it probably 368 // doesn't have some special meaning other than its own name. 369 // This is copied from SafeArg in cmd/go/internal/load/pkg.go. 370 func safeArg(name string) bool { 371 if name == "" { 372 return false 373 } 374 c := name[0] 375 return '0' <= c && c <= '9' || 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || c == '.' || c == '_' || c == '/' || c >= utf8.RuneSelf 376 } 377 378 // parseGoEmbed parses the text following "//go:embed" to extract the glob patterns. 379 // It accepts unquoted space-separated patterns as well as double-quoted and back-quoted Go strings. 380 // go/build/read.go also processes these strings and contains similar logic. 381 func parseGoEmbed(args string) ([]string, error) { 382 var list []string 383 for args = strings.TrimSpace(args); args != ""; args = strings.TrimSpace(args) { 384 var path string 385 Switch: 386 switch args[0] { 387 default: 388 i := len(args) 389 for j, c := range args { 390 if unicode.IsSpace(c) { 391 i = j 392 break 393 } 394 } 395 path = args[:i] 396 args = args[i:] 397 398 case '`': 399 i := strings.Index(args[1:], "`") 400 if i < 0 { 401 return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args) 402 } 403 path = args[1 : 1+i] 404 args = args[1+i+1:] 405 406 case '"': 407 i := 1 408 for ; i < len(args); i++ { 409 if args[i] == '\\' { 410 i++ 411 continue 412 } 413 if args[i] == '"' { 414 q, err := strconv.Unquote(args[:i+1]) 415 if err != nil { 416 return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args[:i+1]) 417 } 418 path = q 419 args = args[i+1:] 420 break Switch 421 } 422 } 423 if i >= len(args) { 424 return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args) 425 } 426 } 427 428 if args != "" { 429 r, _ := utf8.DecodeRuneInString(args) 430 if !unicode.IsSpace(r) { 431 return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args) 432 } 433 } 434 list = append(list, path) 435 } 436 return list, nil 437 } 438 439 // A function named init is a special case. 440 // It is called by the initialization before main is run. 441 // To make it unique within a package and also uncallable, 442 // the name, normally "pkg.init", is altered to "pkg.init.0". 443 var renameinitgen int 444 445 func Renameinit() *types.Sym { 446 s := typecheck.LookupNum("init.", renameinitgen) 447 renameinitgen++ 448 return s 449 } 450 451 func varEmbed(makeXPos func(syntax.Pos) src.XPos, name *ir.Name, decl *syntax.VarDecl, pragma *pragmas, haveEmbed bool) { 452 pragmaEmbeds := pragma.Embeds 453 pragma.Embeds = nil 454 if len(pragmaEmbeds) == 0 { 455 return 456 } 457 458 if err := checkEmbed(decl, haveEmbed, typecheck.DeclContext != ir.PEXTERN); err != nil { 459 base.ErrorfAt(makeXPos(pragmaEmbeds[0].Pos), "%s", err) 460 return 461 } 462 463 var embeds []ir.Embed 464 for _, e := range pragmaEmbeds { 465 embeds = append(embeds, ir.Embed{Pos: makeXPos(e.Pos), Patterns: e.Patterns}) 466 } 467 typecheck.Target.Embeds = append(typecheck.Target.Embeds, name) 468 name.Embed = &embeds 469 } 470 471 func checkEmbed(decl *syntax.VarDecl, haveEmbed, withinFunc bool) error { 472 switch { 473 case !haveEmbed: 474 return errors.New("go:embed only allowed in Go files that import \"embed\"") 475 case len(decl.NameList) > 1: 476 return errors.New("go:embed cannot apply to multiple vars") 477 case decl.Values != nil: 478 return errors.New("go:embed cannot apply to var with initializer") 479 case decl.Type == nil: 480 // Should not happen, since Values == nil now. 481 return errors.New("go:embed cannot apply to var without type") 482 case withinFunc: 483 return errors.New("go:embed cannot apply to var inside func") 484 case !types.AllowsGoVersion(1, 16): 485 return fmt.Errorf("go:embed requires go1.16 or later (-lang was set to %s; check go.mod)", base.Flag.Lang) 486 487 default: 488 return nil 489 } 490 }