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  }