diff options
Diffstat (limited to 'vendor/github.com/d5/tengo/v2/bytecode.go')
-rw-r--r-- | vendor/github.com/d5/tengo/v2/bytecode.go | 292 |
1 files changed, 292 insertions, 0 deletions
diff --git a/vendor/github.com/d5/tengo/v2/bytecode.go b/vendor/github.com/d5/tengo/v2/bytecode.go new file mode 100644 index 00000000..cfd0d0b5 --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/bytecode.go @@ -0,0 +1,292 @@ +package tengo + +import ( + "encoding/gob" + "fmt" + "io" + "reflect" + + "github.com/d5/tengo/v2/parser" +) + +// Bytecode is a compiled instructions and constants. +type Bytecode struct { + FileSet *parser.SourceFileSet + MainFunction *CompiledFunction + Constants []Object +} + +// Encode writes Bytecode data to the writer. +func (b *Bytecode) Encode(w io.Writer) error { + enc := gob.NewEncoder(w) + if err := enc.Encode(b.FileSet); err != nil { + return err + } + if err := enc.Encode(b.MainFunction); err != nil { + return err + } + return enc.Encode(b.Constants) +} + +// CountObjects returns the number of objects found in Constants. +func (b *Bytecode) CountObjects() int { + n := 0 + for _, c := range b.Constants { + n += CountObjects(c) + } + return n +} + +// FormatInstructions returns human readable string representations of +// compiled instructions. +func (b *Bytecode) FormatInstructions() []string { + return FormatInstructions(b.MainFunction.Instructions, 0) +} + +// FormatConstants returns human readable string representations of +// compiled constants. +func (b *Bytecode) FormatConstants() (output []string) { + for cidx, cn := range b.Constants { + switch cn := cn.(type) { + case *CompiledFunction: + output = append(output, fmt.Sprintf( + "[% 3d] (Compiled Function|%p)", cidx, &cn)) + for _, l := range FormatInstructions(cn.Instructions, 0) { + output = append(output, fmt.Sprintf(" %s", l)) + } + default: + output = append(output, fmt.Sprintf("[% 3d] %s (%s|%p)", + cidx, cn, reflect.TypeOf(cn).Elem().Name(), &cn)) + } + } + return +} + +// Decode reads Bytecode data from the reader. +func (b *Bytecode) Decode(r io.Reader, modules *ModuleMap) error { + if modules == nil { + modules = NewModuleMap() + } + + dec := gob.NewDecoder(r) + if err := dec.Decode(&b.FileSet); err != nil { + return err + } + // TODO: files in b.FileSet.File does not have their 'set' field properly + // set to b.FileSet as it's private field and not serialized by gob + // encoder/decoder. + if err := dec.Decode(&b.MainFunction); err != nil { + return err + } + if err := dec.Decode(&b.Constants); err != nil { + return err + } + for i, v := range b.Constants { + fv, err := fixDecodedObject(v, modules) + if err != nil { + return err + } + b.Constants[i] = fv + } + return nil +} + +// RemoveDuplicates finds and remove the duplicate values in Constants. +// Note this function mutates Bytecode. +func (b *Bytecode) RemoveDuplicates() { + var deduped []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 *CompiledFunction: + // add to deduped list + indexMap[curIdx] = len(deduped) + deduped = append(deduped, c) + case *ImmutableMap: + modName := inferModuleName(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 *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 *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 *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 *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 *CompiledFunction: + updateConstIndexes(c.Instructions, indexMap) + } + } +} + +func fixDecodedObject( + o Object, + modules *ModuleMap, +) (Object, error) { + switch o := o.(type) { + case *Bool: + if o.IsFalsy() { + return FalseValue, nil + } + return TrueValue, nil + case *Undefined: + return UndefinedValue, nil + case *Array: + for i, v := range o.Value { + fv, err := fixDecodedObject(v, modules) + if err != nil { + return nil, err + } + o.Value[i] = fv + } + case *ImmutableArray: + for i, v := range o.Value { + fv, err := fixDecodedObject(v, modules) + if err != nil { + return nil, err + } + o.Value[i] = fv + } + case *Map: + for k, v := range o.Value { + fv, err := fixDecodedObject(v, modules) + if err != nil { + return nil, err + } + o.Value[k] = fv + } + case *ImmutableMap: + modName := inferModuleName(o) + if mod := modules.GetBuiltinModule(modName); mod != nil { + return mod.AsImmutableMap(modName), nil + } + + for k, v := range o.Value { + // encoding of user function not supported + if _, isUserFunction := v.(*UserFunction); isUserFunction { + return nil, fmt.Errorf("user function not decodable") + } + + fv, err := fixDecodedObject(v, modules) + if err != nil { + return nil, err + } + o.Value[k] = fv + } + } + return o, nil +} + +func updateConstIndexes(insts []byte, indexMap map[int]int) { + i := 0 + for i < len(insts) { + op := insts[i] + numOperands := parser.OpcodeOperands[op] + _, read := parser.ReadOperands(numOperands, insts[i+1:]) + + switch op { + case parser.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 parser.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 inferModuleName(mod *ImmutableMap) string { + if modName, ok := mod.Value["__module_name__"].(*String); ok { + return modName.Value + } + return "" +} + +func init() { + gob.Register(&parser.SourceFileSet{}) + gob.Register(&parser.SourceFile{}) + gob.Register(&Array{}) + gob.Register(&Bool{}) + gob.Register(&Bytes{}) + gob.Register(&Char{}) + gob.Register(&CompiledFunction{}) + gob.Register(&Error{}) + gob.Register(&Float{}) + gob.Register(&ImmutableArray{}) + gob.Register(&ImmutableMap{}) + gob.Register(&Int{}) + gob.Register(&Map{}) + gob.Register(&String{}) + gob.Register(&Time{}) + gob.Register(&Undefined{}) + gob.Register(&UserFunction{}) +} |