298 lines
9.2 KiB
Go
298 lines
9.2 KiB
Go
package tengo_test
|
|
|
|
import (
|
|
"bytes"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/d5/tengo/v2"
|
|
"github.com/d5/tengo/v2/parser"
|
|
"github.com/d5/tengo/v2/require"
|
|
)
|
|
|
|
type srcfile struct {
|
|
name string
|
|
size int
|
|
}
|
|
|
|
func TestBytecode(t *testing.T) {
|
|
testBytecodeSerialization(t, bytecode(concatInsts(), objectsArray()))
|
|
|
|
testBytecodeSerialization(t, bytecode(
|
|
concatInsts(), objectsArray(
|
|
&tengo.Char{Value: 'y'},
|
|
&tengo.Float{Value: 93.11},
|
|
compiledFunction(1, 0,
|
|
tengo.MakeInstruction(parser.OpConstant, 3),
|
|
tengo.MakeInstruction(parser.OpSetLocal, 0),
|
|
tengo.MakeInstruction(parser.OpGetGlobal, 0),
|
|
tengo.MakeInstruction(parser.OpGetFree, 0)),
|
|
&tengo.Float{Value: 39.2},
|
|
&tengo.Int{Value: 192},
|
|
&tengo.String{Value: "bar"})))
|
|
|
|
testBytecodeSerialization(t, bytecodeFileSet(
|
|
concatInsts(
|
|
tengo.MakeInstruction(parser.OpConstant, 0),
|
|
tengo.MakeInstruction(parser.OpSetGlobal, 0),
|
|
tengo.MakeInstruction(parser.OpConstant, 6),
|
|
tengo.MakeInstruction(parser.OpPop)),
|
|
objectsArray(
|
|
&tengo.Int{Value: 55},
|
|
&tengo.Int{Value: 66},
|
|
&tengo.Int{Value: 77},
|
|
&tengo.Int{Value: 88},
|
|
&tengo.ImmutableMap{
|
|
Value: map[string]tengo.Object{
|
|
"array": &tengo.ImmutableArray{
|
|
Value: []tengo.Object{
|
|
&tengo.Int{Value: 1},
|
|
&tengo.Int{Value: 2},
|
|
&tengo.Int{Value: 3},
|
|
tengo.TrueValue,
|
|
tengo.FalseValue,
|
|
tengo.UndefinedValue,
|
|
},
|
|
},
|
|
"true": tengo.TrueValue,
|
|
"false": tengo.FalseValue,
|
|
"bytes": &tengo.Bytes{Value: make([]byte, 16)},
|
|
"char": &tengo.Char{Value: 'Y'},
|
|
"error": &tengo.Error{Value: &tengo.String{
|
|
Value: "some error",
|
|
}},
|
|
"float": &tengo.Float{Value: -19.84},
|
|
"immutable_array": &tengo.ImmutableArray{
|
|
Value: []tengo.Object{
|
|
&tengo.Int{Value: 1},
|
|
&tengo.Int{Value: 2},
|
|
&tengo.Int{Value: 3},
|
|
tengo.TrueValue,
|
|
tengo.FalseValue,
|
|
tengo.UndefinedValue,
|
|
},
|
|
},
|
|
"immutable_map": &tengo.ImmutableMap{
|
|
Value: map[string]tengo.Object{
|
|
"a": &tengo.Int{Value: 1},
|
|
"b": &tengo.Int{Value: 2},
|
|
"c": &tengo.Int{Value: 3},
|
|
"d": tengo.TrueValue,
|
|
"e": tengo.FalseValue,
|
|
"f": tengo.UndefinedValue,
|
|
},
|
|
},
|
|
"int": &tengo.Int{Value: 91},
|
|
"map": &tengo.Map{
|
|
Value: map[string]tengo.Object{
|
|
"a": &tengo.Int{Value: 1},
|
|
"b": &tengo.Int{Value: 2},
|
|
"c": &tengo.Int{Value: 3},
|
|
"d": tengo.TrueValue,
|
|
"e": tengo.FalseValue,
|
|
"f": tengo.UndefinedValue,
|
|
},
|
|
},
|
|
"string": &tengo.String{Value: "foo bar"},
|
|
"time": &tengo.Time{Value: time.Now()},
|
|
"undefined": tengo.UndefinedValue,
|
|
},
|
|
},
|
|
compiledFunction(1, 0,
|
|
tengo.MakeInstruction(parser.OpConstant, 3),
|
|
tengo.MakeInstruction(parser.OpSetLocal, 0),
|
|
tengo.MakeInstruction(parser.OpGetGlobal, 0),
|
|
tengo.MakeInstruction(parser.OpGetFree, 0),
|
|
tengo.MakeInstruction(parser.OpBinaryOp, 11),
|
|
tengo.MakeInstruction(parser.OpGetFree, 1),
|
|
tengo.MakeInstruction(parser.OpBinaryOp, 11),
|
|
tengo.MakeInstruction(parser.OpGetLocal, 0),
|
|
tengo.MakeInstruction(parser.OpBinaryOp, 11),
|
|
tengo.MakeInstruction(parser.OpReturn, 1)),
|
|
compiledFunction(1, 0,
|
|
tengo.MakeInstruction(parser.OpConstant, 2),
|
|
tengo.MakeInstruction(parser.OpSetLocal, 0),
|
|
tengo.MakeInstruction(parser.OpGetFree, 0),
|
|
tengo.MakeInstruction(parser.OpGetLocal, 0),
|
|
tengo.MakeInstruction(parser.OpClosure, 4, 2),
|
|
tengo.MakeInstruction(parser.OpReturn, 1)),
|
|
compiledFunction(1, 0,
|
|
tengo.MakeInstruction(parser.OpConstant, 1),
|
|
tengo.MakeInstruction(parser.OpSetLocal, 0),
|
|
tengo.MakeInstruction(parser.OpGetLocal, 0),
|
|
tengo.MakeInstruction(parser.OpClosure, 5, 1),
|
|
tengo.MakeInstruction(parser.OpReturn, 1))),
|
|
fileSet(srcfile{name: "file1", size: 100},
|
|
srcfile{name: "file2", size: 200})))
|
|
}
|
|
|
|
func TestBytecode_RemoveDuplicates(t *testing.T) {
|
|
testBytecodeRemoveDuplicates(t,
|
|
bytecode(
|
|
concatInsts(), objectsArray(
|
|
&tengo.Char{Value: 'y'},
|
|
&tengo.Float{Value: 93.11},
|
|
compiledFunction(1, 0,
|
|
tengo.MakeInstruction(parser.OpConstant, 3),
|
|
tengo.MakeInstruction(parser.OpSetLocal, 0),
|
|
tengo.MakeInstruction(parser.OpGetGlobal, 0),
|
|
tengo.MakeInstruction(parser.OpGetFree, 0)),
|
|
&tengo.Float{Value: 39.2},
|
|
&tengo.Int{Value: 192},
|
|
&tengo.String{Value: "bar"})),
|
|
bytecode(
|
|
concatInsts(), objectsArray(
|
|
&tengo.Char{Value: 'y'},
|
|
&tengo.Float{Value: 93.11},
|
|
compiledFunction(1, 0,
|
|
tengo.MakeInstruction(parser.OpConstant, 3),
|
|
tengo.MakeInstruction(parser.OpSetLocal, 0),
|
|
tengo.MakeInstruction(parser.OpGetGlobal, 0),
|
|
tengo.MakeInstruction(parser.OpGetFree, 0)),
|
|
&tengo.Float{Value: 39.2},
|
|
&tengo.Int{Value: 192},
|
|
&tengo.String{Value: "bar"})))
|
|
|
|
testBytecodeRemoveDuplicates(t,
|
|
bytecode(
|
|
concatInsts(
|
|
tengo.MakeInstruction(parser.OpConstant, 0),
|
|
tengo.MakeInstruction(parser.OpConstant, 1),
|
|
tengo.MakeInstruction(parser.OpConstant, 2),
|
|
tengo.MakeInstruction(parser.OpConstant, 3),
|
|
tengo.MakeInstruction(parser.OpConstant, 4),
|
|
tengo.MakeInstruction(parser.OpConstant, 5),
|
|
tengo.MakeInstruction(parser.OpConstant, 6),
|
|
tengo.MakeInstruction(parser.OpConstant, 7),
|
|
tengo.MakeInstruction(parser.OpConstant, 8),
|
|
tengo.MakeInstruction(parser.OpClosure, 4, 1)),
|
|
objectsArray(
|
|
&tengo.Int{Value: 1},
|
|
&tengo.Float{Value: 2.0},
|
|
&tengo.Char{Value: '3'},
|
|
&tengo.String{Value: "four"},
|
|
compiledFunction(1, 0,
|
|
tengo.MakeInstruction(parser.OpConstant, 3),
|
|
tengo.MakeInstruction(parser.OpConstant, 7),
|
|
tengo.MakeInstruction(parser.OpSetLocal, 0),
|
|
tengo.MakeInstruction(parser.OpGetGlobal, 0),
|
|
tengo.MakeInstruction(parser.OpGetFree, 0)),
|
|
&tengo.Int{Value: 1},
|
|
&tengo.Float{Value: 2.0},
|
|
&tengo.Char{Value: '3'},
|
|
&tengo.String{Value: "four"})),
|
|
bytecode(
|
|
concatInsts(
|
|
tengo.MakeInstruction(parser.OpConstant, 0),
|
|
tengo.MakeInstruction(parser.OpConstant, 1),
|
|
tengo.MakeInstruction(parser.OpConstant, 2),
|
|
tengo.MakeInstruction(parser.OpConstant, 3),
|
|
tengo.MakeInstruction(parser.OpConstant, 4),
|
|
tengo.MakeInstruction(parser.OpConstant, 0),
|
|
tengo.MakeInstruction(parser.OpConstant, 1),
|
|
tengo.MakeInstruction(parser.OpConstant, 2),
|
|
tengo.MakeInstruction(parser.OpConstant, 3),
|
|
tengo.MakeInstruction(parser.OpClosure, 4, 1)),
|
|
objectsArray(
|
|
&tengo.Int{Value: 1},
|
|
&tengo.Float{Value: 2.0},
|
|
&tengo.Char{Value: '3'},
|
|
&tengo.String{Value: "four"},
|
|
compiledFunction(1, 0,
|
|
tengo.MakeInstruction(parser.OpConstant, 3),
|
|
tengo.MakeInstruction(parser.OpConstant, 2),
|
|
tengo.MakeInstruction(parser.OpSetLocal, 0),
|
|
tengo.MakeInstruction(parser.OpGetGlobal, 0),
|
|
tengo.MakeInstruction(parser.OpGetFree, 0)))))
|
|
|
|
testBytecodeRemoveDuplicates(t,
|
|
bytecode(
|
|
concatInsts(
|
|
tengo.MakeInstruction(parser.OpConstant, 0),
|
|
tengo.MakeInstruction(parser.OpConstant, 1),
|
|
tengo.MakeInstruction(parser.OpConstant, 2),
|
|
tengo.MakeInstruction(parser.OpConstant, 3),
|
|
tengo.MakeInstruction(parser.OpConstant, 4)),
|
|
objectsArray(
|
|
&tengo.Int{Value: 1},
|
|
&tengo.Int{Value: 2},
|
|
&tengo.Int{Value: 3},
|
|
&tengo.Int{Value: 1},
|
|
&tengo.Int{Value: 3})),
|
|
bytecode(
|
|
concatInsts(
|
|
tengo.MakeInstruction(parser.OpConstant, 0),
|
|
tengo.MakeInstruction(parser.OpConstant, 1),
|
|
tengo.MakeInstruction(parser.OpConstant, 2),
|
|
tengo.MakeInstruction(parser.OpConstant, 0),
|
|
tengo.MakeInstruction(parser.OpConstant, 2)),
|
|
objectsArray(
|
|
&tengo.Int{Value: 1},
|
|
&tengo.Int{Value: 2},
|
|
&tengo.Int{Value: 3})))
|
|
}
|
|
|
|
func TestBytecode_CountObjects(t *testing.T) {
|
|
b := bytecode(
|
|
concatInsts(),
|
|
objectsArray(
|
|
&tengo.Int{Value: 55},
|
|
&tengo.Int{Value: 66},
|
|
&tengo.Int{Value: 77},
|
|
&tengo.Int{Value: 88},
|
|
compiledFunction(1, 0,
|
|
tengo.MakeInstruction(parser.OpConstant, 3),
|
|
tengo.MakeInstruction(parser.OpReturn, 1)),
|
|
compiledFunction(1, 0,
|
|
tengo.MakeInstruction(parser.OpConstant, 2),
|
|
tengo.MakeInstruction(parser.OpReturn, 1)),
|
|
compiledFunction(1, 0,
|
|
tengo.MakeInstruction(parser.OpConstant, 1),
|
|
tengo.MakeInstruction(parser.OpReturn, 1))))
|
|
require.Equal(t, 7, b.CountObjects())
|
|
}
|
|
|
|
func fileSet(files ...srcfile) *parser.SourceFileSet {
|
|
fileSet := parser.NewFileSet()
|
|
for _, f := range files {
|
|
fileSet.AddFile(f.name, -1, f.size)
|
|
}
|
|
return fileSet
|
|
}
|
|
|
|
func bytecodeFileSet(
|
|
instructions []byte,
|
|
constants []tengo.Object,
|
|
fileSet *parser.SourceFileSet,
|
|
) *tengo.Bytecode {
|
|
return &tengo.Bytecode{
|
|
FileSet: fileSet,
|
|
MainFunction: &tengo.CompiledFunction{Instructions: instructions},
|
|
Constants: constants,
|
|
}
|
|
}
|
|
|
|
func testBytecodeRemoveDuplicates(
|
|
t *testing.T,
|
|
input, expected *tengo.Bytecode,
|
|
) {
|
|
input.RemoveDuplicates()
|
|
|
|
require.Equal(t, expected.FileSet, input.FileSet)
|
|
require.Equal(t, expected.MainFunction, input.MainFunction)
|
|
require.Equal(t, expected.Constants, input.Constants)
|
|
}
|
|
|
|
func testBytecodeSerialization(t *testing.T, b *tengo.Bytecode) {
|
|
var buf bytes.Buffer
|
|
err := b.Encode(&buf)
|
|
require.NoError(t, err)
|
|
|
|
r := &tengo.Bytecode{}
|
|
err = r.Decode(bytes.NewReader(buf.Bytes()), nil)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, b.FileSet, r.FileSet)
|
|
require.Equal(t, b.MainFunction, r.MainFunction)
|
|
require.Equal(t, b.Constants, r.Constants)
|
|
}
|