go.starlark.net@v0.0.0-20231101134539-556fd59b42f6/internal/compile/codegen_test.go (about) 1 package compile 2 3 import ( 4 "bytes" 5 "fmt" 6 "testing" 7 8 "go.starlark.net/resolve" 9 "go.starlark.net/syntax" 10 ) 11 12 // TestPlusFolding ensures that the compiler generates optimized code for 13 // n-ary addition of strings, lists, and tuples. 14 func TestPlusFolding(t *testing.T) { 15 isPredeclared := func(name string) bool { return name == "x" } 16 isUniversal := func(name string) bool { return false } 17 for i, test := range []struct { 18 src string // source expression 19 want string // disassembled code 20 }{ 21 { 22 // string folding 23 `"a" + "b" + "c" + "d"`, 24 `constant "abcd"; return`, 25 }, 26 { 27 // string folding with variable: 28 `"a" + "b" + x + "c" + "d"`, 29 `constant "ab"; predeclared x; plus; constant "cd"; plus; return`, 30 }, 31 { 32 // list folding 33 `[1] + [2] + [3]`, 34 `constant 1; constant 2; constant 3; makelist<3>; return`, 35 }, 36 { 37 // list folding with variable 38 `[1] + [2] + x + [3]`, 39 `constant 1; constant 2; makelist<2>; ` + 40 `predeclared x; plus; ` + 41 `constant 3; makelist<1>; plus; ` + 42 `return`, 43 }, 44 { 45 // tuple folding 46 `() + (1,) + (2, 3)`, 47 `constant 1; constant 2; constant 3; maketuple<3>; return`, 48 }, 49 { 50 // tuple folding with variable 51 `() + (1,) + x + (2, 3)`, 52 `constant 1; maketuple<1>; predeclared x; plus; ` + 53 `constant 2; constant 3; maketuple<2>; plus; ` + 54 `return`, 55 }, 56 } { 57 expr, err := syntax.ParseExpr("in.star", test.src, 0) 58 if err != nil { 59 t.Errorf("#%d: %v", i, err) 60 continue 61 } 62 locals, err := resolve.Expr(expr, isPredeclared, isUniversal) 63 if err != nil { 64 t.Errorf("#%d: %v", i, err) 65 continue 66 } 67 got := disassemble(Expr(syntax.LegacyFileOptions(), expr, "<expr>", locals).Toplevel) 68 if test.want != got { 69 t.Errorf("expression <<%s>> generated <<%s>>, want <<%s>>", 70 test.src, got, test.want) 71 } 72 } 73 } 74 75 // disassemble is a trivial disassembler tailored to the accumulator test. 76 func disassemble(f *Funcode) string { 77 out := new(bytes.Buffer) 78 code := f.Code 79 for pc := 0; pc < len(code); { 80 op := Opcode(code[pc]) 81 pc++ 82 // TODO(adonovan): factor in common with interpreter. 83 var arg uint32 84 if op >= OpcodeArgMin { 85 for s := uint(0); ; s += 7 { 86 b := code[pc] 87 pc++ 88 arg |= uint32(b&0x7f) << s 89 if b < 0x80 { 90 break 91 } 92 } 93 } 94 95 if out.Len() > 0 { 96 out.WriteString("; ") 97 } 98 fmt.Fprintf(out, "%s", op) 99 if op >= OpcodeArgMin { 100 switch op { 101 case CONSTANT: 102 switch x := f.Prog.Constants[arg].(type) { 103 case string: 104 fmt.Fprintf(out, " %q", x) 105 default: 106 fmt.Fprintf(out, " %v", x) 107 } 108 case LOCAL: 109 fmt.Fprintf(out, " %s", f.Locals[arg].Name) 110 case PREDECLARED: 111 fmt.Fprintf(out, " %s", f.Prog.Names[arg]) 112 default: 113 fmt.Fprintf(out, "<%d>", arg) 114 } 115 } 116 } 117 return out.String() 118 }