github.com/joomcode/cue@v0.4.4-0.20221111115225-539fe3512047/internal/core/dep/dep.go (about) 1 // Copyright 2020 CUE Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package dep analyzes dependencies between values. 16 package dep 17 18 import ( 19 "errors" 20 21 "github.com/joomcode/cue/internal/core/adt" 22 ) 23 24 // A Dependency is a reference and the node that reference resolves to. 25 type Dependency struct { 26 // Node is the referenced node. 27 Node *adt.Vertex 28 29 // Reference is the expression that referenced the node. 30 Reference adt.Resolver 31 32 top bool 33 } 34 35 // Import returns the import reference or nil if the reference was within 36 // the same package as the visited Vertex. 37 func (d *Dependency) Import() *adt.ImportReference { 38 x, _ := d.Reference.(adt.Expr) 39 return importRef(x) 40 } 41 42 // IsRoot reports whether the dependency is referenced by the root of the 43 // original Vertex passed to any of the Visit* functions, and not one of its 44 // descendent arcs. This always returns true for Visit(). 45 func (d *Dependency) IsRoot() bool { 46 return d.top 47 } 48 49 func (d *Dependency) Path() []adt.Feature { 50 return nil 51 } 52 53 func importRef(r adt.Expr) *adt.ImportReference { 54 switch x := r.(type) { 55 case *adt.ImportReference: 56 return x 57 case *adt.SelectorExpr: 58 return importRef(x.X) 59 case *adt.IndexExpr: 60 return importRef(x.X) 61 } 62 return nil 63 } 64 65 // VisitFunc is used for reporting dependencies. 66 type VisitFunc func(Dependency) error 67 68 // Visit calls f for all vertices referenced by the conjuncts of n without 69 // descending into the elements of list or fields of structs. Only references 70 // that do not refer to the conjuncts of n itself are reported. 71 func Visit(c *adt.OpContext, n *adt.Vertex, f VisitFunc) error { 72 return visit(c, n, f, false, true) 73 } 74 75 // VisitAll calls f for all vertices referenced by the conjuncts of n including 76 // those of descendant fields and elements. Only references that do not refer to 77 // the conjuncts of n itself are reported. 78 func VisitAll(c *adt.OpContext, n *adt.Vertex, f VisitFunc) error { 79 return visit(c, n, f, true, true) 80 } 81 82 // VisitFields calls f for n and all its descendent arcs that have a conjunct 83 // that originates from a conjunct in n. Only the conjuncts of n that ended up 84 // as a conjunct in an actual field are visited and they are visited for each 85 // field in which the occurs. 86 func VisitFields(c *adt.OpContext, n *adt.Vertex, f VisitFunc) error { 87 m := marked{} 88 89 m.markExpr(n) 90 91 dynamic(c, n, f, m, true) 92 return nil 93 } 94 95 var empty *adt.Vertex 96 97 func init() { 98 // TODO: Consider setting a non-nil BaseValue. 99 empty = &adt.Vertex{} 100 empty.UpdateStatus(adt.Finalized) 101 } 102 103 func visit(c *adt.OpContext, n *adt.Vertex, f VisitFunc, all, top bool) (err error) { 104 if c == nil { 105 panic("nil context") 106 } 107 v := visitor{ 108 ctxt: c, 109 visit: f, 110 node: n, 111 all: all, 112 top: top, 113 } 114 115 defer func() { 116 switch x := recover(); x { 117 case nil: 118 case aborted: 119 err = v.err 120 default: 121 panic(x) 122 } 123 }() 124 125 for _, x := range n.Conjuncts { 126 v.markExpr(x.Env, x.Elem()) 127 } 128 129 return nil 130 } 131 132 var aborted = errors.New("aborted") 133 134 type visitor struct { 135 ctxt *adt.OpContext 136 visit VisitFunc 137 node *adt.Vertex 138 err error 139 all bool 140 top bool 141 } 142 143 // TODO: factor out the below logic as either a low-level dependency analyzer or 144 // some walk functionality. 145 146 // markExpr visits all nodes in an expression to mark dependencies. 147 func (c *visitor) markExpr(env *adt.Environment, expr adt.Elem) { 148 switch x := expr.(type) { 149 case nil: 150 case adt.Resolver: 151 c.markResolver(env, x) 152 153 case *adt.BinaryExpr: 154 c.markExpr(env, x.X) 155 c.markExpr(env, x.Y) 156 157 case *adt.UnaryExpr: 158 c.markExpr(env, x.X) 159 160 case *adt.Interpolation: 161 for i := 1; i < len(x.Parts); i += 2 { 162 c.markExpr(env, x.Parts[i]) 163 } 164 165 case *adt.BoundExpr: 166 c.markExpr(env, x.Expr) 167 168 case *adt.CallExpr: 169 c.markExpr(env, x.Fun) 170 saved := c.all 171 c.all = true 172 for _, a := range x.Args { 173 c.markExpr(env, a) 174 } 175 c.all = saved 176 177 case *adt.DisjunctionExpr: 178 for _, d := range x.Values { 179 c.markExpr(env, d.Val) 180 } 181 182 case *adt.SliceExpr: 183 c.markExpr(env, x.X) 184 c.markExpr(env, x.Lo) 185 c.markExpr(env, x.Hi) 186 c.markExpr(env, x.Stride) 187 188 case *adt.ListLit: 189 env := &adt.Environment{Up: env, Vertex: empty} 190 for _, e := range x.Elems { 191 switch x := e.(type) { 192 case *adt.Comprehension: 193 c.markComprehension(env, x) 194 195 case adt.Expr: 196 c.markSubExpr(env, x) 197 198 case *adt.Ellipsis: 199 if x.Value != nil { 200 c.markSubExpr(env, x.Value) 201 } 202 } 203 } 204 205 case *adt.StructLit: 206 env := &adt.Environment{Up: env, Vertex: empty} 207 for _, e := range x.Decls { 208 c.markDecl(env, e) 209 } 210 } 211 } 212 213 // markResolve resolves dependencies. 214 func (c *visitor) markResolver(env *adt.Environment, r adt.Resolver) { 215 switch x := r.(type) { 216 case nil: 217 case *adt.LetReference: 218 saved := c.ctxt.PushState(env, nil) 219 env := c.ctxt.Env(x.UpCount) 220 c.markExpr(env, x.X) 221 c.ctxt.PopState(saved) 222 return 223 } 224 225 if ref, _ := c.ctxt.Resolve(env, r); ref != nil { 226 if ref != c.node && ref != empty { 227 d := Dependency{ 228 Node: ref, 229 Reference: r, 230 top: c.top, 231 } 232 if err := c.visit(d); err != nil { 233 c.err = err 234 panic(aborted) 235 } 236 } 237 238 return 239 } 240 241 // It is possible that a reference cannot be resolved because it is 242 // incomplete. In this case, we should check whether subexpressions of the 243 // reference can be resolved to mark those dependencies. For instance, 244 // prefix paths of selectors and the value or index of an index experssion 245 // may independently resolve to a valid dependency. 246 247 switch x := r.(type) { 248 case *adt.NodeLink: 249 panic("unreachable") 250 251 case *adt.IndexExpr: 252 c.markExpr(env, x.X) 253 c.markExpr(env, x.Index) 254 255 case *adt.SelectorExpr: 256 c.markExpr(env, x.X) 257 } 258 } 259 260 func (c *visitor) markSubExpr(env *adt.Environment, x adt.Expr) { 261 if c.all { 262 saved := c.top 263 c.top = false 264 c.markExpr(env, x) 265 c.top = saved 266 } 267 } 268 269 func (c *visitor) markDecl(env *adt.Environment, d adt.Decl) { 270 switch x := d.(type) { 271 case *adt.Field: 272 c.markSubExpr(env, x.Value) 273 274 case *adt.OptionalField: 275 // when dynamic, only continue if there is evidence of 276 // the field in the parallel actual evaluation. 277 c.markSubExpr(env, x.Value) 278 279 case *adt.BulkOptionalField: 280 c.markExpr(env, x.Filter) 281 // when dynamic, only continue if there is evidence of 282 // the field in the parallel actual evaluation. 283 c.markSubExpr(env, x.Value) 284 285 case *adt.DynamicField: 286 c.markExpr(env, x.Key) 287 // when dynamic, only continue if there is evidence of 288 // a matching field in the parallel actual evaluation. 289 c.markSubExpr(env, x.Value) 290 291 case *adt.Comprehension: 292 c.markComprehension(env, x) 293 294 case adt.Expr: 295 c.markExpr(env, x) 296 297 case *adt.Ellipsis: 298 if x.Value != nil { 299 c.markSubExpr(env, x.Value) 300 } 301 } 302 } 303 304 func (c *visitor) markComprehension(env *adt.Environment, y *adt.Comprehension) { 305 env = c.markYielder(env, y.Clauses) 306 c.markExpr(env, y.Value) 307 } 308 309 func (c *visitor) markYielder(env *adt.Environment, y adt.Yielder) *adt.Environment { 310 switch x := y.(type) { 311 case *adt.ForClause: 312 c.markExpr(env, x.Src) 313 env = &adt.Environment{Up: env, Vertex: empty} 314 env = c.markYielder(env, x.Dst) 315 // In dynamic mode, iterate over all actual value and 316 // evaluate. 317 318 case *adt.LetClause: 319 c.markExpr(env, x.Expr) 320 env = &adt.Environment{Up: env, Vertex: empty} 321 env = c.markYielder(env, x.Dst) 322 323 case *adt.IfClause: 324 c.markExpr(env, x.Condition) 325 // In dynamic mode, only continue if condition is true. 326 env = c.markYielder(env, x.Dst) 327 } 328 return env 329 }