1
2
3
4
5 package out_test
6
7 import (
8 "bufio"
9 "bytes"
10 "fmt"
11 "internal/testenv"
12 "internal/goarch"
13 "os"
14 "path/filepath"
15 "regexp"
16 "strconv"
17 "strings"
18 "testing"
19 )
20
21 type methodAlign struct {
22 Method string
23 Align int
24 }
25
26 var wantAligns = map[string]int{
27 "ReturnEmpty": 1,
28 "ReturnOnlyUint8": 1,
29 "ReturnOnlyUint16": 2,
30 "ReturnOnlyUint32": 4,
31 "ReturnOnlyUint64": goarch.PtrSize,
32 "ReturnOnlyInt": goarch.PtrSize,
33 "ReturnOnlyPtr": goarch.PtrSize,
34 "ReturnByteSlice": goarch.PtrSize,
35 "ReturnString": goarch.PtrSize,
36 "InputAndReturnUint8": 1,
37 "MixedTypes": goarch.PtrSize,
38 }
39
40
41
42
43 func TestAligned(t *testing.T) {
44 testenv.MustHaveGoRun(t)
45 testenv.MustHaveCGO(t)
46
47 testdata, err := filepath.Abs("testdata")
48 if err != nil {
49 t.Fatal(err)
50 }
51
52 objDir := t.TempDir()
53
54 cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "cgo",
55 "-objdir", objDir,
56 filepath.Join(testdata, "aligned.go"))
57 cmd.Stderr = new(bytes.Buffer)
58
59 err = cmd.Run()
60 if err != nil {
61 t.Fatalf("%#q: %v\n%s", cmd, err, cmd.Stderr)
62 }
63
64 haveAligns, err := parseAlign(filepath.Join(objDir, "_cgo_export.c"))
65 if err != nil {
66 t.Fatal(err)
67 }
68
69
70 if len(haveAligns) != len(wantAligns) {
71 t.Fatalf("have %d methods with aligned, want %d", len(haveAligns), len(wantAligns))
72 }
73
74 for i := range haveAligns {
75 method := haveAligns[i].Method
76 haveAlign := haveAligns[i].Align
77
78 wantAlign, ok := wantAligns[method]
79 if !ok {
80 t.Errorf("method %s: have aligned %d, want missing entry", method, haveAlign)
81 } else if haveAlign != wantAlign {
82 t.Errorf("method %s: have aligned %d, want %d", method, haveAlign, wantAlign)
83 }
84 }
85 }
86
87 func parseAlign(filename string) ([]methodAlign, error) {
88 file, err := os.Open(filename)
89 if err != nil {
90 return nil, fmt.Errorf("failed to open file: %w", err)
91 }
92 defer file.Close()
93
94 var results []methodAlign
95 scanner := bufio.NewScanner(file)
96
97
98 funcRegex := regexp.MustCompile(`^struct\s+(\w+)_return\s+(\w+)\(`)
99
100 simpleFuncRegex := regexp.MustCompile(`^Go\w+\s+(\w+)\(`)
101
102 voidFuncRegex := regexp.MustCompile(`^void\s+(\w+)\(`)
103
104 alignRegex := regexp.MustCompile(`__attribute__\(\(aligned\((\d+)\)\)\)`)
105
106 var currentMethod string
107
108 for scanner.Scan() {
109 line := strings.TrimSpace(scanner.Text())
110
111
112 if matches := funcRegex.FindStringSubmatch(line); matches != nil {
113 currentMethod = matches[2]
114 } else if matches := simpleFuncRegex.FindStringSubmatch(line); matches != nil {
115
116 currentMethod = matches[1]
117 } else if matches := voidFuncRegex.FindStringSubmatch(line); matches != nil {
118
119 currentMethod = matches[1]
120 }
121
122
123 if alignMatches := alignRegex.FindStringSubmatch(line); alignMatches != nil && currentMethod != "" {
124 alignStr := alignMatches[1]
125 align, err := strconv.Atoi(alignStr)
126 if err != nil {
127
128 currentMethod = ""
129 continue
130 }
131 results = append(results, methodAlign{
132 Method: currentMethod,
133 Align: align,
134 })
135 currentMethod = ""
136 }
137 }
138
139 if err := scanner.Err(); err != nil {
140 return nil, fmt.Errorf("error reading file: %w", err)
141 }
142
143 return results, nil
144 }
145
View as plain text