123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297 |
- package tengo
- import (
- "encoding/gob"
- "fmt"
- "io"
- "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, cn.TypeName(), &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
- fns := make(map[*CompiledFunction]int)
- 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:
- if newIdx, ok := fns[c]; ok {
- indexMap[curIdx] = newIdx
- } else {
- newIdx = len(deduped)
- fns[c] = newIdx
- indexMap[curIdx] = newIdx
- 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{})
- }
|