Home | History | Annotate | Download | only in gc
      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/objabi"
     10 	"cmd/internal/src"
     11 	"fmt"
     12 	"strings"
     13 )
     14 
     15 // lineno is the source position at the start of the most recently lexed token.
     16 // TODO(gri) rename and eventually remove
     17 var lineno src.XPos
     18 
     19 func makePos(base *src.PosBase, line, col uint) src.XPos {
     20 	return Ctxt.PosTable.XPos(src.MakePos(base, line, col))
     21 }
     22 
     23 func isSpace(c rune) bool {
     24 	return c == ' ' || c == '\t' || c == '\n' || c == '\r'
     25 }
     26 
     27 func isQuoted(s string) bool {
     28 	return len(s) >= 2 && s[0] == '"' && s[len(s)-1] == '"'
     29 }
     30 
     31 func plan9quote(s string) string {
     32 	if s == "" {
     33 		return "''"
     34 	}
     35 	for _, c := range s {
     36 		if c <= ' ' || c == '\'' {
     37 			return "'" + strings.Replace(s, "'", "''", -1) + "'"
     38 		}
     39 	}
     40 	return s
     41 }
     42 
     43 const (
     44 	// Func pragmas.
     45 	Nointerface    syntax.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) syntax.Pragma {
     65 	switch verb {
     66 	case "go:nointerface":
     67 		if objabi.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 		return Systemstack
     80 	case "go:nowritebarrier":
     81 		return Nowritebarrier
     82 	case "go:nowritebarrierrec":
     83 		return Nowritebarrierrec | Nowritebarrier // implies Nowritebarrier
     84 	case "go:yeswritebarrierrec":
     85 		return Yeswritebarrierrec
     86 	case "go:cgo_unsafe_args":
     87 		return CgoUnsafeArgs
     88 	case "go:uintptrescapes":
     89 		// For the next function declared in the file
     90 		// any uintptr arguments may be pointer values
     91 		// converted to uintptr. This directive
     92 		// ensures that the referenced allocated
     93 		// object, if any, is retained and not moved
     94 		// until the call completes, even though from
     95 		// the types alone it would appear that the
     96 		// object is no longer needed during the
     97 		// call. The conversion to uintptr must appear
     98 		// in the argument list.
     99 		// Used in syscall/dll_windows.go.
    100 		return UintptrEscapes
    101 	case "go:notinheap":
    102 		return NotInHeap
    103 	}
    104 	return 0
    105 }
    106 
    107 // pragcgo is called concurrently if files are parsed concurrently.
    108 func (p *noder) pragcgo(pos src.Pos, text string) string {
    109 	f := pragmaFields(text)
    110 
    111 	verb := f[0][3:] // skip "go:"
    112 	switch verb {
    113 	case "cgo_export_static", "cgo_export_dynamic":
    114 		switch {
    115 		case len(f) == 2 && !isQuoted(f[1]):
    116 			local := plan9quote(f[1])
    117 			return fmt.Sprintln(verb, local)
    118 
    119 		case len(f) == 3 && !isQuoted(f[1]) && !isQuoted(f[2]):
    120 			local := plan9quote(f[1])
    121 			remote := plan9quote(f[2])
    122 			return fmt.Sprintln(verb, local, remote)
    123 
    124 		default:
    125 			p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf(`usage: //go:%s local [remote]`, verb)})
    126 		}
    127 	case "cgo_import_dynamic":
    128 		switch {
    129 		case len(f) == 2 && !isQuoted(f[1]):
    130 			local := plan9quote(f[1])
    131 			return fmt.Sprintln(verb, local)
    132 
    133 		case len(f) == 3 && !isQuoted(f[1]) && !isQuoted(f[2]):
    134 			local := plan9quote(f[1])
    135 			remote := plan9quote(f[2])
    136 			return fmt.Sprintln(verb, local, remote)
    137 
    138 		case len(f) == 4 && !isQuoted(f[1]) && !isQuoted(f[2]) && isQuoted(f[3]):
    139 			local := plan9quote(f[1])
    140 			remote := plan9quote(f[2])
    141 			library := plan9quote(strings.Trim(f[3], `"`))
    142 			return fmt.Sprintln(verb, local, remote, library)
    143 
    144 		default:
    145 			p.error(syntax.Error{Pos: pos, Msg: `usage: //go:cgo_import_dynamic local [remote ["library"]]`})
    146 		}
    147 	case "cgo_import_static":
    148 		switch {
    149 		case len(f) == 2 && !isQuoted(f[1]):
    150 			local := plan9quote(f[1])
    151 			return fmt.Sprintln(verb, local)
    152 
    153 		default:
    154 			p.error(syntax.Error{Pos: pos, Msg: `usage: //go:cgo_import_static local`})
    155 		}
    156 	case "cgo_dynamic_linker":
    157 		switch {
    158 		case len(f) == 2 && isQuoted(f[1]):
    159 			path := plan9quote(strings.Trim(f[1], `"`))
    160 			return fmt.Sprintln(verb, path)
    161 
    162 		default:
    163 			p.error(syntax.Error{Pos: pos, Msg: `usage: //go:cgo_dynamic_linker "path"`})
    164 		}
    165 	case "cgo_ldflag":
    166 		switch {
    167 		case len(f) == 2 && isQuoted(f[1]):
    168 			arg := plan9quote(strings.Trim(f[1], `"`))
    169 			return fmt.Sprintln(verb, arg)
    170 
    171 		default:
    172 			p.error(syntax.Error{Pos: pos, Msg: `usage: //go:cgo_ldflag "arg"`})
    173 		}
    174 	}
    175 	return ""
    176 }
    177 
    178 // pragmaFields is similar to strings.FieldsFunc(s, isSpace)
    179 // but does not split when inside double quoted regions and always
    180 // splits before the start and after the end of a double quoted region.
    181 // pragmaFields does not recognize escaped quotes. If a quote in s is not
    182 // closed the part after the opening quote will not be returned as a field.
    183 func pragmaFields(s string) []string {
    184 	var a []string
    185 	inQuote := false
    186 	fieldStart := -1 // Set to -1 when looking for start of field.
    187 	for i, c := range s {
    188 		switch {
    189 		case c == '"':
    190 			if inQuote {
    191 				inQuote = false
    192 				a = append(a, s[fieldStart:i+1])
    193 				fieldStart = -1
    194 			} else {
    195 				inQuote = true
    196 				if fieldStart >= 0 {
    197 					a = append(a, s[fieldStart:i])
    198 				}
    199 				fieldStart = i
    200 			}
    201 		case !inQuote && isSpace(c):
    202 			if fieldStart >= 0 {
    203 				a = append(a, s[fieldStart:i])
    204 				fieldStart = -1
    205 			}
    206 		default:
    207 			if fieldStart == -1 {
    208 				fieldStart = i
    209 			}
    210 		}
    211 	}
    212 	if !inQuote && fieldStart >= 0 { // Last field might end at the end of the string.
    213 		a = append(a, s[fieldStart:])
    214 	}
    215 	return a
    216 }
    217