// Copyright 2017 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package ld import ( objfilepkg "cmd/internal/objfile" // renamed to avoid conflict with objfile function "debug/dwarf" "internal/testenv" "io/ioutil" "os" "os/exec" "path/filepath" "reflect" "runtime" "testing" ) func TestRuntimeTypeDIEs(t *testing.T) { testenv.MustHaveGoBuild(t) if runtime.GOOS == "plan9" { t.Skip("skipping on plan9; no DWARF symbol table in executables") } dir, err := ioutil.TempDir("", "TestRuntimeTypeDIEs") if err != nil { t.Fatalf("could not create directory: %v", err) } defer os.RemoveAll(dir) f := gobuild(t, dir, `package main; func main() { }`) defer f.Close() dwarf, err := f.DWARF() if err != nil { t.Fatalf("error reading DWARF: %v", err) } want := map[string]bool{ "runtime._type": true, "runtime.arraytype": true, "runtime.chantype": true, "runtime.functype": true, "runtime.maptype": true, "runtime.ptrtype": true, "runtime.slicetype": true, "runtime.structtype": true, "runtime.interfacetype": true, "runtime.itab": true, "runtime.imethod": true, } found := findTypes(t, dwarf, want) if len(found) != len(want) { t.Errorf("found %v, want %v", found, want) } } func findTypes(t *testing.T, dw *dwarf.Data, want map[string]bool) (found map[string]bool) { found = make(map[string]bool) rdr := dw.Reader() for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() { if err != nil { t.Fatalf("error reading DWARF: %v", err) } switch entry.Tag { case dwarf.TagTypedef: if name, ok := entry.Val(dwarf.AttrName).(string); ok && want[name] { found[name] = true } } } return } func gobuild(t *testing.T, dir string, testfile string) *objfilepkg.File { src := filepath.Join(dir, "test.go") dst := filepath.Join(dir, "out") if err := ioutil.WriteFile(src, []byte(testfile), 0666); err != nil { t.Fatal(err) } cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", dst, src) if b, err := cmd.CombinedOutput(); err != nil { t.Logf("build: %s\n", b) t.Fatalf("build error: %v", err) } f, err := objfilepkg.Open(dst) if err != nil { t.Fatal(err) } return f } func TestEmbeddedStructMarker(t *testing.T) { testenv.MustHaveGoBuild(t) if runtime.GOOS == "plan9" { t.Skip("skipping on plan9; no DWARF symbol table in executables") } const prog = ` package main import "fmt" type Foo struct { v int } type Bar struct { Foo name string } type Baz struct { *Foo name string } func main() { bar := Bar{ Foo: Foo{v: 123}, name: "onetwothree"} baz := Baz{ Foo: &bar.Foo, name: "123" } fmt.Println(bar, baz) }` want := map[string]map[string]bool{ "main.Foo": map[string]bool{"v": false}, "main.Bar": map[string]bool{"Foo": true, "name": false}, "main.Baz": map[string]bool{"Foo": true, "name": false}, } dir, err := ioutil.TempDir("", "TestEmbeddedStructMarker") if err != nil { t.Fatalf("could not create directory: %v", err) } defer os.RemoveAll(dir) f := gobuild(t, dir, prog) defer f.Close() d, err := f.DWARF() if err != nil { t.Fatalf("error reading DWARF: %v", err) } rdr := d.Reader() for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() { if err != nil { t.Fatalf("error reading DWARF: %v", err) } switch entry.Tag { case dwarf.TagStructType: name := entry.Val(dwarf.AttrName).(string) wantMembers := want[name] if wantMembers == nil { continue } gotMembers, err := findMembers(rdr) if err != nil { t.Fatalf("error reading DWARF: %v", err) } if !reflect.DeepEqual(gotMembers, wantMembers) { t.Errorf("type %v: got map[member]embedded = %+v, want %+v", name, wantMembers, gotMembers) } delete(want, name) } } if len(want) != 0 { t.Errorf("failed to check all expected types: missing types = %+v", want) } } func findMembers(rdr *dwarf.Reader) (map[string]bool, error) { memberEmbedded := map[string]bool{} // TODO(hyangah): define in debug/dwarf package const goEmbeddedStruct = dwarf.Attr(0x2903) for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() { if err != nil { return nil, err } switch entry.Tag { case dwarf.TagMember: name := entry.Val(dwarf.AttrName).(string) embedded := entry.Val(goEmbeddedStruct).(bool) memberEmbedded[name] = embedded case 0: return memberEmbedded, nil } } return memberEmbedded, nil }