fix bytecode encoding/decoding (#152)
* register ImmutableMap type to gob * fix bytecode decoding * disallow empty module name
This commit is contained in:
parent
2520caf581
commit
e785e38bf8
6 changed files with 152 additions and 31 deletions
|
@ -17,27 +17,6 @@ type Bytecode struct {
|
|||
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
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode writes Bytecode data to the writer.
|
||||
func (b *Bytecode) Encode(w io.Writer) error {
|
||||
enc := gob.NewEncoder(w)
|
||||
|
@ -92,9 +71,20 @@ func (b *Bytecode) FormatConstants() (output []string) {
|
|||
func init() {
|
||||
gob.Register(&source.FileSet{})
|
||||
gob.Register(&source.File{})
|
||||
gob.Register(&objects.Array{})
|
||||
gob.Register(&objects.Bool{})
|
||||
gob.Register(&objects.Bytes{})
|
||||
gob.Register(&objects.Char{})
|
||||
gob.Register(&objects.Closure{})
|
||||
gob.Register(&objects.CompiledFunction{})
|
||||
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.String{})
|
||||
gob.Register(&objects.Time{})
|
||||
gob.Register(&objects.Undefined{})
|
||||
gob.Register(&objects.UserFunction{})
|
||||
}
|
||||
|
|
62
compiler/bytecode_decode.go
Normal file
62
compiler/bytecode_decode.go
Normal file
|
@ -0,0 +1,62 @@
|
|||
package compiler
|
||||
|
||||
import (
|
||||
"encoding/gob"
|
||||
"io"
|
||||
|
||||
"github.com/d5/tengo/objects"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
for i, v := range b.Constants {
|
||||
b.Constants[i] = fixDecoded(v)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func fixDecoded(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] = fixDecoded(v)
|
||||
}
|
||||
case *objects.ImmutableArray:
|
||||
for i, v := range o.Value {
|
||||
o.Value[i] = fixDecoded(v)
|
||||
}
|
||||
case *objects.Map:
|
||||
for k, v := range o.Value {
|
||||
o.Value[k] = fixDecoded(v)
|
||||
}
|
||||
case *objects.ImmutableMap:
|
||||
for k, v := range o.Value {
|
||||
o.Value[k] = fixDecoded(v)
|
||||
}
|
||||
}
|
||||
|
||||
return o
|
||||
}
|
|
@ -25,7 +25,7 @@ func (b *Bytecode) RemoveDuplicates() {
|
|||
indexMap[curIdx] = len(deduped)
|
||||
deduped = append(deduped, c)
|
||||
case *objects.ImmutableMap:
|
||||
modName := c.Value["__module_name__"].(*objects.String).Value
|
||||
modName := moduleName(c)
|
||||
newIdx, ok := immutableMaps[modName]
|
||||
if modName != "" && ok {
|
||||
indexMap[curIdx] = newIdx
|
||||
|
@ -72,7 +72,7 @@ func (b *Bytecode) RemoveDuplicates() {
|
|||
deduped = append(deduped, c)
|
||||
}
|
||||
default:
|
||||
panic(fmt.Errorf("invalid constant type: %s", c.TypeName()))
|
||||
panic(fmt.Errorf("unsupported top-level constant type: %s", c.TypeName()))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -119,3 +119,11 @@ func updateConstIndexes(insts []byte, indexMap map[int]int) {
|
|||
i += 1 + read
|
||||
}
|
||||
}
|
||||
|
||||
func moduleName(mod *objects.ImmutableMap) string {
|
||||
if modName, ok := mod.Value["__module_name__"].(*objects.String); ok {
|
||||
return modName.Value
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package compiler_test
|
|||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/d5/tengo/assert"
|
||||
"github.com/d5/tengo/compiler"
|
||||
|
@ -38,10 +39,64 @@ func TestBytecode(t *testing.T) {
|
|||
compiler.MakeInstruction(compiler.OpConstant, 6),
|
||||
compiler.MakeInstruction(compiler.OpPop)),
|
||||
objectsArray(
|
||||
intObject(55),
|
||||
intObject(66),
|
||||
intObject(77),
|
||||
intObject(88),
|
||||
&objects.Int{Value: 55},
|
||||
&objects.Int{Value: 66},
|
||||
&objects.Int{Value: 77},
|
||||
&objects.Int{Value: 88},
|
||||
&objects.ImmutableMap{
|
||||
Value: map[string]objects.Object{
|
||||
"array": &objects.ImmutableArray{
|
||||
Value: []objects.Object{
|
||||
&objects.Int{Value: 1},
|
||||
&objects.Int{Value: 2},
|
||||
&objects.Int{Value: 3},
|
||||
objects.TrueValue,
|
||||
objects.FalseValue,
|
||||
objects.UndefinedValue,
|
||||
},
|
||||
},
|
||||
"true": objects.TrueValue,
|
||||
"false": objects.FalseValue,
|
||||
"bytes": &objects.Bytes{Value: make([]byte, 16)},
|
||||
"char": &objects.Char{Value: 'Y'},
|
||||
"error": &objects.Error{Value: &objects.String{Value: "some error"}},
|
||||
"float": &objects.Float{Value: -19.84},
|
||||
"immutable_array": &objects.ImmutableArray{
|
||||
Value: []objects.Object{
|
||||
&objects.Int{Value: 1},
|
||||
&objects.Int{Value: 2},
|
||||
&objects.Int{Value: 3},
|
||||
objects.TrueValue,
|
||||
objects.FalseValue,
|
||||
objects.UndefinedValue,
|
||||
},
|
||||
},
|
||||
"immutable_map": &objects.ImmutableMap{
|
||||
Value: map[string]objects.Object{
|
||||
"a": &objects.Int{Value: 1},
|
||||
"b": &objects.Int{Value: 2},
|
||||
"c": &objects.Int{Value: 3},
|
||||
"d": objects.TrueValue,
|
||||
"e": objects.FalseValue,
|
||||
"f": objects.UndefinedValue,
|
||||
},
|
||||
},
|
||||
"int": &objects.Int{Value: 91},
|
||||
"map": &objects.Map{
|
||||
Value: map[string]objects.Object{
|
||||
"a": &objects.Int{Value: 1},
|
||||
"b": &objects.Int{Value: 2},
|
||||
"c": &objects.Int{Value: 3},
|
||||
"d": objects.TrueValue,
|
||||
"e": objects.FalseValue,
|
||||
"f": objects.UndefinedValue,
|
||||
},
|
||||
},
|
||||
"string": &objects.String{Value: "foo bar"},
|
||||
"time": &objects.Time{Value: time.Now()},
|
||||
"undefined": objects.UndefinedValue,
|
||||
},
|
||||
},
|
||||
compiledFunction(1, 0,
|
||||
compiler.MakeInstruction(compiler.OpConstant, 3),
|
||||
compiler.MakeInstruction(compiler.OpSetLocal, 0),
|
||||
|
@ -179,10 +234,10 @@ func TestBytecode_CountObjects(t *testing.T) {
|
|||
b := bytecode(
|
||||
concat(),
|
||||
objectsArray(
|
||||
intObject(55),
|
||||
intObject(66),
|
||||
intObject(77),
|
||||
intObject(88),
|
||||
&objects.Int{Value: 55},
|
||||
&objects.Int{Value: 66},
|
||||
&objects.Int{Value: 77},
|
||||
&objects.Int{Value: 88},
|
||||
compiledFunction(1, 0,
|
||||
compiler.MakeInstruction(compiler.OpConstant, 3),
|
||||
compiler.MakeInstruction(compiler.OpReturnValue)),
|
||||
|
|
|
@ -509,6 +509,10 @@ func (c *Compiler) Compile(node ast.Node) error {
|
|||
c.emit(node, OpCall, len(node.Args))
|
||||
|
||||
case *ast.ImportExpr:
|
||||
if node.ModuleName == "" {
|
||||
return c.errorf(node, "empty module name")
|
||||
}
|
||||
|
||||
if mod, ok := c.importModules[node.ModuleName]; ok {
|
||||
v, err := mod.Import(node.ModuleName)
|
||||
if err != nil {
|
||||
|
|
|
@ -891,6 +891,8 @@ r["x"] = {
|
|||
@k:1
|
||||
}
|
||||
`, "Parse Error: illegal character U+0040 '@'\n\tat test:3:5 (and 10 more errors)") // too many errors
|
||||
|
||||
expectError(t, `import("")`, "empty module name")
|
||||
}
|
||||
|
||||
func concat(instructions ...[]byte) []byte {
|
||||
|
|
Loading…
Reference in a new issue