| | |
| | |
| | |
| |
|
| | package noder |
| |
|
| | import ( |
| | "errors" |
| | "fmt" |
| | "internal/buildcfg" |
| | "os" |
| | "path/filepath" |
| | "runtime" |
| | "strconv" |
| | "strings" |
| | "unicode" |
| | "unicode/utf8" |
| |
|
| | "cmd/compile/internal/base" |
| | "cmd/compile/internal/ir" |
| | "cmd/compile/internal/syntax" |
| | "cmd/compile/internal/typecheck" |
| | "cmd/compile/internal/types" |
| | "cmd/internal/objabi" |
| | ) |
| |
|
| | func LoadPackage(filenames []string) { |
| | base.Timer.Start("fe", "parse") |
| |
|
| | |
| | sem := make(chan struct{}, runtime.GOMAXPROCS(0)+10) |
| |
|
| | noders := make([]*noder, len(filenames)) |
| | for i := range noders { |
| | p := noder{ |
| | err: make(chan syntax.Error), |
| | } |
| | noders[i] = &p |
| | } |
| |
|
| | |
| | go func() { |
| | for i, filename := range filenames { |
| | filename := filename |
| | p := noders[i] |
| | sem <- struct{}{} |
| | go func() { |
| | defer func() { <-sem }() |
| | defer close(p.err) |
| | fbase := syntax.NewFileBase(filename) |
| |
|
| | f, err := os.Open(filename) |
| | if err != nil { |
| | p.error(syntax.Error{Msg: err.Error()}) |
| | return |
| | } |
| | defer f.Close() |
| |
|
| | p.file, _ = syntax.Parse(fbase, f, p.error, p.pragma, syntax.CheckBranches) |
| | }() |
| | } |
| | }() |
| |
|
| | var lines uint |
| | var m posMap |
| | for _, p := range noders { |
| | for e := range p.err { |
| | base.ErrorfAt(m.makeXPos(e.Pos), 0, "%s", e.Msg) |
| | } |
| | if p.file == nil { |
| | base.ErrorExit() |
| | } |
| | lines += p.file.EOF.Line() |
| | } |
| | base.Timer.AddEvent(int64(lines), "lines") |
| |
|
| | unified(m, noders) |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | func trimFilename(b *syntax.PosBase) string { |
| | filename := b.Filename() |
| | if !b.Trimmed() { |
| | dir := "" |
| | if b.IsFileBase() { |
| | dir = base.Ctxt.Pathname |
| | } |
| | filename = objabi.AbsFile(dir, filename, base.Flag.TrimPath) |
| | } |
| | return filename |
| | } |
| |
|
| | |
| | type noder struct { |
| | file *syntax.File |
| | linknames []linkname |
| | pragcgobuf [][]string |
| | err chan syntax.Error |
| | } |
| |
|
| | |
| | type linkname struct { |
| | pos syntax.Pos |
| | local string |
| | remote string |
| | } |
| |
|
| | var unOps = [...]ir.Op{ |
| | syntax.Recv: ir.ORECV, |
| | syntax.Mul: ir.ODEREF, |
| | syntax.And: ir.OADDR, |
| |
|
| | syntax.Not: ir.ONOT, |
| | syntax.Xor: ir.OBITNOT, |
| | syntax.Add: ir.OPLUS, |
| | syntax.Sub: ir.ONEG, |
| | } |
| |
|
| | var binOps = [...]ir.Op{ |
| | syntax.OrOr: ir.OOROR, |
| | syntax.AndAnd: ir.OANDAND, |
| |
|
| | syntax.Eql: ir.OEQ, |
| | syntax.Neq: ir.ONE, |
| | syntax.Lss: ir.OLT, |
| | syntax.Leq: ir.OLE, |
| | syntax.Gtr: ir.OGT, |
| | syntax.Geq: ir.OGE, |
| |
|
| | syntax.Add: ir.OADD, |
| | syntax.Sub: ir.OSUB, |
| | syntax.Or: ir.OOR, |
| | syntax.Xor: ir.OXOR, |
| |
|
| | syntax.Mul: ir.OMUL, |
| | syntax.Div: ir.ODIV, |
| | syntax.Rem: ir.OMOD, |
| | syntax.And: ir.OAND, |
| | syntax.AndNot: ir.OANDNOT, |
| | syntax.Shl: ir.OLSH, |
| | syntax.Shr: ir.ORSH, |
| | } |
| |
|
| | |
| | func (p *noder) error(err error) { |
| | p.err <- err.(syntax.Error) |
| | } |
| |
|
| | |
| | |
| | var allowedStdPragmas = map[string]bool{ |
| | "go:cgo_export_static": true, |
| | "go:cgo_export_dynamic": true, |
| | "go:cgo_import_static": true, |
| | "go:cgo_import_dynamic": true, |
| | "go:cgo_ldflag": true, |
| | "go:cgo_dynamic_linker": true, |
| | "go:embed": true, |
| | "go:fix": true, |
| | "go:generate": true, |
| | } |
| |
|
| | |
| | type pragmas struct { |
| | Flag ir.PragmaFlag |
| | Pos []pragmaPos |
| | Embeds []pragmaEmbed |
| | WasmImport *WasmImport |
| | WasmExport *WasmExport |
| | } |
| |
|
| | |
| | type WasmImport struct { |
| | Pos syntax.Pos |
| | Module string |
| | Name string |
| | } |
| |
|
| | |
| | type WasmExport struct { |
| | Pos syntax.Pos |
| | Name string |
| | } |
| |
|
| | type pragmaPos struct { |
| | Flag ir.PragmaFlag |
| | Pos syntax.Pos |
| | } |
| |
|
| | type pragmaEmbed struct { |
| | Pos syntax.Pos |
| | Patterns []string |
| | } |
| |
|
| | func (p *noder) checkUnusedDuringParse(pragma *pragmas) { |
| | for _, pos := range pragma.Pos { |
| | if pos.Flag&pragma.Flag != 0 { |
| | p.error(syntax.Error{Pos: pos.Pos, Msg: "misplaced compiler directive"}) |
| | } |
| | } |
| | if len(pragma.Embeds) > 0 { |
| | for _, e := range pragma.Embeds { |
| | p.error(syntax.Error{Pos: e.Pos, Msg: "misplaced go:embed directive"}) |
| | } |
| | } |
| | if pragma.WasmImport != nil { |
| | p.error(syntax.Error{Pos: pragma.WasmImport.Pos, Msg: "misplaced go:wasmimport directive"}) |
| | } |
| | if pragma.WasmExport != nil { |
| | p.error(syntax.Error{Pos: pragma.WasmExport.Pos, Msg: "misplaced go:wasmexport directive"}) |
| | } |
| | } |
| |
|
| | |
| | func (p *noder) pragma(pos syntax.Pos, blankLine bool, text string, old syntax.Pragma) syntax.Pragma { |
| | pragma, _ := old.(*pragmas) |
| | if pragma == nil { |
| | pragma = new(pragmas) |
| | } |
| |
|
| | if text == "" { |
| | |
| | p.checkUnusedDuringParse(pragma) |
| | return nil |
| | } |
| |
|
| | if strings.HasPrefix(text, "line ") { |
| | |
| | panic("unreachable") |
| | } |
| |
|
| | if !blankLine { |
| | |
| | p.error(syntax.Error{Pos: pos, Msg: "misplaced compiler directive"}) |
| | return pragma |
| | } |
| |
|
| | switch { |
| | case strings.HasPrefix(text, "go:wasmimport "): |
| | f := strings.Fields(text) |
| | if len(f) != 3 { |
| | p.error(syntax.Error{Pos: pos, Msg: "usage: //go:wasmimport importmodule importname"}) |
| | break |
| | } |
| |
|
| | if buildcfg.GOARCH == "wasm" { |
| | |
| | pragma.WasmImport = &WasmImport{ |
| | Pos: pos, |
| | Module: f[1], |
| | Name: f[2], |
| | } |
| | } |
| |
|
| | case strings.HasPrefix(text, "go:wasmexport "): |
| | f := strings.Fields(text) |
| | if len(f) != 2 { |
| | |
| | p.error(syntax.Error{Pos: pos, Msg: "usage: //go:wasmexport exportname"}) |
| | break |
| | } |
| |
|
| | if buildcfg.GOARCH == "wasm" { |
| | |
| | pragma.WasmExport = &WasmExport{ |
| | Pos: pos, |
| | Name: f[1], |
| | } |
| | } |
| |
|
| | case strings.HasPrefix(text, "go:linkname "): |
| | f := strings.Fields(text) |
| | if !(2 <= len(f) && len(f) <= 3) { |
| | p.error(syntax.Error{Pos: pos, Msg: "usage: //go:linkname localname [linkname]"}) |
| | break |
| | } |
| | |
| | |
| | |
| | |
| | |
| | var target string |
| | if len(f) == 3 { |
| | target = f[2] |
| | } else if base.Ctxt.Pkgpath != "" { |
| | |
| | |
| | target = objabi.PathToPrefix(base.Ctxt.Pkgpath) + "." + f[1] |
| | } else { |
| | panic("missing pkgpath") |
| | } |
| | p.linknames = append(p.linknames, linkname{pos, f[1], target}) |
| |
|
| | case text == "go:embed", strings.HasPrefix(text, "go:embed "): |
| | args, err := parseGoEmbed(text[len("go:embed"):]) |
| | if err != nil { |
| | p.error(syntax.Error{Pos: pos, Msg: err.Error()}) |
| | } |
| | if len(args) == 0 { |
| | p.error(syntax.Error{Pos: pos, Msg: "usage: //go:embed pattern..."}) |
| | break |
| | } |
| | pragma.Embeds = append(pragma.Embeds, pragmaEmbed{pos, args}) |
| |
|
| | case strings.HasPrefix(text, "go:cgo_import_dynamic "): |
| | |
| | |
| | fields := pragmaFields(text) |
| | if len(fields) >= 4 { |
| | lib := strings.Trim(fields[3], `"`) |
| | if lib != "" && !safeArg(lib) && !isCgoGeneratedFile(pos) { |
| | p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("invalid library name %q in cgo_import_dynamic directive", lib)}) |
| | } |
| | p.pragcgo(pos, text) |
| | pragma.Flag |= pragmaFlag("go:cgo_import_dynamic") |
| | break |
| | } |
| | fallthrough |
| | case strings.HasPrefix(text, "go:cgo_"): |
| | |
| | |
| | |
| | if !isCgoGeneratedFile(pos) && !base.Flag.Std { |
| | p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("//%s only allowed in cgo-generated code", text)}) |
| | } |
| | p.pragcgo(pos, text) |
| | fallthrough |
| | default: |
| | verb := text |
| | if i := strings.Index(text, " "); i >= 0 { |
| | verb = verb[:i] |
| | } |
| | flag := pragmaFlag(verb) |
| | const runtimePragmas = ir.Systemstack | ir.Nowritebarrier | ir.Nowritebarrierrec | ir.Yeswritebarrierrec |
| | if !base.Flag.CompilingRuntime && flag&runtimePragmas != 0 { |
| | p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("//%s only allowed in runtime", verb)}) |
| | } |
| | if flag == ir.UintptrKeepAlive && !base.Flag.Std { |
| | p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("//%s is only allowed in the standard library", verb)}) |
| | } |
| | if flag == 0 && !allowedStdPragmas[verb] && base.Flag.Std { |
| | p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("//%s is not allowed in the standard library", verb)}) |
| | } |
| | pragma.Flag |= flag |
| | pragma.Pos = append(pragma.Pos, pragmaPos{flag, pos}) |
| | } |
| |
|
| | return pragma |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | func isCgoGeneratedFile(pos syntax.Pos) bool { |
| | |
| | |
| | return strings.HasPrefix(filepath.Base(trimFilename(pos.Base().Pos().Base())), "_cgo_") |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | func safeArg(name string) bool { |
| | if name == "" { |
| | return false |
| | } |
| | c := name[0] |
| | return '0' <= c && c <= '9' || 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || c == '.' || c == '_' || c == '/' || c >= utf8.RuneSelf |
| | } |
| |
|
| | |
| | |
| | |
| | func parseGoEmbed(args string) ([]string, error) { |
| | var list []string |
| | for args = strings.TrimSpace(args); args != ""; args = strings.TrimSpace(args) { |
| | var path string |
| | Switch: |
| | switch args[0] { |
| | default: |
| | i := len(args) |
| | for j, c := range args { |
| | if unicode.IsSpace(c) { |
| | i = j |
| | break |
| | } |
| | } |
| | path = args[:i] |
| | args = args[i:] |
| |
|
| | case '`': |
| | i := strings.Index(args[1:], "`") |
| | if i < 0 { |
| | return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args) |
| | } |
| | path = args[1 : 1+i] |
| | args = args[1+i+1:] |
| |
|
| | case '"': |
| | i := 1 |
| | for ; i < len(args); i++ { |
| | if args[i] == '\\' { |
| | i++ |
| | continue |
| | } |
| | if args[i] == '"' { |
| | q, err := strconv.Unquote(args[:i+1]) |
| | if err != nil { |
| | return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args[:i+1]) |
| | } |
| | path = q |
| | args = args[i+1:] |
| | break Switch |
| | } |
| | } |
| | if i >= len(args) { |
| | return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args) |
| | } |
| | } |
| |
|
| | if args != "" { |
| | r, _ := utf8.DecodeRuneInString(args) |
| | if !unicode.IsSpace(r) { |
| | return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args) |
| | } |
| | } |
| | list = append(list, path) |
| | } |
| | return list, nil |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | var renameinitgen int |
| |
|
| | func Renameinit() *types.Sym { |
| | s := typecheck.LookupNum("init.", renameinitgen) |
| | renameinitgen++ |
| | return s |
| | } |
| |
|
| | func checkEmbed(decl *syntax.VarDecl, haveEmbed, withinFunc bool) error { |
| | switch { |
| | case !haveEmbed: |
| | return errors.New("go:embed requires import \"embed\" (or import _ \"embed\", if package is not used)") |
| | case len(decl.NameList) > 1: |
| | return errors.New("go:embed cannot apply to multiple vars") |
| | case decl.Values != nil: |
| | return errors.New("go:embed cannot apply to var with initializer") |
| | case decl.Type == nil: |
| | |
| | return errors.New("go:embed cannot apply to var without type") |
| | case withinFunc: |
| | return errors.New("go:embed cannot apply to var inside func") |
| | case !types.AllowsGoVersion(1, 16): |
| | return fmt.Errorf("go:embed requires go1.16 or later (-lang was set to %s; check go.mod)", base.Flag.Lang) |
| |
|
| | default: |
| | return nil |
| | } |
| | } |
| |
|