forked from lug/matterbridge
		
	
		
			
				
	
	
		
			130 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			130 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package compiler
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 
 | |
| 	"github.com/d5/tengo/objects"
 | |
| )
 | |
| 
 | |
| // RemoveDuplicates finds and remove the duplicate values in Constants.
 | |
| // Note this function mutates Bytecode.
 | |
| func (b *Bytecode) RemoveDuplicates() {
 | |
| 	var deduped []objects.Object
 | |
| 
 | |
| 	indexMap := make(map[int]int) // mapping from old constant index to new index
 | |
| 	ints := make(map[int64]int)
 | |
| 	strings := make(map[string]int)
 | |
| 	floats := make(map[float64]int)
 | |
| 	chars := make(map[rune]int)
 | |
| 	immutableMaps := make(map[string]int) // for modules
 | |
| 
 | |
| 	for curIdx, c := range b.Constants {
 | |
| 		switch c := c.(type) {
 | |
| 		case *objects.CompiledFunction:
 | |
| 			// add to deduped list
 | |
| 			indexMap[curIdx] = len(deduped)
 | |
| 			deduped = append(deduped, c)
 | |
| 		case *objects.ImmutableMap:
 | |
| 			modName := moduleName(c)
 | |
| 			newIdx, ok := immutableMaps[modName]
 | |
| 			if modName != "" && ok {
 | |
| 				indexMap[curIdx] = newIdx
 | |
| 			} else {
 | |
| 				newIdx = len(deduped)
 | |
| 				immutableMaps[modName] = newIdx
 | |
| 				indexMap[curIdx] = newIdx
 | |
| 				deduped = append(deduped, c)
 | |
| 			}
 | |
| 		case *objects.Int:
 | |
| 			if newIdx, ok := ints[c.Value]; ok {
 | |
| 				indexMap[curIdx] = newIdx
 | |
| 			} else {
 | |
| 				newIdx = len(deduped)
 | |
| 				ints[c.Value] = newIdx
 | |
| 				indexMap[curIdx] = newIdx
 | |
| 				deduped = append(deduped, c)
 | |
| 			}
 | |
| 		case *objects.String:
 | |
| 			if newIdx, ok := strings[c.Value]; ok {
 | |
| 				indexMap[curIdx] = newIdx
 | |
| 			} else {
 | |
| 				newIdx = len(deduped)
 | |
| 				strings[c.Value] = newIdx
 | |
| 				indexMap[curIdx] = newIdx
 | |
| 				deduped = append(deduped, c)
 | |
| 			}
 | |
| 		case *objects.Float:
 | |
| 			if newIdx, ok := floats[c.Value]; ok {
 | |
| 				indexMap[curIdx] = newIdx
 | |
| 			} else {
 | |
| 				newIdx = len(deduped)
 | |
| 				floats[c.Value] = newIdx
 | |
| 				indexMap[curIdx] = newIdx
 | |
| 				deduped = append(deduped, c)
 | |
| 			}
 | |
| 		case *objects.Char:
 | |
| 			if newIdx, ok := chars[c.Value]; ok {
 | |
| 				indexMap[curIdx] = newIdx
 | |
| 			} else {
 | |
| 				newIdx = len(deduped)
 | |
| 				chars[c.Value] = newIdx
 | |
| 				indexMap[curIdx] = newIdx
 | |
| 				deduped = append(deduped, c)
 | |
| 			}
 | |
| 		default:
 | |
| 			panic(fmt.Errorf("unsupported top-level constant type: %s", c.TypeName()))
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// replace with de-duplicated constants
 | |
| 	b.Constants = deduped
 | |
| 
 | |
| 	// update CONST instructions with new indexes
 | |
| 	// main function
 | |
| 	updateConstIndexes(b.MainFunction.Instructions, indexMap)
 | |
| 	// other compiled functions in constants
 | |
| 	for _, c := range b.Constants {
 | |
| 		switch c := c.(type) {
 | |
| 		case *objects.CompiledFunction:
 | |
| 			updateConstIndexes(c.Instructions, indexMap)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func updateConstIndexes(insts []byte, indexMap map[int]int) {
 | |
| 	i := 0
 | |
| 	for i < len(insts) {
 | |
| 		op := insts[i]
 | |
| 		numOperands := OpcodeOperands[op]
 | |
| 		_, read := ReadOperands(numOperands, insts[i+1:])
 | |
| 
 | |
| 		switch op {
 | |
| 		case OpConstant:
 | |
| 			curIdx := int(insts[i+2]) | int(insts[i+1])<<8
 | |
| 			newIdx, ok := indexMap[curIdx]
 | |
| 			if !ok {
 | |
| 				panic(fmt.Errorf("constant index not found: %d", curIdx))
 | |
| 			}
 | |
| 			copy(insts[i:], MakeInstruction(op, newIdx))
 | |
| 		case OpClosure:
 | |
| 			curIdx := int(insts[i+2]) | int(insts[i+1])<<8
 | |
| 			numFree := int(insts[i+3])
 | |
| 			newIdx, ok := indexMap[curIdx]
 | |
| 			if !ok {
 | |
| 				panic(fmt.Errorf("constant index not found: %d", curIdx))
 | |
| 			}
 | |
| 			copy(insts[i:], MakeInstruction(op, newIdx, numFree))
 | |
| 		}
 | |
| 
 | |
| 		i += 1 + read
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func moduleName(mod *objects.ImmutableMap) string {
 | |
| 	if modName, ok := mod.Value["__module_name__"].(*objects.String); ok {
 | |
| 		return modName.Value
 | |
| 	}
 | |
| 
 | |
| 	return ""
 | |
| }
 | 
