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.

221 lines
6.5 KiB

  1. package pointer
  2. import (
  3. "errors"
  4. "fmt"
  5. "go/ast"
  6. "go/parser"
  7. "go/token"
  8. "go/types"
  9. "strconv"
  10. )
  11. // An extendedQuery represents a sequence of destructuring operations
  12. // applied to an ssa.Value (denoted by "x").
  13. type extendedQuery struct {
  14. ops []interface{}
  15. ptr *Pointer
  16. }
  17. // indexValue returns the value of an integer literal used as an
  18. // index.
  19. func indexValue(expr ast.Expr) (int, error) {
  20. lit, ok := expr.(*ast.BasicLit)
  21. if !ok {
  22. return 0, fmt.Errorf("non-integer index (%T)", expr)
  23. }
  24. if lit.Kind != token.INT {
  25. return 0, fmt.Errorf("non-integer index %s", lit.Value)
  26. }
  27. return strconv.Atoi(lit.Value)
  28. }
  29. // parseExtendedQuery parses and validates a destructuring Go
  30. // expression and returns the sequence of destructuring operations.
  31. // See parseDestructuringExpr for details.
  32. func parseExtendedQuery(typ types.Type, query string) ([]interface{}, types.Type, error) {
  33. expr, err := parser.ParseExpr(query)
  34. if err != nil {
  35. return nil, nil, err
  36. }
  37. ops, typ, err := destructuringOps(typ, expr)
  38. if err != nil {
  39. return nil, nil, err
  40. }
  41. if len(ops) == 0 {
  42. return nil, nil, errors.New("invalid query: must not be empty")
  43. }
  44. if ops[0] != "x" {
  45. return nil, nil, fmt.Errorf("invalid query: query operand must be named x")
  46. }
  47. if !CanPoint(typ) {
  48. return nil, nil, fmt.Errorf("query does not describe a pointer-like value: %s", typ)
  49. }
  50. return ops, typ, nil
  51. }
  52. // destructuringOps parses a Go expression consisting only of an
  53. // identifier "x", field selections, indexing, channel receives, load
  54. // operations and parens---for example: "<-(*x[i])[key]"--- and
  55. // returns the sequence of destructuring operations on x.
  56. func destructuringOps(typ types.Type, expr ast.Expr) ([]interface{}, types.Type, error) {
  57. switch expr := expr.(type) {
  58. case *ast.SelectorExpr:
  59. out, typ, err := destructuringOps(typ, expr.X)
  60. if err != nil {
  61. return nil, nil, err
  62. }
  63. var structT *types.Struct
  64. switch typ := typ.(type) {
  65. case *types.Pointer:
  66. var ok bool
  67. structT, ok = typ.Elem().Underlying().(*types.Struct)
  68. if !ok {
  69. return nil, nil, fmt.Errorf("cannot access field %s of pointer to type %s", expr.Sel.Name, typ.Elem())
  70. }
  71. out = append(out, "load")
  72. case *types.Struct:
  73. structT = typ
  74. default:
  75. return nil, nil, fmt.Errorf("cannot access field %s of type %s", expr.Sel.Name, typ)
  76. }
  77. for i := 0; i < structT.NumFields(); i++ {
  78. field := structT.Field(i)
  79. if field.Name() == expr.Sel.Name {
  80. out = append(out, "field", i)
  81. return out, field.Type().Underlying(), nil
  82. }
  83. }
  84. // TODO(dh): supporting embedding would need something like
  85. // types.LookupFieldOrMethod, but without taking package
  86. // boundaries into account, because we may want to access
  87. // unexported fields. If we were only interested in one level
  88. // of unexported name, we could determine the appropriate
  89. // package and run LookupFieldOrMethod with that. However, a
  90. // single query may want to cross multiple package boundaries,
  91. // and at this point it's not really worth the complexity.
  92. return nil, nil, fmt.Errorf("no field %s in %s (embedded fields must be resolved manually)", expr.Sel.Name, structT)
  93. case *ast.Ident:
  94. return []interface{}{expr.Name}, typ, nil
  95. case *ast.BasicLit:
  96. return []interface{}{expr.Value}, nil, nil
  97. case *ast.IndexExpr:
  98. out, typ, err := destructuringOps(typ, expr.X)
  99. if err != nil {
  100. return nil, nil, err
  101. }
  102. switch typ := typ.(type) {
  103. case *types.Array:
  104. out = append(out, "arrayelem")
  105. return out, typ.Elem().Underlying(), nil
  106. case *types.Slice:
  107. out = append(out, "sliceelem")
  108. return out, typ.Elem().Underlying(), nil
  109. case *types.Map:
  110. out = append(out, "mapelem")
  111. return out, typ.Elem().Underlying(), nil
  112. case *types.Tuple:
  113. out = append(out, "index")
  114. idx, err := indexValue(expr.Index)
  115. if err != nil {
  116. return nil, nil, err
  117. }
  118. out = append(out, idx)
  119. if idx >= typ.Len() || idx < 0 {
  120. return nil, nil, fmt.Errorf("tuple index %d out of bounds", idx)
  121. }
  122. return out, typ.At(idx).Type().Underlying(), nil
  123. default:
  124. return nil, nil, fmt.Errorf("cannot index type %s", typ)
  125. }
  126. case *ast.UnaryExpr:
  127. if expr.Op != token.ARROW {
  128. return nil, nil, fmt.Errorf("unsupported unary operator %s", expr.Op)
  129. }
  130. out, typ, err := destructuringOps(typ, expr.X)
  131. if err != nil {
  132. return nil, nil, err
  133. }
  134. ch, ok := typ.(*types.Chan)
  135. if !ok {
  136. return nil, nil, fmt.Errorf("cannot receive from value of type %s", typ)
  137. }
  138. out = append(out, "recv")
  139. return out, ch.Elem().Underlying(), err
  140. case *ast.ParenExpr:
  141. return destructuringOps(typ, expr.X)
  142. case *ast.StarExpr:
  143. out, typ, err := destructuringOps(typ, expr.X)
  144. if err != nil {
  145. return nil, nil, err
  146. }
  147. ptr, ok := typ.(*types.Pointer)
  148. if !ok {
  149. return nil, nil, fmt.Errorf("cannot dereference type %s", typ)
  150. }
  151. out = append(out, "load")
  152. return out, ptr.Elem().Underlying(), err
  153. default:
  154. return nil, nil, fmt.Errorf("unsupported expression %T", expr)
  155. }
  156. }
  157. func (a *analysis) evalExtendedQuery(t types.Type, id nodeid, ops []interface{}) (types.Type, nodeid) {
  158. pid := id
  159. // TODO(dh): we're allocating intermediary nodes each time
  160. // evalExtendedQuery is called. We should probably only generate
  161. // them once per (v, ops) pair.
  162. for i := 1; i < len(ops); i++ {
  163. var nid nodeid
  164. switch ops[i] {
  165. case "recv":
  166. t = t.(*types.Chan).Elem().Underlying()
  167. nid = a.addNodes(t, "query.extended")
  168. a.load(nid, pid, 0, a.sizeof(t))
  169. case "field":
  170. i++ // fetch field index
  171. tt := t.(*types.Struct)
  172. idx := ops[i].(int)
  173. offset := a.offsetOf(t, idx)
  174. t = tt.Field(idx).Type().Underlying()
  175. nid = a.addNodes(t, "query.extended")
  176. a.copy(nid, pid+nodeid(offset), a.sizeof(t))
  177. case "arrayelem":
  178. t = t.(*types.Array).Elem().Underlying()
  179. nid = a.addNodes(t, "query.extended")
  180. a.copy(nid, 1+pid, a.sizeof(t))
  181. case "sliceelem":
  182. t = t.(*types.Slice).Elem().Underlying()
  183. nid = a.addNodes(t, "query.extended")
  184. a.load(nid, pid, 1, a.sizeof(t))
  185. case "mapelem":
  186. tt := t.(*types.Map)
  187. t = tt.Elem()
  188. ksize := a.sizeof(tt.Key())
  189. vsize := a.sizeof(tt.Elem())
  190. nid = a.addNodes(t, "query.extended")
  191. a.load(nid, pid, ksize, vsize)
  192. case "index":
  193. i++ // fetch index
  194. tt := t.(*types.Tuple)
  195. idx := ops[i].(int)
  196. t = tt.At(idx).Type().Underlying()
  197. nid = a.addNodes(t, "query.extended")
  198. a.copy(nid, pid+nodeid(idx), a.sizeof(t))
  199. case "load":
  200. t = t.(*types.Pointer).Elem().Underlying()
  201. nid = a.addNodes(t, "query.extended")
  202. a.load(nid, pid, 0, a.sizeof(t))
  203. default:
  204. // shouldn't happen
  205. panic(fmt.Sprintf("unknown op %q", ops[i]))
  206. }
  207. pid = nid
  208. }
  209. return t, pid
  210. }