github.com/solo-io/cue@v0.4.7/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/solo-io/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 empty = &adt.Vertex{} 99 empty.UpdateStatus(adt.Finalized) 100 } 101 102 func visit(c *adt.OpContext, n *adt.Vertex, f VisitFunc, all, top bool) (err error) { 103 if c == nil { 104 panic("nil context") 105 } 106 v := visitor{ 107 ctxt: c, 108 visit: f, 109 node: n, 110 all: all, 111 top: top, 112 } 113 114 defer func() { 115 switch x := recover(); x { 116 case nil: 117 case aborted: 118 err = v.err 119 default: 120 panic(x) 121 } 122 }() 123 124 for _, x := range n.Conjuncts { 125 v.markExpr(x.Env, x.Expr()) 126 } 127 128 return nil 129 } 130 131 var aborted = errors.New("aborted") 132 133 type visitor struct { 134 ctxt *adt.OpContext 135 visit VisitFunc 136 node *adt.Vertex 137 err error 138 all bool 139 top bool 140 } 141 142 // TODO: factor out the below logic as either a low-level dependency analyzer or 143 // some walk functionality. 144 145 // markExpr visits all nodes in an expression to mark dependencies. 146 func (c *visitor) markExpr(env *adt.Environment, expr adt.Expr) { 147 switch x := expr.(type) { 148 case nil: 149 case adt.Resolver: 150 c.markResolver(env, x) 151 152 case *adt.BinaryExpr: 153 c.markExpr(env, x.X) 154 c.markExpr(env, x.Y) 155 156 case *adt.UnaryExpr: 157 c.markExpr(env, x.X) 158 159 case *adt.Interpolation: 160 for i := 1; i < len(x.Parts); i += 2 { 161 c.markExpr(env, x.Parts[i]) 162 } 163 164 case *adt.BoundExpr: 165 c.markExpr(env, x.Expr) 166 167 case *adt.CallExpr: 168 c.markExpr(env, x.Fun) 169 saved := c.all 170 c.all = true 171 for _, a := range x.Args { 172 c.markExpr(env, a) 173 } 174 c.all = saved 175 176 case *adt.DisjunctionExpr: 177 for _, d := range x.Values { 178 c.markExpr(env, d.Val) 179 } 180 181 case *adt.SliceExpr: 182 c.markExpr(env, x.X) 183 c.markExpr(env, x.Lo) 184 c.markExpr(env, x.Hi) 185 c.markExpr(env, x.Stride) 186 187 case *adt.ListLit: 188 for _, e := range x.Elems { 189 switch x := e.(type) { 190 case adt.Yielder: 191 c.markYielder(env, x) 192 193 case adt.Expr: 194 c.markSubExpr(env, x) 195 196 case *adt.Ellipsis: 197 if x.Value != nil { 198 c.markSubExpr(env, x.Value) 199 } 200 } 201 } 202 203 case *adt.StructLit: 204 env := &adt.Environment{Up: env, Vertex: empty} 205 for _, e := range x.Decls { 206 c.markDecl(env, e) 207 } 208 } 209 } 210 211 // markResolve resolves dependencies. 212 func (c *visitor) markResolver(env *adt.Environment, r adt.Resolver) { 213 switch x := r.(type) { 214 case nil: 215 case *adt.LetReference: 216 saved := c.ctxt.PushState(env, nil) 217 env := c.ctxt.Env(x.UpCount) 218 c.markExpr(env, x.X) 219 c.ctxt.PopState(saved) 220 return 221 } 222 223 if ref, _ := c.ctxt.Resolve(env, r); ref != nil { 224 if ref != c.node { 225 d := Dependency{ 226 Node: ref, 227 Reference: r, 228 top: c.top, 229 } 230 if err := c.visit(d); err != nil { 231 c.err = err 232 panic(aborted) 233 } 234 } 235 236 return 237 } 238 239 // It is possible that a reference cannot be resolved because it is 240 // incomplete. In this case, we should check whether subexpressions of the 241 // reference can be resolved to mark those dependencies. For instance, 242 // prefix paths of selectors and the value or index of an index experssion 243 // may independently resolve to a valid dependency. 244 245 switch x := r.(type) { 246 case *adt.NodeLink: 247 panic("unreachable") 248 249 case *adt.IndexExpr: 250 c.markExpr(env, x.X) 251 c.markExpr(env, x.Index) 252 253 case *adt.SelectorExpr: 254 c.markExpr(env, x.X) 255 } 256 } 257 258 func (c *visitor) markSubExpr(env *adt.Environment, x adt.Expr) { 259 if c.all { 260 saved := c.top 261 c.top = false 262 c.markExpr(env, x) 263 c.top = saved 264 } 265 } 266 267 func (c *visitor) markDecl(env *adt.Environment, d adt.Decl) { 268 switch x := d.(type) { 269 case *adt.Field: 270 c.markSubExpr(env, x.Value) 271 272 case *adt.OptionalField: 273 // when dynamic, only continue if there is evidence of 274 // the field in the parallel actual evaluation. 275 c.markSubExpr(env, x.Value) 276 277 case *adt.BulkOptionalField: 278 c.markExpr(env, x.Filter) 279 // when dynamic, only continue if there is evidence of 280 // the field in the parallel actual evaluation. 281 c.markSubExpr(env, x.Value) 282 283 case *adt.DynamicField: 284 c.markExpr(env, x.Key) 285 // when dynamic, only continue if there is evidence of 286 // a matching field in the parallel actual evaluation. 287 c.markSubExpr(env, x.Value) 288 289 case adt.Yielder: 290 c.markYielder(env, x) 291 292 case adt.Expr: 293 c.markExpr(env, x) 294 295 case *adt.Ellipsis: 296 if x.Value != nil { 297 c.markSubExpr(env, x.Value) 298 } 299 } 300 } 301 302 func (c *visitor) markYielder(env *adt.Environment, y adt.Yielder) { 303 switch x := y.(type) { 304 case *adt.ForClause: 305 c.markExpr(env, x.Src) 306 env := &adt.Environment{Up: env, Vertex: empty} 307 c.markYielder(env, x.Dst) 308 // In dynamic mode, iterate over all actual value and 309 // evaluate. 310 311 case *adt.LetClause: 312 c.markExpr(env, x.Expr) 313 env := &adt.Environment{Up: env, Vertex: empty} 314 c.markYielder(env, x.Dst) 315 316 case *adt.IfClause: 317 c.markExpr(env, x.Condition) 318 // In dynamic mode, only continue if condition is true. 319 c.markYielder(env, x.Dst) 320 321 case *adt.ValueClause: 322 c.markExpr(env, x.StructLit) 323 } 324 }