mirror of
https://github.com/42wim/matterbridge.git
synced 2024-11-23 11:12:02 -08:00
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 ""
|
||
|
}
|