Skip to content

Commit

Permalink
internal/gcimporter: add support for reading unified IR export data
Browse files Browse the repository at this point in the history
This does not include writing export data in the unified IR format.

Most of this change is an import of code from the Go implementation,
with minor tweaks and gaskets added.

This does not (yet) address the registry issue mentioned in #52163.

Updates #52163.

Change-Id: I98030e6c9ff35c6ff678b8a7ce9b653b18e65e17
Reviewed-on: https://go-review.googlesource.com/c/tools/+/412821
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: David Chase <drchase@google.com>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
  • Loading branch information
dr2chase committed Jul 15, 2022
1 parent c3af7c2 commit 22d1494
Show file tree
Hide file tree
Showing 18 changed files with 1,968 additions and 18 deletions.
28 changes: 22 additions & 6 deletions go/gcexportdata/gcexportdata.go
Expand Up @@ -116,13 +116,29 @@ func Read(in io.Reader, fset *token.FileSet, imports map[string]*types.Package,
// The indexed export format starts with an 'i'; the older
// binary export format starts with a 'c', 'd', or 'v'
// (from "version"). Select appropriate importer.
if len(data) > 0 && data[0] == 'i' {
_, pkg, err := gcimporter.IImportData(fset, imports, data[1:], path)
return pkg, err
}
if len(data) > 0 {
switch data[0] {
case 'i':
_, pkg, err := gcimporter.IImportData(fset, imports, data[1:], path)
return pkg, err

case 'v', 'c', 'd':
_, pkg, err := gcimporter.BImportData(fset, imports, data, path)
return pkg, err

_, pkg, err := gcimporter.BImportData(fset, imports, data, path)
return pkg, err
case 'u':
_, pkg, err := gcimporter.UImportData(fset, imports, data[1:], path)
return pkg, err

default:
l := len(data)
if l > 10 {
l = 10
}
return nil, fmt.Errorf("unexpected export data with prefix %q for path %s", string(data[:l]), path)
}
}
return nil, fmt.Errorf("empty export data for %s", path)
}

