1// Copyright 2017 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
5package types
6
7import (
8	"cmd/compile/internal/base"
9	"cmd/internal/obj"
10	"unicode"
11	"unicode/utf8"
12)
13
14// Sym represents an object name in a segmented (pkg, name) namespace.
15// Most commonly, this is a Go identifier naming an object declared within a package,
16// but Syms are also used to name internal synthesized objects.
17//
18// As an exception, field and method names that are exported use the Sym
19// associated with localpkg instead of the package that declared them. This
20// allows using Sym pointer equality to test for Go identifier uniqueness when
21// handling selector expressions.
22//
23// Ideally, Sym should be used for representing Go language constructs,
24// while cmd/internal/obj.LSym is used for representing emitted artifacts.
25//
26// NOTE: In practice, things can be messier than the description above
27// for various reasons (historical, convenience).
28type Sym struct {
29	Linkname string // link name
30
31	Pkg  *Pkg
32	Name string // object name
33
34	// The unique ONAME, OTYPE, OPACK, or OLITERAL node that this symbol is
35	// bound to within the current scope. (Most parts of the compiler should
36	// prefer passing the Node directly, rather than relying on this field.)
37	//
38	// Deprecated: New code should avoid depending on Sym.Def. Add
39	// mdempsky@ as a reviewer for any CLs involving Sym.Def.
40	Def Object
41
42	flags bitset8
43}
44
45const (
46	symOnExportList = 1 << iota // added to exportlist (no need to add again)
47	symUniq
48	symSiggen // type symbol has been generated
49	symAsm    // on asmlist, for writing to -asmhdr
50	symFunc   // function symbol
51)
52
53func (sym *Sym) OnExportList() bool { return sym.flags&symOnExportList != 0 }
54func (sym *Sym) Uniq() bool         { return sym.flags&symUniq != 0 }
55func (sym *Sym) Siggen() bool       { return sym.flags&symSiggen != 0 }
56func (sym *Sym) Asm() bool          { return sym.flags&symAsm != 0 }
57func (sym *Sym) Func() bool         { return sym.flags&symFunc != 0 }
58
59func (sym *Sym) SetOnExportList(b bool) { sym.flags.set(symOnExportList, b) }
60func (sym *Sym) SetUniq(b bool)         { sym.flags.set(symUniq, b) }
61func (sym *Sym) SetSiggen(b bool)       { sym.flags.set(symSiggen, b) }
62func (sym *Sym) SetAsm(b bool)          { sym.flags.set(symAsm, b) }
63func (sym *Sym) SetFunc(b bool)         { sym.flags.set(symFunc, b) }
64
65func (sym *Sym) IsBlank() bool {
66	return sym != nil && sym.Name == "_"
67}
68
69// Deprecated: This method should not be used directly. Instead, use a
70// higher-level abstraction that directly returns the linker symbol
71// for a named object. For example, reflectdata.TypeLinksym(t) instead
72// of reflectdata.TypeSym(t).Linksym().
73func (sym *Sym) Linksym() *obj.LSym {
74	abi := obj.ABI0
75	if sym.Func() {
76		abi = obj.ABIInternal
77	}
78	return sym.LinksymABI(abi)
79}
80
81// Deprecated: This method should not be used directly. Instead, use a
82// higher-level abstraction that directly returns the linker symbol
83// for a named object. For example, (*ir.Name).LinksymABI(abi) instead
84// of (*ir.Name).Sym().LinksymABI(abi).
85func (sym *Sym) LinksymABI(abi obj.ABI) *obj.LSym {
86	if sym == nil {
87		base.Fatalf("nil symbol")
88	}
89	if sym.Linkname != "" {
90		return base.Linkname(sym.Linkname, abi)
91	}
92	return base.PkgLinksym(sym.Pkg.Prefix, sym.Name, abi)
93}
94
95// Less reports whether symbol a is ordered before symbol b.
96//
97// Symbols are ordered exported before non-exported, then by name, and
98// finally (for non-exported symbols) by package path.
99func (a *Sym) Less(b *Sym) bool {
100	if a == b {
101		return false
102	}
103
104	// Nil before non-nil.
105	if a == nil {
106		return true
107	}
108	if b == nil {
109		return false
110	}
111
112	// Exported symbols before non-exported.
113	ea := IsExported(a.Name)
114	eb := IsExported(b.Name)
115	if ea != eb {
116		return ea
117	}
118
119	// Order by name and then (for non-exported names) by package
120	// height and path.
121	if a.Name != b.Name {
122		return a.Name < b.Name
123	}
124	if !ea {
125		return a.Pkg.Path < b.Pkg.Path
126	}
127	return false
128}
129
130// IsExported reports whether name is an exported Go symbol (that is,
131// whether it begins with an upper-case letter).
132func IsExported(name string) bool {
133	if r := name[0]; r < utf8.RuneSelf {
134		return 'A' <= r && r <= 'Z'
135	}
136	r, _ := utf8.DecodeRuneInString(name)
137	return unicode.IsUpper(r)
138}
139