kythe.io@v0.0.68-0.20240422202219-7225dbc01741/kythe/go/util/markedsource/resolve.go (about)

     1  /*
     2   * Copyright 2023 The Kythe Authors. All rights reserved.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *   http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package markedsource
    18  
    19  import (
    20  	"fmt"
    21  
    22  	"kythe.io/kythe/go/util/kytheuri"
    23  	"kythe.io/kythe/go/util/schema/edges"
    24  	"kythe.io/kythe/go/util/schema/facts"
    25  
    26  	"google.golang.org/protobuf/encoding/protojson"
    27  	"google.golang.org/protobuf/proto"
    28  
    29  	cpb "kythe.io/kythe/proto/common_go_proto"
    30  	spb "kythe.io/kythe/proto/storage_go_proto"
    31  )
    32  
    33  // Resolver produces fully resolved MarkedSources from a set of entries.
    34  type Resolver struct {
    35  	nodes map[string]*cpb.MarkedSource
    36  
    37  	params  map[string][]string
    38  	tparams map[string][]string
    39  	typed   map[string]string
    40  }
    41  
    42  // NewResolver constructs a MarkedSource resolver from the given entries.
    43  func NewResolver(entries []*spb.Entry) (*Resolver, error) {
    44  	r := &Resolver{
    45  		nodes:   make(map[string]*cpb.MarkedSource),
    46  		params:  make(map[string][]string),
    47  		tparams: make(map[string][]string),
    48  		typed:   make(map[string]string),
    49  	}
    50  	for _, e := range entries {
    51  		if e.GetFactName() == facts.Code {
    52  			ticket := kytheuri.ToString(e.GetSource())
    53  			var ms cpb.MarkedSource
    54  			if err := proto.Unmarshal(e.GetFactValue(), &ms); err != nil {
    55  				return nil, fmt.Errorf("error unmarshalling code for %s: %v", ticket, err)
    56  			}
    57  			r.nodes[ticket] = &ms
    58  		} else if e.GetFactName() == facts.Code+"/json" {
    59  			ticket := kytheuri.ToString(e.GetSource())
    60  			var ms cpb.MarkedSource
    61  			if err := protojson.Unmarshal(e.GetFactValue(), &ms); err != nil {
    62  				return nil, fmt.Errorf("error unmarshalling code/json for %s: %v", ticket, err)
    63  			}
    64  			r.nodes[ticket] = &ms
    65  		} else if e.GetEdgeKind() != "" {
    66  			ticket := kytheuri.ToString(e.GetSource())
    67  			kind, ord, _ := edges.ParseOrdinal(e.GetEdgeKind())
    68  			if ord < 0 {
    69  				return nil, fmt.Errorf("invalid ordinal: %d", ord)
    70  			}
    71  			switch kind {
    72  			case edges.Typed:
    73  				r.typed[ticket] = kytheuri.ToString(e.GetTarget())
    74  			case edges.Param:
    75  				params := r.params[ticket]
    76  				if len(params)-1 < ord {
    77  					n := make([]string, ord+1)
    78  					copy(n, params)
    79  					params = n
    80  					r.params[ticket] = params
    81  				}
    82  				params[ord] = kytheuri.ToString(e.GetTarget())
    83  			case edges.TParam:
    84  				tparams := r.tparams[ticket]
    85  				if len(tparams)-1 < ord {
    86  					n := make([]string, ord+1)
    87  					copy(n, tparams)
    88  					tparams = n
    89  					r.tparams[ticket] = tparams
    90  				}
    91  				tparams[ord] = kytheuri.ToString(e.GetTarget())
    92  			}
    93  		}
    94  	}
    95  	return r, nil
    96  }
    97  
    98  // Resolve returns the fully resolved MarkedSource for the given source VName.
    99  // May return nil if no MarkedSource is found.
   100  func (r *Resolver) Resolve(src *spb.VName) *cpb.MarkedSource {
   101  	return r.ResolveTicket(kytheuri.ToString(src))
   102  }
   103  
   104  // ResolveTicket returns the fully resolved MarkedSource for the given source ticket.
   105  // May return nil if no MarkedSource is found.
   106  func (r *Resolver) ResolveTicket(ticket string) *cpb.MarkedSource {
   107  	if ticket == "" {
   108  		return nil
   109  	}
   110  	return r.resolve(ticket, r.nodes[ticket])
   111  }
   112  
   113  func ensureKind(ms *cpb.MarkedSource, k cpb.MarkedSource_Kind) *cpb.MarkedSource {
   114  	if ms.GetKind() == k {
   115  		return ms
   116  	}
   117  	if ms != nil && ms.GetKind() == cpb.MarkedSource_BOX {
   118  		ret := proto.Clone(ms).(*cpb.MarkedSource)
   119  		ret.Kind = k
   120  		return ret
   121  	}
   122  	return &cpb.MarkedSource{
   123  		Kind:  k,
   124  		Child: []*cpb.MarkedSource{ms},
   125  	}
   126  }
   127  
   128  func removeExcluded(kind cpb.MarkedSource_Kind, ms *cpb.MarkedSource) *cpb.MarkedSource {
   129  	for _, k := range ms.GetExcludeOnInclude() {
   130  		if k == kind {
   131  			return nil
   132  		}
   133  	}
   134  	if len(ms.GetChild()) == 0 {
   135  		return ms
   136  	}
   137  
   138  	children := make([]*cpb.MarkedSource, 0, len(ms.GetChild()))
   139  	for _, c := range ms.GetChild() {
   140  		if e := removeExcluded(kind, c); e != nil {
   141  			children = append(children, e)
   142  		}
   143  	}
   144  	res := proto.Clone(ms).(*cpb.MarkedSource)
   145  	res.Child = children
   146  	return res
   147  }
   148  
   149  var invalidLookupReplacement = &cpb.MarkedSource{PreText: "???"}
   150  
   151  func (r *Resolver) resolve(ticket string, ms *cpb.MarkedSource) *cpb.MarkedSource {
   152  	if ms == nil {
   153  		return ms
   154  	}
   155  	switch ms.GetKind() {
   156  	case cpb.MarkedSource_LOOKUP_BY_PARAM:
   157  		params := r.params[ticket]
   158  		if int(ms.GetLookupIndex()) >= len(params) {
   159  			return ensureKind(invalidLookupReplacement, cpb.MarkedSource_PARAMETER)
   160  		}
   161  		if p := params[ms.GetLookupIndex()]; p != "" {
   162  			return removeExcluded(ms.GetKind(), ensureKind(r.ResolveTicket(p), cpb.MarkedSource_PARAMETER))
   163  		}
   164  	case cpb.MarkedSource_LOOKUP_BY_TPARAM:
   165  		tparams := r.tparams[ticket]
   166  		if int(ms.GetLookupIndex()) >= len(tparams) {
   167  			return ensureKind(invalidLookupReplacement, cpb.MarkedSource_PARAMETER)
   168  		}
   169  		if p := tparams[ms.GetLookupIndex()]; p != "" {
   170  			return removeExcluded(ms.GetKind(), ensureKind(r.ResolveTicket(p), cpb.MarkedSource_PARAMETER))
   171  		}
   172  	case cpb.MarkedSource_LOOKUP_BY_TYPED:
   173  		return removeExcluded(ms.GetKind(), ensureKind(r.ResolveTicket(r.typed[ticket]), cpb.MarkedSource_TYPE))
   174  	case cpb.MarkedSource_PARAMETER_LOOKUP_BY_PARAM_WITH_DEFAULTS, cpb.MarkedSource_PARAMETER_LOOKUP_BY_PARAM:
   175  		// TODO: handle param/default
   176  		params := r.params[ticket]
   177  		if int(ms.GetLookupIndex()) > len(params) {
   178  			return ensureKind(invalidLookupReplacement, cpb.MarkedSource_PARAMETER)
   179  		}
   180  		return r.resolveParameters(ms, params[ms.GetLookupIndex():])
   181  	case cpb.MarkedSource_PARAMETER_LOOKUP_BY_TPARAM:
   182  		tparams := r.tparams[ticket]
   183  		if int(ms.GetLookupIndex()) > len(tparams) {
   184  			return ensureKind(invalidLookupReplacement, cpb.MarkedSource_PARAMETER)
   185  		}
   186  		return r.resolveParameters(ms, tparams[ms.GetLookupIndex():])
   187  	}
   188  	return r.resolveChildren(ticket, ms)
   189  }
   190  
   191  func (r *Resolver) resolveParameters(base *cpb.MarkedSource, params []string) *cpb.MarkedSource {
   192  	n := proto.Clone(base).(*cpb.MarkedSource)
   193  	n.LookupIndex = 0
   194  	n.Kind = cpb.MarkedSource_PARAMETER
   195  	n.Child = make([]*cpb.MarkedSource, 0, len(params))
   196  	for _, p := range params {
   197  		if c := removeExcluded(base.GetKind(), r.ResolveTicket(p)); c != nil {
   198  			n.Child = append(n.Child, c)
   199  		}
   200  	}
   201  	return n
   202  }
   203  
   204  func (r *Resolver) resolveChildren(ticket string, ms *cpb.MarkedSource) *cpb.MarkedSource {
   205  	n := proto.Clone(ms).(*cpb.MarkedSource)
   206  	children := make([]*cpb.MarkedSource, 0, len(n.GetChild()))
   207  	for _, ms := range n.GetChild() {
   208  		if c := r.resolve(ticket, ms); c != nil {
   209  			children = append(children, c)
   210  		}
   211  	}
   212  	n.Child = children
   213  	return n
   214  }