You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

objectpath.go 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523
  1. // Copyright 2018 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. // Package objectpath defines a naming scheme for types.Objects
  5. // (that is, named entities in Go programs) relative to their enclosing
  6. // package.
  7. //
  8. // Type-checker objects are canonical, so they are usually identified by
  9. // their address in memory (a pointer), but a pointer has meaning only
  10. // within one address space. By contrast, objectpath names allow the
  11. // identity of an object to be sent from one program to another,
  12. // establishing a correspondence between types.Object variables that are
  13. // distinct but logically equivalent.
  14. //
  15. // A single object may have multiple paths. In this example,
  16. // type A struct{ X int }
  17. // type B A
  18. // the field X has two paths due to its membership of both A and B.
  19. // The For(obj) function always returns one of these paths, arbitrarily
  20. // but consistently.
  21. package objectpath
  22. import (
  23. "fmt"
  24. "strconv"
  25. "strings"
  26. "go/types"
  27. )
  28. // A Path is an opaque name that identifies a types.Object
  29. // relative to its package. Conceptually, the name consists of a
  30. // sequence of destructuring operations applied to the package scope
  31. // to obtain the original object.
  32. // The name does not include the package itself.
  33. type Path string
  34. // Encoding
  35. //
  36. // An object path is a textual and (with training) human-readable encoding
  37. // of a sequence of destructuring operators, starting from a types.Package.
  38. // The sequences represent a path through the package/object/type graph.
  39. // We classify these operators by their type:
  40. //
  41. // PO package->object Package.Scope.Lookup
  42. // OT object->type Object.Type
  43. // TT type->type Type.{Elem,Key,Params,Results,Underlying} [EKPRU]
  44. // TO type->object Type.{At,Field,Method,Obj} [AFMO]
  45. //
  46. // All valid paths start with a package and end at an object
  47. // and thus may be defined by the regular language:
  48. //
  49. // objectpath = PO (OT TT* TO)*
  50. //
  51. // The concrete encoding follows directly:
  52. // - The only PO operator is Package.Scope.Lookup, which requires an identifier.
  53. // - The only OT operator is Object.Type,
  54. // which we encode as '.' because dot cannot appear in an identifier.
  55. // - The TT operators are encoded as [EKPRU].
  56. // - The OT operators are encoded as [AFMO];
  57. // three of these (At,Field,Method) require an integer operand,
  58. // which is encoded as a string of decimal digits.
  59. // These indices are stable across different representations
  60. // of the same package, even source and export data.
  61. //
  62. // In the example below,
  63. //
  64. // package p
  65. //
  66. // type T interface {
  67. // f() (a string, b struct{ X int })
  68. // }
  69. //
  70. // field X has the path "T.UM0.RA1.F0",
  71. // representing the following sequence of operations:
  72. //
  73. // p.Lookup("T") T
  74. // .Type().Underlying().Method(0). f
  75. // .Type().Results().At(1) b
  76. // .Type().Field(0) X
  77. //
  78. // The encoding is not maximally compact---every R or P is
  79. // followed by an A, for example---but this simplifies the
  80. // encoder and decoder.
  81. //
  82. const (
  83. // object->type operators
  84. opType = '.' // .Type() (Object)
  85. // type->type operators
  86. opElem = 'E' // .Elem() (Pointer, Slice, Array, Chan, Map)
  87. opKey = 'K' // .Key() (Map)
  88. opParams = 'P' // .Params() (Signature)
  89. opResults = 'R' // .Results() (Signature)
  90. opUnderlying = 'U' // .Underlying() (Named)
  91. // type->object operators
  92. opAt = 'A' // .At(i) (Tuple)
  93. opField = 'F' // .Field(i) (Struct)
  94. opMethod = 'M' // .Method(i) (Named or Interface; not Struct: "promoted" names are ignored)
  95. opObj = 'O' // .Obj() (Named)
  96. )
  97. // The For function returns the path to an object relative to its package,
  98. // or an error if the object is not accessible from the package's Scope.
  99. //
  100. // The For function guarantees to return a path only for the following objects:
  101. // - package-level types
  102. // - exported package-level non-types
  103. // - methods
  104. // - parameter and result variables
  105. // - struct fields
  106. // These objects are sufficient to define the API of their package.
  107. // The objects described by a package's export data are drawn from this set.
  108. //
  109. // For does not return a path for predeclared names, imported package
  110. // names, local names, and unexported package-level names (except
  111. // types).
  112. //
  113. // Example: given this definition,
  114. //
  115. // package p
  116. //
  117. // type T interface {
  118. // f() (a string, b struct{ X int })
  119. // }
  120. //
  121. // For(X) would return a path that denotes the following sequence of operations:
  122. //
  123. // p.Scope().Lookup("T") (TypeName T)
  124. // .Type().Underlying().Method(0). (method Func f)
  125. // .Type().Results().At(1) (field Var b)
  126. // .Type().Field(0) (field Var X)
  127. //
  128. // where p is the package (*types.Package) to which X belongs.
  129. func For(obj types.Object) (Path, error) {
  130. pkg := obj.Pkg()
  131. // This table lists the cases of interest.
  132. //
  133. // Object Action
  134. // ------ ------
  135. // nil reject
  136. // builtin reject
  137. // pkgname reject
  138. // label reject
  139. // var
  140. // package-level accept
  141. // func param/result accept
  142. // local reject
  143. // struct field accept
  144. // const
  145. // package-level accept
  146. // local reject
  147. // func
  148. // package-level accept
  149. // init functions reject
  150. // concrete method accept
  151. // interface method accept
  152. // type
  153. // package-level accept
  154. // local reject
  155. //
  156. // The only accessible package-level objects are members of pkg itself.
  157. //
  158. // The cases are handled in four steps:
  159. //
  160. // 1. reject nil and builtin
  161. // 2. accept package-level objects
  162. // 3. reject obviously invalid objects
  163. // 4. search the API for the path to the param/result/field/method.
  164. // 1. reference to nil or builtin?
  165. if pkg == nil {
  166. return "", fmt.Errorf("predeclared %s has no path", obj)
  167. }
  168. scope := pkg.Scope()
  169. // 2. package-level object?
  170. if scope.Lookup(obj.Name()) == obj {
  171. // Only exported objects (and non-exported types) have a path.
  172. // Non-exported types may be referenced by other objects.
  173. if _, ok := obj.(*types.TypeName); !ok && !obj.Exported() {
  174. return "", fmt.Errorf("no path for non-exported %v", obj)
  175. }
  176. return Path(obj.Name()), nil
  177. }
  178. // 3. Not a package-level object.
  179. // Reject obviously non-viable cases.
  180. switch obj := obj.(type) {
  181. case *types.Const, // Only package-level constants have a path.
  182. *types.TypeName, // Only package-level types have a path.
  183. *types.Label, // Labels are function-local.
  184. *types.PkgName: // PkgNames are file-local.
  185. return "", fmt.Errorf("no path for %v", obj)
  186. case *types.Var:
  187. // Could be:
  188. // - a field (obj.IsField())
  189. // - a func parameter or result
  190. // - a local var.
  191. // Sadly there is no way to distinguish
  192. // a param/result from a local
  193. // so we must proceed to the find.
  194. case *types.Func:
  195. // A func, if not package-level, must be a method.
  196. if recv := obj.Type().(*types.Signature).Recv(); recv == nil {
  197. return "", fmt.Errorf("func is not a method: %v", obj)
  198. }
  199. // TODO(adonovan): opt: if the method is concrete,
  200. // do a specialized version of the rest of this function so
  201. // that it's O(1) not O(|scope|). Basically 'find' is needed
  202. // only for struct fields and interface methods.
  203. default:
  204. panic(obj)
  205. }
  206. // 4. Search the API for the path to the var (field/param/result) or method.
  207. // First inspect package-level named types.
  208. // In the presence of path aliases, these give
  209. // the best paths because non-types may
  210. // refer to types, but not the reverse.
  211. empty := make([]byte, 0, 48) // initial space
  212. for _, name := range scope.Names() {
  213. o := scope.Lookup(name)
  214. tname, ok := o.(*types.TypeName)
  215. if !ok {
  216. continue // handle non-types in second pass
  217. }
  218. path := append(empty, name...)
  219. path = append(path, opType)
  220. T := o.Type()
  221. if tname.IsAlias() {
  222. // type alias
  223. if r := find(obj, T, path); r != nil {
  224. return Path(r), nil
  225. }
  226. } else {
  227. // defined (named) type
  228. if r := find(obj, T.Underlying(), append(path, opUnderlying)); r != nil {
  229. return Path(r), nil
  230. }
  231. }
  232. }
  233. // Then inspect everything else:
  234. // non-types, and declared methods of defined types.
  235. for _, name := range scope.Names() {
  236. o := scope.Lookup(name)
  237. path := append(empty, name...)
  238. if _, ok := o.(*types.TypeName); !ok {
  239. if o.Exported() {
  240. // exported non-type (const, var, func)
  241. if r := find(obj, o.Type(), append(path, opType)); r != nil {
  242. return Path(r), nil
  243. }
  244. }
  245. continue
  246. }
  247. // Inspect declared methods of defined types.
  248. if T, ok := o.Type().(*types.Named); ok {
  249. path = append(path, opType)
  250. for i := 0; i < T.NumMethods(); i++ {
  251. m := T.Method(i)
  252. path2 := appendOpArg(path, opMethod, i)
  253. if m == obj {
  254. return Path(path2), nil // found declared method
  255. }
  256. if r := find(obj, m.Type(), append(path2, opType)); r != nil {
  257. return Path(r), nil
  258. }
  259. }
  260. }
  261. }
  262. return "", fmt.Errorf("can't find path for %v in %s", obj, pkg.Path())
  263. }
  264. func appendOpArg(path []byte, op byte, arg int) []byte {
  265. path = append(path, op)
  266. path = strconv.AppendInt(path, int64(arg), 10)
  267. return path
  268. }
  269. // find finds obj within type T, returning the path to it, or nil if not found.
  270. func find(obj types.Object, T types.Type, path []byte) []byte {
  271. switch T := T.(type) {
  272. case *types.Basic, *types.Named:
  273. // Named types belonging to pkg were handled already,
  274. // so T must belong to another package. No path.
  275. return nil
  276. case *types.Pointer:
  277. return find(obj, T.Elem(), append(path, opElem))
  278. case *types.Slice:
  279. return find(obj, T.Elem(), append(path, opElem))
  280. case *types.Array:
  281. return find(obj, T.Elem(), append(path, opElem))
  282. case *types.Chan:
  283. return find(obj, T.Elem(), append(path, opElem))
  284. case *types.Map:
  285. if r := find(obj, T.Key(), append(path, opKey)); r != nil {
  286. return r
  287. }
  288. return find(obj, T.Elem(), append(path, opElem))
  289. case *types.Signature:
  290. if r := find(obj, T.Params(), append(path, opParams)); r != nil {
  291. return r
  292. }
  293. return find(obj, T.Results(), append(path, opResults))
  294. case *types.Struct:
  295. for i := 0; i < T.NumFields(); i++ {
  296. f := T.Field(i)
  297. path2 := appendOpArg(path, opField, i)
  298. if f == obj {
  299. return path2 // found field var
  300. }
  301. if r := find(obj, f.Type(), append(path2, opType)); r != nil {
  302. return r
  303. }
  304. }
  305. return nil
  306. case *types.Tuple:
  307. for i := 0; i < T.Len(); i++ {
  308. v := T.At(i)
  309. path2 := appendOpArg(path, opAt, i)
  310. if v == obj {
  311. return path2 // found param/result var
  312. }
  313. if r := find(obj, v.Type(), append(path2, opType)); r != nil {
  314. return r
  315. }
  316. }
  317. return nil
  318. case *types.Interface:
  319. for i := 0; i < T.NumMethods(); i++ {
  320. m := T.Method(i)
  321. path2 := appendOpArg(path, opMethod, i)
  322. if m == obj {
  323. return path2 // found interface method
  324. }
  325. if r := find(obj, m.Type(), append(path2, opType)); r != nil {
  326. return r
  327. }
  328. }
  329. return nil
  330. }
  331. panic(T)
  332. }
  333. // Object returns the object denoted by path p within the package pkg.
  334. func Object(pkg *types.Package, p Path) (types.Object, error) {
  335. if p == "" {
  336. return nil, fmt.Errorf("empty path")
  337. }
  338. pathstr := string(p)
  339. var pkgobj, suffix string
  340. if dot := strings.IndexByte(pathstr, opType); dot < 0 {
  341. pkgobj = pathstr
  342. } else {
  343. pkgobj = pathstr[:dot]
  344. suffix = pathstr[dot:] // suffix starts with "."
  345. }
  346. obj := pkg.Scope().Lookup(pkgobj)
  347. if obj == nil {
  348. return nil, fmt.Errorf("package %s does not contain %q", pkg.Path(), pkgobj)
  349. }
  350. // abstraction of *types.{Pointer,Slice,Array,Chan,Map}
  351. type hasElem interface {
  352. Elem() types.Type
  353. }
  354. // abstraction of *types.{Interface,Named}
  355. type hasMethods interface {
  356. Method(int) *types.Func
  357. NumMethods() int
  358. }
  359. // The loop state is the pair (t, obj),
  360. // exactly one of which is non-nil, initially obj.
  361. // All suffixes start with '.' (the only object->type operation),
  362. // followed by optional type->type operations,
  363. // then a type->object operation.
  364. // The cycle then repeats.
  365. var t types.Type
  366. for suffix != "" {
  367. code := suffix[0]
  368. suffix = suffix[1:]
  369. // Codes [AFM] have an integer operand.
  370. var index int
  371. switch code {
  372. case opAt, opField, opMethod:
  373. rest := strings.TrimLeft(suffix, "0123456789")
  374. numerals := suffix[:len(suffix)-len(rest)]
  375. suffix = rest
  376. i, err := strconv.Atoi(numerals)
  377. if err != nil {
  378. return nil, fmt.Errorf("invalid path: bad numeric operand %q for code %q", numerals, code)
  379. }
  380. index = int(i)
  381. case opObj:
  382. // no operand
  383. default:
  384. // The suffix must end with a type->object operation.
  385. if suffix == "" {
  386. return nil, fmt.Errorf("invalid path: ends with %q, want [AFMO]", code)
  387. }
  388. }
  389. if code == opType {
  390. if t != nil {
  391. return nil, fmt.Errorf("invalid path: unexpected %q in type context", opType)
  392. }
  393. t = obj.Type()
  394. obj = nil
  395. continue
  396. }
  397. if t == nil {
  398. return nil, fmt.Errorf("invalid path: code %q in object context", code)
  399. }
  400. // Inv: t != nil, obj == nil
  401. switch code {
  402. case opElem:
  403. hasElem, ok := t.(hasElem) // Pointer, Slice, Array, Chan, Map
  404. if !ok {
  405. return nil, fmt.Errorf("cannot apply %q to %s (got %T, want pointer, slice, array, chan or map)", code, t, t)
  406. }
  407. t = hasElem.Elem()
  408. case opKey:
  409. mapType, ok := t.(*types.Map)
  410. if !ok {
  411. return nil, fmt.Errorf("cannot apply %q to %s (got %T, want map)", code, t, t)
  412. }
  413. t = mapType.Key()
  414. case opParams:
  415. sig, ok := t.(*types.Signature)
  416. if !ok {
  417. return nil, fmt.Errorf("cannot apply %q to %s (got %T, want signature)", code, t, t)
  418. }
  419. t = sig.Params()
  420. case opResults:
  421. sig, ok := t.(*types.Signature)
  422. if !ok {
  423. return nil, fmt.Errorf("cannot apply %q to %s (got %T, want signature)", code, t, t)
  424. }
  425. t = sig.Results()
  426. case opUnderlying:
  427. named, ok := t.(*types.Named)
  428. if !ok {
  429. return nil, fmt.Errorf("cannot apply %q to %s (got %s, want named)", code, t, t)
  430. }
  431. t = named.Underlying()
  432. case opAt:
  433. tuple, ok := t.(*types.Tuple)
  434. if !ok {
  435. return nil, fmt.Errorf("cannot apply %q to %s (got %s, want tuple)", code, t, t)
  436. }
  437. if n := tuple.Len(); index >= n {
  438. return nil, fmt.Errorf("tuple index %d out of range [0-%d)", index, n)
  439. }
  440. obj = tuple.At(index)
  441. t = nil
  442. case opField:
  443. structType, ok := t.(*types.Struct)
  444. if !ok {
  445. return nil, fmt.Errorf("cannot apply %q to %s (got %T, want struct)", code, t, t)
  446. }
  447. if n := structType.NumFields(); index >= n {
  448. return nil, fmt.Errorf("field index %d out of range [0-%d)", index, n)
  449. }
  450. obj = structType.Field(index)
  451. t = nil
  452. case opMethod:
  453. hasMethods, ok := t.(hasMethods) // Interface or Named
  454. if !ok {
  455. return nil, fmt.Errorf("cannot apply %q to %s (got %s, want interface or named)", code, t, t)
  456. }
  457. if n := hasMethods.NumMethods(); index >= n {
  458. return nil, fmt.Errorf("method index %d out of range [0-%d)", index, n)
  459. }
  460. obj = hasMethods.Method(index)
  461. t = nil
  462. case opObj:
  463. named, ok := t.(*types.Named)
  464. if !ok {
  465. return nil, fmt.Errorf("cannot apply %q to %s (got %s, want named)", code, t, t)
  466. }
  467. obj = named.Obj()
  468. t = nil
  469. default:
  470. return nil, fmt.Errorf("invalid path: unknown code %q", code)
  471. }
  472. }
  473. if obj.Pkg() != pkg {
  474. return nil, fmt.Errorf("path denotes %s, which belongs to a different package", obj)
  475. }
  476. return obj, nil // success
  477. }