lookup_sig.mx raw

   1  package iskra
   2  
   3  import "bytes"
   4  
   5  // FindBySignature scans all meta entries at the given stage using the
   6  // 24-bit signature hash for fast rejection, then verifying with full
   7  // IsIsomorphic on the content. Returns recIdxs of matching entries.
   8  func FindBySignature(t *Tree, sig *BindingSignature, stage uint8) []uint32 {
   9  	targetHash := SignatureHash24(sig)
  10  	var matches []uint32
  11  
  12  	for recIdx := range t.RecMeta {
  13  		meta := &t.RecMeta[recIdx]
  14  		if meta.StageTag != stage {
  15  			continue
  16  		}
  17  		if meta.SigHash() != targetHash {
  18  			continue
  19  		}
  20  		content := t.GetContent(uint32(recIdx))
  21  		if len(content) == 0 {
  22  			continue
  23  		}
  24  		cst := ExtractSymbols(string(content))
  25  		candidateSig := SignatureWithTypes(cst)
  26  		if sig.IsIsomorphic(candidateSig) {
  27  			matches = append(matches, uint32(recIdx))
  28  		}
  29  	}
  30  	return matches
  31  }
  32  
  33  // FindBySignatureFromAST extracts symbols from an AST dump and finds
  34  // matching entries in the lattice. Convenience wrapper.
  35  func FindBySignatureFromAST(t *Tree, astDump string) []uint32 {
  36  	st := ExtractSymbols(astDump)
  37  	sig := SignatureWithTypes(st)
  38  	return FindBySignature(t, sig, StageAST)
  39  }
  40  
  41  // FindBySignatureInPkg is like FindBySignature but only considers entries
  42  // whose IR representation (at the same name hash, StageIR) contains pkgPrefix.
  43  // Cross-stage adjacency is now key-implicit: same hash, different stage prefix.
  44  func FindBySignatureInPkg(t *Tree, sig *BindingSignature, stage uint8, pkgPrefix string) []uint32 {
  45  	targetHash := SignatureHash24(sig)
  46  	prefix := []byte(pkgPrefix)
  47  	var matches []uint32
  48  
  49  	for recIdx := range t.RecMeta {
  50  		meta := &t.RecMeta[recIdx]
  51  		if meta.StageTag != stage {
  52  			continue
  53  		}
  54  		if meta.SigHash() != targetHash {
  55  			continue
  56  		}
  57  		if len(pkgPrefix) > 0 {
  58  			// Look up IR version of this entry: same name hash, StageIR, same branch.
  59  			key, ok := t.db.RecKey[uint32(recIdx)]
  60  			if !ok {
  61  				continue
  62  			}
  63  			branch := KindToBranch(meta.Kind)
  64  			irKey := MakeCodeKey(StageIR, KeyHash(key))
  65  			irRecIdx := t.db.LookupRecIdx(branch, irKey)
  66  			if irRecIdx == NullLatticeRec {
  67  				continue
  68  			}
  69  			ir := t.GetContent(irRecIdx)
  70  			if !bytes.Contains(ir, prefix) {
  71  				continue
  72  			}
  73  		}
  74  		content := t.GetContent(uint32(recIdx))
  75  		if len(content) == 0 {
  76  			continue
  77  		}
  78  		cst := ExtractSymbols(string(content))
  79  		candidateSig := SignatureWithTypes(cst)
  80  		if sig.IsIsomorphic(candidateSig) {
  81  			matches = append(matches, uint32(recIdx))
  82  		}
  83  	}
  84  	return matches
  85  }
  86  
  87  func FindBySignatureFromASTInPkg(t *Tree, astDump string, pkgPrefix string) []uint32 {
  88  	st := ExtractSymbols(astDump)
  89  	sig := SignatureWithTypes(st)
  90  	return FindBySignatureInPkg(t, sig, StageAST, pkgPrefix)
  91  }
  92  
  93  // RankMatches reorders matches: self first, then name match, then lowest cost.
  94  func RankMatches(t *Tree, matches []uint32, selfIdx uint32, queryName string) {
  95  	if len(matches) < 2 {
  96  		return
  97  	}
  98  	qMethod := unqualifiedName(queryName)
  99  
 100  	for j, mi := range matches {
 101  		if mi == selfIdx && j != 0 {
 102  			matches[0], matches[j] = matches[j], matches[0]
 103  			return
 104  		}
 105  	}
 106  	if matches[0] == selfIdx {
 107  		return
 108  	}
 109  
 110  	for j := 0; j < len(matches); j++ {
 111  		rec := t.db.GetRecord(matches[j])
 112  		if rec == nil {
 113  			continue
 114  		}
 115  		mName := FormFromRecord(rec, t.StringPool)
 116  		if unqualifiedName(mName) == qMethod {
 117  			if j != 0 {
 118  				matches[0], matches[j] = matches[j], matches[0]
 119  			}
 120  			return
 121  		}
 122  	}
 123  
 124  	RankByCost(t, matches)
 125  }
 126  
 127  // RankByCost sorts matches by ascending CostMap cost.
 128  func RankByCost(t *Tree, matches []uint32) {
 129  	if len(t.CostMap) == 0 || len(matches) < 2 {
 130  		return
 131  	}
 132  	for i := 1; i < len(matches); i++ {
 133  		key := matches[i]
 134  		kCost, kHas := t.CostMap[key]
 135  		j := i - 1
 136  		for j >= 0 {
 137  			jCost, jHas := t.CostMap[matches[j]]
 138  			if !costLess(kCost, kHas, jCost, jHas) {
 139  				break
 140  			}
 141  			matches[j+1] = matches[j]
 142  			j--
 143  		}
 144  		matches[j+1] = key
 145  	}
 146  }
 147  
 148  func costLess(a CostEntry, aHas bool, b CostEntry, bHas bool) bool {
 149  	if aHas && !bHas {
 150  		return true
 151  	}
 152  	if !aHas {
 153  		return false
 154  	}
 155  	return a.NsOp100 < b.NsOp100
 156  }
 157  
 158  func unqualifiedName(name string) string {
 159  	for i := len(name) - 1; i >= 0; i-- {
 160  		if name[i] == '.' {
 161  			return name[i+1:]
 162  		}
 163  	}
 164  	return name
 165  }
 166