// Write writes encoded type information for the specified package to out.
Expand Down
28 changes: 23 additions & 5 deletions go/internal/gcimporter/gcimporter.go
Expand Up @@ -181,8 +181,9 @@ func Import(packages map[string]*types.Package, path, srcDir string, lookup func
defer rc.Close()

var hdr string
var size int64
buf := bufio.NewReader(rc)
if hdr, _, err = FindExportData(buf); err != nil {
if hdr, size, err = FindExportData(buf); err != nil {
return
}

Expand Down Expand Up @@ -210,10 +211,27 @@ func Import(packages map[string]*types.Package, path, srcDir string, lookup func
// The indexed export format starts with an 'i'; the older
// binary export format starts with a 'c', 'd', or 'v'
// (from "version"). Select appropriate importer.
if len(data) > 0 && data[0] == 'i' {
_, pkg, err = IImportData(fset, packages, data[1:], id)
} else {
_, pkg, err = BImportData(fset, packages, data, id)
if len(data) > 0 {
switch data[0] {
case 'i':
_, pkg, err := IImportData(fset, packages, data[1:], id)
return pkg, err

case 'v', 'c', 'd':
_, pkg, err := BImportData(fset, packages, data, id)
return pkg, err

case 'u':
_, pkg, err := UImportData(fset, packages, data[1:size], id)
return pkg, err

default:
l := len(data)
if l > 10 {
l = 10
}
return nil, fmt.Errorf("unexpected export data with prefix %q for path %s", string(data[:l]), id)
}
}

default:
Expand Down
39 changes: 32 additions & 7 deletions go/internal/gcimporter/gcimporter_test.go
Expand Up @@ -45,6 +45,10 @@ func needsCompiler(t *testing.T, compiler string) {
// compile runs the compiler on filename, with dirname as the working directory,
// and writes the output file to outdirname.
func compile(t *testing.T, dirname, filename, outdirname string) string {
return compilePkg(t, dirname, filename, outdirname, "p")
}

func compilePkg(t *testing.T, dirname, filename, outdirname, pkg string) string {
testenv.NeedsGoBuild(t)

// filename must end with ".go"
Expand All @@ -53,12 +57,12 @@ func compile(t *testing.T, dirname, filename, outdirname string) string {
}
basename := filepath.Base(filename)
outname := filepath.Join(outdirname, basename[:len(basename)-2]+"o")
cmd := exec.Command("go", "tool", "compile", "-p=p", "-o", outname, filename)
cmd := exec.Command("go", "tool", "compile", "-p="+pkg, "-o", outname, filename)
cmd.Dir = dirname
out, err := cmd.CombinedOutput()
if err != nil {
t.Logf("%s", out)
t.Fatalf("go tool compile %s failed: %s", filename, err)
t.Fatalf("(cd %v && %v) failed: %s", cmd.Dir, cmd, err)
}
return outname
}
Expand Down Expand Up @@ -140,7 +144,11 @@ func TestImportTestdata(t *testing.T) {
// For now, we just test the presence of a few packages
// that we know are there for sure.
got := fmt.Sprint(pkg.Imports())
for _, want := range []string{"go/ast", "go/token"} {
wants := []string{"go/ast", "go/token"}
if unifiedIR {
wants = []string{"go/ast"}
}
for _, want := range wants {
if !strings.Contains(got, want) {
t.Errorf(`Package("exports").Imports() = %s, does not contain %s`, got, want)
}
Expand Down Expand Up @@ -364,6 +372,14 @@ func verifyInterfaceMethodRecvs(t *testing.T, named *types.Named, level int) {
return // not an interface
}

// The unified IR importer always sets interface method receiver
// parameters to point to the Interface type, rather than the Named.
// See #49906.
var want types.Type = named
if unifiedIR {
want = iface
}

// check explicitly declared methods
for i := 0; i < iface.NumExplicitMethods(); i++ {
m := iface.ExplicitMethod(i)
Expand All @@ -372,8 +388,8 @@ func verifyInterfaceMethodRecvs(t *testing.T, named *types.Named, level int) {
t.Errorf("%s: missing receiver type", m)
continue
}
if recv.Type() != named {
t.Errorf("%s: got recv type %s; want %s", m, recv.Type(), named)
if recv.Type() != want {
t.Errorf("%s: got recv type %s; want %s", m, recv.Type(), want)
}
}

Expand Down Expand Up @@ -451,7 +467,7 @@ func TestIssue13566(t *testing.T) {
if err != nil {
t.Fatal(err)
}
compile(t, "testdata", "a.go", testoutdir)
compilePkg(t, "testdata", "a.go", testoutdir, apkg(testoutdir))
compile(t, testoutdir, bpath, testoutdir)

// import must succeed (test for issue at hand)
Expand Down Expand Up @@ -611,13 +627,22 @@ func TestIssue51836(t *testing.T) {
if err != nil {
t.Fatal(err)
}
compile(t, dir, "a.go", testoutdir)
compilePkg(t, dir, "a.go", testoutdir, apkg(testoutdir))
compile(t, testoutdir, bpath, testoutdir)

// import must succeed (test for issue at hand)
_ = importPkg(t, "./testdata/aa", tmpdir)
}

// apkg returns the package "a" prefixed by (as a package) testoutdir
func apkg(testoutdir string) string {
apkg := testoutdir + "/a"
if os.PathSeparator != '/' {
apkg = strings.ReplaceAll(apkg, string(os.PathSeparator), "/")
}
return apkg
}

func importPkg(t *testing.T, path, srcDir string) *types.Package {
pkg, err := Import(make(map[string]*types.Package), path, srcDir, nil)
if err != nil {
Expand Down
10 changes: 10 additions & 0 deletions go/internal/gcimporter/unified_no.go
@@ -0,0 +1,10 @@
// Copyright 2022 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.

//go:build !(go1.18 && goexperiment.unified)
// +build !go1.18 !goexperiment.unified

package gcimporter

const unifiedIR = false
10 changes: 10 additions & 0 deletions go/internal/gcimporter/unified_yes.go
@@ -0,0 +1,10 @@
// Copyright 2022 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.

//go:build go1.18 && goexperiment.unified
// +build go1.18,goexperiment.unified

package gcimporter

const unifiedIR = true
19 changes: 19 additions & 0 deletions go/internal/gcimporter/ureader_no.go
@@ -0,0 +1,19 @@
// Copyright 2022 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.

//go:build !go1.18
// +build !go1.18

package gcimporter

import (
"fmt"
"go/token"
"go/types"
)

func UImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (_ int, pkg *types.Package, err error) {
err = fmt.Errorf("go/tools compiled with a Go version earlier than 1.18 cannot read unified IR export data")
return
}

0 comments on commit 22d1494

Please sign in to comment.