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/internal/obj"
9	"cmd/internal/src"
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	Importdef *Pkg   // where imported definition was found
30	Linkname  string // link name
31
32	Pkg  *Pkg
33	Name string // object name
34
35	// saved and restored by dcopy
36	Def        *Node    // definition: ONAME OTYPE OPACK or OLITERAL
37	Block      int32    // blocknumber to catch redeclaration
38	Lastlineno src.XPos // last declaration for diagnostic
39
40	flags   bitset8
41	Label   *Node // corresponding label (ephemeral)
42	Origpkg *Pkg  // original package for . import
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; uses internal ABI
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
69func (sym *Sym) LinksymName() string {
70	if sym.IsBlank() {
71		return "_"
72	}
73	if sym.Linkname != "" {
74		return sym.Linkname
75	}
76	return sym.Pkg.Prefix + "." + sym.Name
77}
78
79func (sym *Sym) Linksym() *obj.LSym {
80	if sym == nil {
81		return nil
82	}
83	initPkg := func(r *obj.LSym) {
84		if sym.Linkname != "" {
85			r.Pkg = "_"
86		} else {
87			r.Pkg = sym.Pkg.Prefix
88		}
89	}
90	if sym.Func() {
91		// This is a function symbol. Mark it as "internal ABI".
92		return Ctxt.LookupABIInit(sym.LinksymName(), obj.ABIInternal, initPkg)
93	}
94	return Ctxt.LookupInit(sym.LinksymName(), initPkg)
95}
96
97// Less reports whether symbol a is ordered before symbol b.
98//
99// Symbols are ordered exported before non-exported, then by name, and
100// finally (for non-exported symbols) by package height and path.
101//
102// Ordering by package height is necessary to establish a consistent
103// ordering for non-exported names with the same spelling but from
104// different packages. We don't necessarily know the path for the
105// package being compiled, but by definition it will have a height
106// greater than any other packages seen within the compilation unit.
107// For more background, see issue #24693.
108func (a *Sym) Less(b *Sym) bool {
109	if a == b {
110		return false
111	}
112
113	// Exported symbols before non-exported.
114	ea := IsExported(a.Name)
115	eb := IsExported(b.Name)
116	if ea != eb {
117		return ea
118	}
119
120	// Order by name and then (for non-exported names) by package
121	// height and path.
122	if a.Name != b.Name {
123		return a.Name < b.Name
124	}
125	if !ea {
126		if a.Pkg.Height != b.Pkg.Height {
127			return a.Pkg.Height < b.Pkg.Height
128		}
129		return a.Pkg.Path < b.Pkg.Path
130	}
131	return false
132}
133
134// IsExported reports whether name is an exported Go symbol (that is,
135// whether it begins with an upper-case letter).
136func IsExported(name string) bool {
137	if r := name[0]; r < utf8.RuneSelf {
138		return 'A' <= r && r <= 'Z'
139	}
140	r, _ := utf8.DecodeRuneInString(name)
141	return unicode.IsUpper(r)
142}
143