summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/d5/tengo/compiler/bytecode.go
blob: 42527731a53a273f8a30e6eeba49aea4074ff62f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package compiler

import (
	"encoding/gob"
	"fmt"
	"io"
	"reflect"

	"github.com/d5/tengo/compiler/source"
	"github.com/d5/tengo/objects"
)

// Bytecode is a compiled instructions and constants.
type Bytecode struct {
	FileSet      *source.FileSet
	MainFunction *objects.CompiledFunction
	Constants    []objects.Object
}

// Decode reads Bytecode data from the reader.
func (b *Bytecode) Decode(r io.Reader) error {
	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
	}

	// replace Bool and Undefined with known value
	for i, v := range b.Constants {
		b.Constants[i] = cleanupObjects(v)
	}

	return nil
}

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

	// constants
	return enc.Encode(b.Constants)
}

// 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 *objects.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
}

func cleanupObjects(o objects.Object) objects.Object {
	switch o := o.(type) {
	case *objects.Bool:
		if o.IsFalsy() {
			return objects.FalseValue
		}
		return objects.TrueValue
	case *objects.Undefined:
		return objects.UndefinedValue
	case *objects.Array:
		for i, v := range o.Value {
			o.Value[i] = cleanupObjects(v)
		}
	case *objects.Map:
		for k, v := range o.Value {
			o.Value[k] = cleanupObjects(v)
		}
	}

	return o
}

func init() {
	gob.Register(&source.FileSet{})
	gob.Register(&source.File{})
	gob.Register(&objects.Array{})
	gob.Register(&objects.ArrayIterator{})
	gob.Register(&objects.Bool{})
	gob.Register(&objects.Break{})
	gob.Register(&objects.BuiltinFunction{})
	gob.Register(&objects.Bytes{})
	gob.Register(&objects.Char{})
	gob.Register(&objects.Closure{})
	gob.Register(&objects.CompiledFunction{})
	gob.Register(&objects.Continue{})
	gob.Register(&objects.Error{})
	gob.Register(&objects.Float{})
	gob.Register(&objects.ImmutableArray{})
	gob.Register(&objects.ImmutableMap{})
	gob.Register(&objects.Int{})
	gob.Register(&objects.Map{})
	gob.Register(&objects.MapIterator{})
	gob.Register(&objects.ReturnValue{})
	gob.Register(&objects.String{})
	gob.Register(&objects.StringIterator{})
	gob.Register(&objects.Time{})
	gob.Register(&objects.Undefined{})
	gob.Register(&objects.UserFunction{})
}