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 "" }