aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal/noder/lex.go
blob: cef0f082ca9e04f9ce74eb1233e885d3dde1d960 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package noder

import (
	"fmt"
	"internal/buildcfg"
	"strings"

	"cmd/compile/internal/ir"
	"cmd/compile/internal/syntax"
)

func isSpace(c rune) bool {
	return c == ' ' || c == '\t' || c == '\n' || c == '\r'
}

func isQuoted(s string) bool {
	return len(s) >= 2 && s[0] == '"' && s[len(s)-1] == '"'
}

const (
	funcPragmas = ir.Nointerface |
		ir.Noescape |
		ir.Norace |
		ir.Nosplit |
		ir.Noinline |
		ir.NoCheckPtr |
		ir.RegisterParams | // TODO(register args) remove after register abi is working
		ir.CgoUnsafeArgs |
		ir.UintptrKeepAlive |
		ir.UintptrEscapes |
		ir.Systemstack |
		ir.Nowritebarrier |
		ir.Nowritebarrierrec |
		ir.Yeswritebarrierrec

	typePragmas = ir.NotInHeap
)

func pragmaFlag(verb string) ir.PragmaFlag {
	switch verb {
	case "go:build":
		return ir.GoBuildPragma
	case "go:nointerface":
		if buildcfg.Experiment.FieldTrack {
			return ir.Nointerface
		}
	case "go:noescape":
		return ir.Noescape
	case "go:norace":
		return ir.Norace
	case "go:nosplit":
		return ir.Nosplit | ir.NoCheckPtr // implies NoCheckPtr (see #34972)
	case "go:noinline":
		return ir.Noinline
	case "go:nocheckptr":
		return ir.NoCheckPtr
	case "go:systemstack":
		return ir.Systemstack
	case "go:nowritebarrier":
		return ir.Nowritebarrier
	case "go:nowritebarrierrec":
		return ir.Nowritebarrierrec | ir.Nowritebarrier // implies Nowritebarrier
	case "go:yeswritebarrierrec":
		return ir.Yeswritebarrierrec
	case "go:cgo_unsafe_args":
		return ir.CgoUnsafeArgs | ir.NoCheckPtr // implies NoCheckPtr (see #34968)
	case "go:uintptrkeepalive":
		return ir.UintptrKeepAlive
	case "go:uintptrescapes":
		// This directive extends //go:uintptrkeepalive by forcing
		// uintptr arguments to escape to the heap, which makes stack
		// growth safe.
		return ir.UintptrEscapes | ir.UintptrKeepAlive // implies UintptrKeepAlive
	case "go:registerparams": // TODO(register args) remove after register abi is working
		return ir.RegisterParams
	case "go:notinheap":
		return ir.NotInHeap
	}
	return 0
}

// pragcgo is called concurrently if files are parsed concurrently.
func (p *noder) pragcgo(pos syntax.Pos, text string) {
	f := pragmaFields(text)

	verb := strings.TrimPrefix(f[0], "go:")
	f[0] = verb

	switch verb {
	case "cgo_export_static", "cgo_export_dynamic":
		switch {
		case len(f) == 2 && !isQuoted(f[1]):
		case len(f) == 3 && !isQuoted(f[1]) && !isQuoted(f[2]):
		default:
			p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf(`usage: //go:%s local [remote]`, verb)})
			return
		}
	case "cgo_import_dynamic":
		switch {
		case len(f) == 2 && !isQuoted(f[1]):
		case len(f) == 3 && !isQuoted(f[1]) && !isQuoted(f[2]):
		case len(f) == 4 && !isQuoted(f[1]) && !isQuoted(f[2]) && isQuoted(f[3]):
			f[3] = strings.Trim(f[3], `"`)
			if buildcfg.GOOS == "aix" && f[3] != "" {
				// On Aix, library pattern must be "lib.a/object.o"
				// or "lib.a/libname.so.X"
				n := strings.Split(f[3], "/")
				if len(n) != 2 || !strings.HasSuffix(n[0], ".a") || (!strings.HasSuffix(n[1], ".o") && !strings.Contains(n[1], ".so.")) {
					p.error(syntax.Error{Pos: pos, Msg: `usage: //go:cgo_import_dynamic local [remote ["lib.a/object.o"]]`})
					return
				}
			}
		default:
			p.error(syntax.Error{Pos: pos, Msg: `usage: //go:cgo_import_dynamic local [remote ["library"]]`})
			return
		}
	case "cgo_import_static":
		switch {
		case len(f) == 2 && !isQuoted(f[1]):
		default:
			p.error(syntax.Error{Pos: pos, Msg: `usage: //go:cgo_import_static local`})
			return
		}
	case "cgo_dynamic_linker":
		switch {
		case len(f) == 2 && isQuoted(f[1]):
			f[1] = strings.Trim(f[1], `"`)
		default:
			p.error(syntax.Error{Pos: pos, Msg: `usage: //go:cgo_dynamic_linker "path"`})
			return
		}
	case "cgo_ldflag":
		switch {
		case len(f) == 2 && isQuoted(f[1]):
			f[1] = strings.Trim(f[1], `"`)
		default:
			p.error(syntax.Error{Pos: pos, Msg: `usage: //go:cgo_ldflag "arg"`})
			return
		}
	default:
		return
	}
	p.pragcgobuf = append(p.pragcgobuf, f)
}

// pragmaFields is similar to strings.FieldsFunc(s, isSpace)
// but does not split when inside double quoted regions and always
// splits before the start and after the end of a double quoted region.
// pragmaFields does not recognize escaped quotes. If a quote in s is not
// closed the part after the opening quote will not be returned as a field.
func pragmaFields(s string) []string {
	var a []string
	inQuote := false
	fieldStart := -1 // Set to -1 when looking for start of field.
	for i, c := range s {
		switch {
		case c == '"':
			if inQuote {
				inQuote = false
				a = append(a, s[fieldStart:i+1])
				fieldStart = -1
			} else {
				inQuote = true
				if fieldStart >= 0 {
					a = append(a, s[fieldStart:i])
				}
				fieldStart = i
			}
		case !inQuote && isSpace(c):
			if fieldStart >= 0 {
				a = append(a, s[fieldStart:i])
				fieldStart = -1
			}
		default:
			if fieldStart == -1 {
				fieldStart = i
			}
		}
	}
	if !inQuote && fieldStart >= 0 { // Last field might end at the end of the string.
		a = append(a, s[fieldStart:])
	}
	return a
}