bytecode_test.go 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. package tengo_test
  2. import (
  3. "bytes"
  4. "testing"
  5. "time"
  6. "github.com/d5/tengo/v2"
  7. "github.com/d5/tengo/v2/parser"
  8. "github.com/d5/tengo/v2/require"
  9. )
  10. type srcfile struct {
  11. name string
  12. size int
  13. }
  14. func TestBytecode(t *testing.T) {
  15. testBytecodeSerialization(t, bytecode(concatInsts(), objectsArray()))
  16. testBytecodeSerialization(t, bytecode(
  17. concatInsts(), objectsArray(
  18. &tengo.Char{Value: 'y'},
  19. &tengo.Float{Value: 93.11},
  20. compiledFunction(1, 0,
  21. tengo.MakeInstruction(parser.OpConstant, 3),
  22. tengo.MakeInstruction(parser.OpSetLocal, 0),
  23. tengo.MakeInstruction(parser.OpGetGlobal, 0),
  24. tengo.MakeInstruction(parser.OpGetFree, 0)),
  25. &tengo.Float{Value: 39.2},
  26. &tengo.Int{Value: 192},
  27. &tengo.String{Value: "bar"})))
  28. testBytecodeSerialization(t, bytecodeFileSet(
  29. concatInsts(
  30. tengo.MakeInstruction(parser.OpConstant, 0),
  31. tengo.MakeInstruction(parser.OpSetGlobal, 0),
  32. tengo.MakeInstruction(parser.OpConstant, 6),
  33. tengo.MakeInstruction(parser.OpPop)),
  34. objectsArray(
  35. &tengo.Int{Value: 55},
  36. &tengo.Int{Value: 66},
  37. &tengo.Int{Value: 77},
  38. &tengo.Int{Value: 88},
  39. &tengo.ImmutableMap{
  40. Value: map[string]tengo.Object{
  41. "array": &tengo.ImmutableArray{
  42. Value: []tengo.Object{
  43. &tengo.Int{Value: 1},
  44. &tengo.Int{Value: 2},
  45. &tengo.Int{Value: 3},
  46. tengo.TrueValue,
  47. tengo.FalseValue,
  48. tengo.UndefinedValue,
  49. },
  50. },
  51. "true": tengo.TrueValue,
  52. "false": tengo.FalseValue,
  53. "bytes": &tengo.Bytes{Value: make([]byte, 16)},
  54. "char": &tengo.Char{Value: 'Y'},
  55. "error": &tengo.Error{Value: &tengo.String{
  56. Value: "some error",
  57. }},
  58. "float": &tengo.Float{Value: -19.84},
  59. "immutable_array": &tengo.ImmutableArray{
  60. Value: []tengo.Object{
  61. &tengo.Int{Value: 1},
  62. &tengo.Int{Value: 2},
  63. &tengo.Int{Value: 3},
  64. tengo.TrueValue,
  65. tengo.FalseValue,
  66. tengo.UndefinedValue,
  67. },
  68. },
  69. "immutable_map": &tengo.ImmutableMap{
  70. Value: map[string]tengo.Object{
  71. "a": &tengo.Int{Value: 1},
  72. "b": &tengo.Int{Value: 2},
  73. "c": &tengo.Int{Value: 3},
  74. "d": tengo.TrueValue,
  75. "e": tengo.FalseValue,
  76. "f": tengo.UndefinedValue,
  77. },
  78. },
  79. "int": &tengo.Int{Value: 91},
  80. "map": &tengo.Map{
  81. Value: map[string]tengo.Object{
  82. "a": &tengo.Int{Value: 1},
  83. "b": &tengo.Int{Value: 2},
  84. "c": &tengo.Int{Value: 3},
  85. "d": tengo.TrueValue,
  86. "e": tengo.FalseValue,
  87. "f": tengo.UndefinedValue,
  88. },
  89. },
  90. "string": &tengo.String{Value: "foo bar"},
  91. "time": &tengo.Time{Value: time.Now()},
  92. "undefined": tengo.UndefinedValue,
  93. },
  94. },
  95. compiledFunction(1, 0,
  96. tengo.MakeInstruction(parser.OpConstant, 3),
  97. tengo.MakeInstruction(parser.OpSetLocal, 0),
  98. tengo.MakeInstruction(parser.OpGetGlobal, 0),
  99. tengo.MakeInstruction(parser.OpGetFree, 0),
  100. tengo.MakeInstruction(parser.OpBinaryOp, 11),
  101. tengo.MakeInstruction(parser.OpGetFree, 1),
  102. tengo.MakeInstruction(parser.OpBinaryOp, 11),
  103. tengo.MakeInstruction(parser.OpGetLocal, 0),
  104. tengo.MakeInstruction(parser.OpBinaryOp, 11),
  105. tengo.MakeInstruction(parser.OpReturn, 1)),
  106. compiledFunction(1, 0,
  107. tengo.MakeInstruction(parser.OpConstant, 2),
  108. tengo.MakeInstruction(parser.OpSetLocal, 0),
  109. tengo.MakeInstruction(parser.OpGetFree, 0),
  110. tengo.MakeInstruction(parser.OpGetLocal, 0),
  111. tengo.MakeInstruction(parser.OpClosure, 4, 2),
  112. tengo.MakeInstruction(parser.OpReturn, 1)),
  113. compiledFunction(1, 0,
  114. tengo.MakeInstruction(parser.OpConstant, 1),
  115. tengo.MakeInstruction(parser.OpSetLocal, 0),
  116. tengo.MakeInstruction(parser.OpGetLocal, 0),
  117. tengo.MakeInstruction(parser.OpClosure, 5, 1),
  118. tengo.MakeInstruction(parser.OpReturn, 1))),
  119. fileSet(srcfile{name: "file1", size: 100},
  120. srcfile{name: "file2", size: 200})))
  121. }
  122. func TestBytecode_RemoveDuplicates(t *testing.T) {
  123. testBytecodeRemoveDuplicates(t,
  124. bytecode(
  125. concatInsts(), objectsArray(
  126. &tengo.Char{Value: 'y'},
  127. &tengo.Float{Value: 93.11},
  128. compiledFunction(1, 0,
  129. tengo.MakeInstruction(parser.OpConstant, 3),
  130. tengo.MakeInstruction(parser.OpSetLocal, 0),
  131. tengo.MakeInstruction(parser.OpGetGlobal, 0),
  132. tengo.MakeInstruction(parser.OpGetFree, 0)),
  133. &tengo.Float{Value: 39.2},
  134. &tengo.Int{Value: 192},
  135. &tengo.String{Value: "bar"})),
  136. bytecode(
  137. concatInsts(), objectsArray(
  138. &tengo.Char{Value: 'y'},
  139. &tengo.Float{Value: 93.11},
  140. compiledFunction(1, 0,
  141. tengo.MakeInstruction(parser.OpConstant, 3),
  142. tengo.MakeInstruction(parser.OpSetLocal, 0),
  143. tengo.MakeInstruction(parser.OpGetGlobal, 0),
  144. tengo.MakeInstruction(parser.OpGetFree, 0)),
  145. &tengo.Float{Value: 39.2},
  146. &tengo.Int{Value: 192},
  147. &tengo.String{Value: "bar"})))
  148. testBytecodeRemoveDuplicates(t,
  149. bytecode(
  150. concatInsts(
  151. tengo.MakeInstruction(parser.OpConstant, 0),
  152. tengo.MakeInstruction(parser.OpConstant, 1),
  153. tengo.MakeInstruction(parser.OpConstant, 2),
  154. tengo.MakeInstruction(parser.OpConstant, 3),
  155. tengo.MakeInstruction(parser.OpConstant, 4),
  156. tengo.MakeInstruction(parser.OpConstant, 5),
  157. tengo.MakeInstruction(parser.OpConstant, 6),
  158. tengo.MakeInstruction(parser.OpConstant, 7),
  159. tengo.MakeInstruction(parser.OpConstant, 8),
  160. tengo.MakeInstruction(parser.OpClosure, 4, 1)),
  161. objectsArray(
  162. &tengo.Int{Value: 1},
  163. &tengo.Float{Value: 2.0},
  164. &tengo.Char{Value: '3'},
  165. &tengo.String{Value: "four"},
  166. compiledFunction(1, 0,
  167. tengo.MakeInstruction(parser.OpConstant, 3),
  168. tengo.MakeInstruction(parser.OpConstant, 7),
  169. tengo.MakeInstruction(parser.OpSetLocal, 0),
  170. tengo.MakeInstruction(parser.OpGetGlobal, 0),
  171. tengo.MakeInstruction(parser.OpGetFree, 0)),
  172. &tengo.Int{Value: 1},
  173. &tengo.Float{Value: 2.0},
  174. &tengo.Char{Value: '3'},
  175. &tengo.String{Value: "four"})),
  176. bytecode(
  177. concatInsts(
  178. tengo.MakeInstruction(parser.OpConstant, 0),
  179. tengo.MakeInstruction(parser.OpConstant, 1),
  180. tengo.MakeInstruction(parser.OpConstant, 2),
  181. tengo.MakeInstruction(parser.OpConstant, 3),
  182. tengo.MakeInstruction(parser.OpConstant, 4),
  183. tengo.MakeInstruction(parser.OpConstant, 0),
  184. tengo.MakeInstruction(parser.OpConstant, 1),
  185. tengo.MakeInstruction(parser.OpConstant, 2),
  186. tengo.MakeInstruction(parser.OpConstant, 3),
  187. tengo.MakeInstruction(parser.OpClosure, 4, 1)),
  188. objectsArray(
  189. &tengo.Int{Value: 1},
  190. &tengo.Float{Value: 2.0},
  191. &tengo.Char{Value: '3'},
  192. &tengo.String{Value: "four"},
  193. compiledFunction(1, 0,
  194. tengo.MakeInstruction(parser.OpConstant, 3),
  195. tengo.MakeInstruction(parser.OpConstant, 2),
  196. tengo.MakeInstruction(parser.OpSetLocal, 0),
  197. tengo.MakeInstruction(parser.OpGetGlobal, 0),
  198. tengo.MakeInstruction(parser.OpGetFree, 0)))))
  199. testBytecodeRemoveDuplicates(t,
  200. bytecode(
  201. concatInsts(
  202. tengo.MakeInstruction(parser.OpConstant, 0),
  203. tengo.MakeInstruction(parser.OpConstant, 1),
  204. tengo.MakeInstruction(parser.OpConstant, 2),
  205. tengo.MakeInstruction(parser.OpConstant, 3),
  206. tengo.MakeInstruction(parser.OpConstant, 4)),
  207. objectsArray(
  208. &tengo.Int{Value: 1},
  209. &tengo.Int{Value: 2},
  210. &tengo.Int{Value: 3},
  211. &tengo.Int{Value: 1},
  212. &tengo.Int{Value: 3})),
  213. bytecode(
  214. concatInsts(
  215. tengo.MakeInstruction(parser.OpConstant, 0),
  216. tengo.MakeInstruction(parser.OpConstant, 1),
  217. tengo.MakeInstruction(parser.OpConstant, 2),
  218. tengo.MakeInstruction(parser.OpConstant, 0),
  219. tengo.MakeInstruction(parser.OpConstant, 2)),
  220. objectsArray(
  221. &tengo.Int{Value: 1},
  222. &tengo.Int{Value: 2},
  223. &tengo.Int{Value: 3})))
  224. }
  225. func TestBytecode_CountObjects(t *testing.T) {
  226. b := bytecode(
  227. concatInsts(),
  228. objectsArray(
  229. &tengo.Int{Value: 55},
  230. &tengo.Int{Value: 66},
  231. &tengo.Int{Value: 77},
  232. &tengo.Int{Value: 88},
  233. compiledFunction(1, 0,
  234. tengo.MakeInstruction(parser.OpConstant, 3),
  235. tengo.MakeInstruction(parser.OpReturn, 1)),
  236. compiledFunction(1, 0,
  237. tengo.MakeInstruction(parser.OpConstant, 2),
  238. tengo.MakeInstruction(parser.OpReturn, 1)),
  239. compiledFunction(1, 0,
  240. tengo.MakeInstruction(parser.OpConstant, 1),
  241. tengo.MakeInstruction(parser.OpReturn, 1))))
  242. require.Equal(t, 7, b.CountObjects())
  243. }
  244. func fileSet(files ...srcfile) *parser.SourceFileSet {
  245. fileSet := parser.NewFileSet()
  246. for _, f := range files {
  247. fileSet.AddFile(f.name, -1, f.size)
  248. }
  249. return fileSet
  250. }
  251. func bytecodeFileSet(
  252. instructions []byte,
  253. constants []tengo.Object,
  254. fileSet *parser.SourceFileSet,
  255. ) *tengo.Bytecode {
  256. return &tengo.Bytecode{
  257. FileSet: fileSet,
  258. MainFunction: &tengo.CompiledFunction{Instructions: instructions},
  259. Constants: constants,
  260. }
  261. }
  262. func testBytecodeRemoveDuplicates(
  263. t *testing.T,
  264. input, expected *tengo.Bytecode,
  265. ) {
  266. input.RemoveDuplicates()
  267. require.Equal(t, expected.FileSet, input.FileSet)
  268. require.Equal(t, expected.MainFunction, input.MainFunction)
  269. require.Equal(t, expected.Constants, input.Constants)
  270. }
  271. func testBytecodeSerialization(t *testing.T, b *tengo.Bytecode) {
  272. var buf bytes.Buffer
  273. err := b.Encode(&buf)
  274. require.NoError(t, err)
  275. r := &tengo.Bytecode{}
  276. err = r.Decode(bytes.NewReader(buf.Bytes()), nil)
  277. require.NoError(t, err)
  278. require.Equal(t, b.FileSet, r.FileSet)
  279. require.Equal(t, b.MainFunction, r.MainFunction)
  280. require.Equal(t, b.Constants, r.Constants)
  281. }