2018-04-26 20:58:52 +03:00
|
|
|
// Copyright 2018 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.
|
|
|
|
|
|
|
|
// Indexed package import.
|
|
|
|
// See cmd/compile/internal/gc/iexport.go for the export data format.
|
|
|
|
|
|
|
|
// This file is a copy of $GOROOT/src/go/internal/gcimporter/iimport.go.
|
|
|
|
|
|
|
|
package gcimporter
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/binary"
|
2018-06-13 08:33:41 +03:00
|
|
|
"fmt"
|
2018-04-26 20:58:52 +03:00
|
|
|
"go/constant"
|
|
|
|
"go/token"
|
|
|
|
"go/types"
|
|
|
|
"io"
|
2022-07-15 02:30:14 +03:00
|
|
|
"math/big"
|
2018-04-26 20:58:52 +03:00
|
|
|
"sort"
|
2021-10-01 05:45:40 +03:00
|
|
|
"strings"
|
2021-09-14 20:07:03 +03:00
|
|
|
|
internal/gcimporter: supporting encoding objects from different packages
Through forwarded underlyings or type aliases, shallow export data may
reference fields or methods declared in other packages. However, the
importer was not able to canonicalize these fields or methods, because
unlike with deep export data, it had no index of types or objects in
other packages.
For example, consider:
package a
func A() *[]func() struct { X int }
package b
import "a"
var B = a.A()
The export data for package 'a' must record that the type graphs for A,
and B must share the subgraph for the interior node X, deeply within
both. With deep export data this is easy because it encodes several
packages in a single object graph, but shallow export data needs some
kind of identifier for object X deep within package b so it can be
located when b is imported independently. Objectpath is such a name.
Use the objectpath algorithm to encode these objects during shallow
export, and look them up during shallow import.
Running TestShallowStd in the internal/gcimporter package turned up only
one set of fields affected by this change: the use of the
fuzz.CorpusEntry alias from the testing/internal/testdeps package.
Therefore, the performance cost of objectpath is likely insignificant.
Also simplify exporting in iexport.go. Unlike during importing, there is
no need to track the current package.
Fixes golang/go#60676
Fixes golang/go#60819
Change-Id: I15ae14c5733d1cafd14aabd7150da4f7b851e733
Reviewed-on: https://go-review.googlesource.com/c/tools/+/503438
gopls-CI: kokoro <noreply+kokoro@google.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
2023-06-16 00:10:58 +03:00
|
|
|
"golang.org/x/tools/go/types/objectpath"
|
2021-09-14 20:07:03 +03:00
|
|
|
"golang.org/x/tools/internal/typeparams"
|
2018-04-26 20:58:52 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
type intReader struct {
|
|
|
|
*bytes.Reader
|
|
|
|
path string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *intReader) int64() int64 {
|
|
|
|
i, err := binary.ReadVarint(r.Reader)
|
|
|
|
if err != nil {
|
|
|
|
errorf("import %q: read varint error: %v", r.path, err)
|
|
|
|
}
|
|
|
|
return i
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *intReader) uint64() uint64 {
|
|
|
|
i, err := binary.ReadUvarint(r.Reader)
|
|
|
|
if err != nil {
|
|
|
|
errorf("import %q: read varint error: %v", r.path, err)
|
|
|
|
}
|
|
|
|
return i
|
|
|
|
}
|
|
|
|
|
2021-09-14 20:07:03 +03:00
|
|
|
// Keep this in sync with constants in iexport.go.
|
|
|
|
const (
|
2022-01-11 02:46:59 +03:00
|
|
|
iexportVersionGo1_11 = 0
|
|
|
|
iexportVersionPosCol = 1
|
|
|
|
iexportVersionGo1_18 = 2
|
|
|
|
iexportVersionGenerics = 2
|
2022-10-20 21:03:47 +03:00
|
|
|
|
|
|
|
iexportVersionCurrent = 2
|
2021-09-14 20:07:03 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
type ident struct {
|
2022-04-14 22:47:16 +03:00
|
|
|
pkg *types.Package
|
2021-09-14 20:07:03 +03:00
|
|
|
name string
|
|
|
|
}
|
|
|
|
|
2018-04-26 20:58:52 +03:00
|
|
|
const predeclReserved = 32
|
|
|
|
|
|
|
|
type itag uint64
|
|
|
|
|
|
|
|
const (
|
|
|
|
// Types
|
|
|
|
definedType itag = iota
|
|
|
|
pointerType
|
|
|
|
sliceType
|
|
|
|
arrayType
|
|
|
|
chanType
|
|
|
|
mapType
|
|
|
|
signatureType
|
|
|
|
structType
|
|
|
|
interfaceType
|
2021-09-14 20:07:03 +03:00
|
|
|
typeParamType
|
2021-09-15 03:46:30 +03:00
|
|
|
instanceType
|
2021-09-14 20:07:03 +03:00
|
|
|
unionType
|
2018-04-26 20:58:52 +03:00
|
|
|
)
|
|
|
|
|
2018-06-13 08:33:41 +03:00
|
|
|
// IImportData imports a package from the serialized package data
|
2021-02-16 09:17:43 +03:00
|
|
|
// and returns 0 and a reference to the package.
|
2018-04-26 20:58:52 +03:00
|
|
|
// If the export data version is not recognized or the format is otherwise
|
|
|
|
// compromised, an error is returned.
|
2021-02-16 09:17:43 +03:00
|
|
|
func IImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (int, *types.Package, error) {
|
internal/gcimporter: supporting encoding objects from different packages
Through forwarded underlyings or type aliases, shallow export data may
reference fields or methods declared in other packages. However, the
importer was not able to canonicalize these fields or methods, because
unlike with deep export data, it had no index of types or objects in
other packages.
For example, consider:
package a
func A() *[]func() struct { X int }
package b
import "a"
var B = a.A()
The export data for package 'a' must record that the type graphs for A,
and B must share the subgraph for the interior node X, deeply within
both. With deep export data this is easy because it encodes several
packages in a single object graph, but shallow export data needs some
kind of identifier for object X deep within package b so it can be
located when b is imported independently. Objectpath is such a name.
Use the objectpath algorithm to encode these objects during shallow
export, and look them up during shallow import.
Running TestShallowStd in the internal/gcimporter package turned up only
one set of fields affected by this change: the use of the
fuzz.CorpusEntry alias from the testing/internal/testdeps package.
Therefore, the performance cost of objectpath is likely insignificant.
Also simplify exporting in iexport.go. Unlike during importing, there is
no need to track the current package.
Fixes golang/go#60676
Fixes golang/go#60819
Change-Id: I15ae14c5733d1cafd14aabd7150da4f7b851e733
Reviewed-on: https://go-review.googlesource.com/c/tools/+/503438
gopls-CI: kokoro <noreply+kokoro@google.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
2023-06-16 00:10:58 +03:00
|
|
|
pkgs, err := iimportCommon(fset, GetPackagesFromMap(imports), data, false, path, false, nil)
|
2021-02-16 09:17:43 +03:00
|
|
|
if err != nil {
|
|
|
|
return 0, nil, err
|
|
|
|
}
|
|
|
|
return 0, pkgs[0], nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// IImportBundle imports a set of packages from the serialized package bundle.
|
|
|
|
func IImportBundle(fset *token.FileSet, imports map[string]*types.Package, data []byte) ([]*types.Package, error) {
|
internal/gcimporter: supporting encoding objects from different packages
Through forwarded underlyings or type aliases, shallow export data may
reference fields or methods declared in other packages. However, the
importer was not able to canonicalize these fields or methods, because
unlike with deep export data, it had no index of types or objects in
other packages.
For example, consider:
package a
func A() *[]func() struct { X int }
package b
import "a"
var B = a.A()
The export data for package 'a' must record that the type graphs for A,
and B must share the subgraph for the interior node X, deeply within
both. With deep export data this is easy because it encodes several
packages in a single object graph, but shallow export data needs some
kind of identifier for object X deep within package b so it can be
located when b is imported independently. Objectpath is such a name.
Use the objectpath algorithm to encode these objects during shallow
export, and look them up during shallow import.
Running TestShallowStd in the internal/gcimporter package turned up only
one set of fields affected by this change: the use of the
fuzz.CorpusEntry alias from the testing/internal/testdeps package.
Therefore, the performance cost of objectpath is likely insignificant.
Also simplify exporting in iexport.go. Unlike during importing, there is
no need to track the current package.
Fixes golang/go#60676
Fixes golang/go#60819
Change-Id: I15ae14c5733d1cafd14aabd7150da4f7b851e733
Reviewed-on: https://go-review.googlesource.com/c/tools/+/503438
gopls-CI: kokoro <noreply+kokoro@google.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
2023-06-16 00:10:58 +03:00
|
|
|
return iimportCommon(fset, GetPackagesFromMap(imports), data, true, "", false, nil)
|
2021-02-16 09:17:43 +03:00
|
|
|
}
|
|
|
|
|
gopls/internal/lsp/cache: reduce importing in analysis
This CL is a substantial reorganization of the analysis driver to
ensure that export data is imported at most once per batch of packages
that are analyzed, instead of once per import edge. This greatly
reduces the amount of allocation and computation done during analysis.
In cache/analysis.go, Snapshot.Analyze (which now takes a set of
PackageIDs, instead of being called singly in a loop) constructs an
ephemeral DAG that mirrors the package graph, and then works in
parallel postorder over this graph doing analysis. It uses a single
FileSet for the whole batch of packages it creates. The subgraph
rooted at each node is effectively a types.Importer for that node,
as it represents the mapping from PackagePath to *types.Package.
We no longer bother with promises or invalidation. We rely on the fact
that the graph is relatively cheap to construct, cache hits are cheap
to process, and the whole process only occurs after an idle delay of
about a second.
Also:
- In internal/facts, optimize the fact decoder by using a callback.
Previously, it was spending a lot of time traversing the API of all
imports of a package to build a PackagePath-to-types.Package
mapping. For many packages in terraform-provider-aws this visits
over 1M objects (!!). But of course this is trivially computed from
the new representation.
- In internal/gcimporter, IImportShallow now uses a single callback to
get all the types.Package symbols from the client, potentially in
parallel (and that's what gopls does). The previous separation of
"create" and "populate" has gone away.
The analysis driver additionally exploits the getPackages callback to
efficiently read the package manifest of an export data file,
then abort with an error before proceeding to actually decode
the rest of the file.
With this change, we can process the internal/provider package of the
terraform-provider-aws repo in 20s cold, 4s hot. (Before, it would run
out of memory.)
$ go test -bench=InitialWorkspaceLoad/hashiform ./gopls/internal/regtest/bench
BenchmarkInitialWorkspaceLoad/hashiform-8 1 4014521793 ns/op 349570384 alloc_bytes 439230464 in_use_bytes 668992216 total_alloc_bytes
PASS
Fixes golang/go#60621
Change-Id: Iadeb02f57eb19dcccb639857053b897a60e0a90e
Reviewed-on: https://go-review.googlesource.com/c/tools/+/503195
Reviewed-by: Robert Findley <rfindley@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Alan Donovan <adonovan@google.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
2023-06-13 18:47:20 +03:00
|
|
|
// A GetPackagesFunc function obtains the non-nil symbols for a set of
|
|
|
|
// packages, creating and recursively importing them as needed. An
|
|
|
|
// implementation should store each package symbol is in the Pkg
|
|
|
|
// field of the items array.
|
2023-03-15 17:27:49 +03:00
|
|
|
//
|
gopls/internal/lsp/cache: reduce importing in analysis
This CL is a substantial reorganization of the analysis driver to
ensure that export data is imported at most once per batch of packages
that are analyzed, instead of once per import edge. This greatly
reduces the amount of allocation and computation done during analysis.
In cache/analysis.go, Snapshot.Analyze (which now takes a set of
PackageIDs, instead of being called singly in a loop) constructs an
ephemeral DAG that mirrors the package graph, and then works in
parallel postorder over this graph doing analysis. It uses a single
FileSet for the whole batch of packages it creates. The subgraph
rooted at each node is effectively a types.Importer for that node,
as it represents the mapping from PackagePath to *types.Package.
We no longer bother with promises or invalidation. We rely on the fact
that the graph is relatively cheap to construct, cache hits are cheap
to process, and the whole process only occurs after an idle delay of
about a second.
Also:
- In internal/facts, optimize the fact decoder by using a callback.
Previously, it was spending a lot of time traversing the API of all
imports of a package to build a PackagePath-to-types.Package
mapping. For many packages in terraform-provider-aws this visits
over 1M objects (!!). But of course this is trivially computed from
the new representation.
- In internal/gcimporter, IImportShallow now uses a single callback to
get all the types.Package symbols from the client, potentially in
parallel (and that's what gopls does). The previous separation of
"create" and "populate" has gone away.
The analysis driver additionally exploits the getPackages callback to
efficiently read the package manifest of an export data file,
then abort with an error before proceeding to actually decode
the rest of the file.
With this change, we can process the internal/provider package of the
terraform-provider-aws repo in 20s cold, 4s hot. (Before, it would run
out of memory.)
$ go test -bench=InitialWorkspaceLoad/hashiform ./gopls/internal/regtest/bench
BenchmarkInitialWorkspaceLoad/hashiform-8 1 4014521793 ns/op 349570384 alloc_bytes 439230464 in_use_bytes 668992216 total_alloc_bytes
PASS
Fixes golang/go#60621
Change-Id: Iadeb02f57eb19dcccb639857053b897a60e0a90e
Reviewed-on: https://go-review.googlesource.com/c/tools/+/503195
Reviewed-by: Robert Findley <rfindley@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Alan Donovan <adonovan@google.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
2023-06-13 18:47:20 +03:00
|
|
|
// Any error causes importing to fail. This can be used to quickly read
|
|
|
|
// the import manifest of an export data file without fully decoding it.
|
|
|
|
type GetPackagesFunc = func(items []GetPackagesItem) error
|
|
|
|
|
|
|
|
// A GetPackagesItem is a request from the importer for the package
|
|
|
|
// symbol of the specified name and path.
|
|
|
|
type GetPackagesItem struct {
|
|
|
|
Name, Path string
|
|
|
|
Pkg *types.Package // to be filled in by GetPackagesFunc call
|
|
|
|
|
|
|
|
// private importer state
|
|
|
|
pathOffset uint64
|
|
|
|
nameIndex map[string]uint64
|
|
|
|
}
|
2023-03-15 17:27:49 +03:00
|
|
|
|
gopls/internal/lsp/cache: reduce importing in analysis
This CL is a substantial reorganization of the analysis driver to
ensure that export data is imported at most once per batch of packages
that are analyzed, instead of once per import edge. This greatly
reduces the amount of allocation and computation done during analysis.
In cache/analysis.go, Snapshot.Analyze (which now takes a set of
PackageIDs, instead of being called singly in a loop) constructs an
ephemeral DAG that mirrors the package graph, and then works in
parallel postorder over this graph doing analysis. It uses a single
FileSet for the whole batch of packages it creates. The subgraph
rooted at each node is effectively a types.Importer for that node,
as it represents the mapping from PackagePath to *types.Package.
We no longer bother with promises or invalidation. We rely on the fact
that the graph is relatively cheap to construct, cache hits are cheap
to process, and the whole process only occurs after an idle delay of
about a second.
Also:
- In internal/facts, optimize the fact decoder by using a callback.
Previously, it was spending a lot of time traversing the API of all
imports of a package to build a PackagePath-to-types.Package
mapping. For many packages in terraform-provider-aws this visits
over 1M objects (!!). But of course this is trivially computed from
the new representation.
- In internal/gcimporter, IImportShallow now uses a single callback to
get all the types.Package symbols from the client, potentially in
parallel (and that's what gopls does). The previous separation of
"create" and "populate" has gone away.
The analysis driver additionally exploits the getPackages callback to
efficiently read the package manifest of an export data file,
then abort with an error before proceeding to actually decode
the rest of the file.
With this change, we can process the internal/provider package of the
terraform-provider-aws repo in 20s cold, 4s hot. (Before, it would run
out of memory.)
$ go test -bench=InitialWorkspaceLoad/hashiform ./gopls/internal/regtest/bench
BenchmarkInitialWorkspaceLoad/hashiform-8 1 4014521793 ns/op 349570384 alloc_bytes 439230464 in_use_bytes 668992216 total_alloc_bytes
PASS
Fixes golang/go#60621
Change-Id: Iadeb02f57eb19dcccb639857053b897a60e0a90e
Reviewed-on: https://go-review.googlesource.com/c/tools/+/503195
Reviewed-by: Robert Findley <rfindley@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Alan Donovan <adonovan@google.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
2023-06-13 18:47:20 +03:00
|
|
|
// GetPackagesFromMap returns a GetPackagesFunc that retrieves
|
|
|
|
// packages from the given map of package path to package.
|
2023-03-15 17:27:49 +03:00
|
|
|
//
|
gopls/internal/lsp/cache: reduce importing in analysis
This CL is a substantial reorganization of the analysis driver to
ensure that export data is imported at most once per batch of packages
that are analyzed, instead of once per import edge. This greatly
reduces the amount of allocation and computation done during analysis.
In cache/analysis.go, Snapshot.Analyze (which now takes a set of
PackageIDs, instead of being called singly in a loop) constructs an
ephemeral DAG that mirrors the package graph, and then works in
parallel postorder over this graph doing analysis. It uses a single
FileSet for the whole batch of packages it creates. The subgraph
rooted at each node is effectively a types.Importer for that node,
as it represents the mapping from PackagePath to *types.Package.
We no longer bother with promises or invalidation. We rely on the fact
that the graph is relatively cheap to construct, cache hits are cheap
to process, and the whole process only occurs after an idle delay of
about a second.
Also:
- In internal/facts, optimize the fact decoder by using a callback.
Previously, it was spending a lot of time traversing the API of all
imports of a package to build a PackagePath-to-types.Package
mapping. For many packages in terraform-provider-aws this visits
over 1M objects (!!). But of course this is trivially computed from
the new representation.
- In internal/gcimporter, IImportShallow now uses a single callback to
get all the types.Package symbols from the client, potentially in
parallel (and that's what gopls does). The previous separation of
"create" and "populate" has gone away.
The analysis driver additionally exploits the getPackages callback to
efficiently read the package manifest of an export data file,
then abort with an error before proceeding to actually decode
the rest of the file.
With this change, we can process the internal/provider package of the
terraform-provider-aws repo in 20s cold, 4s hot. (Before, it would run
out of memory.)
$ go test -bench=InitialWorkspaceLoad/hashiform ./gopls/internal/regtest/bench
BenchmarkInitialWorkspaceLoad/hashiform-8 1 4014521793 ns/op 349570384 alloc_bytes 439230464 in_use_bytes 668992216 total_alloc_bytes
PASS
Fixes golang/go#60621
Change-Id: Iadeb02f57eb19dcccb639857053b897a60e0a90e
Reviewed-on: https://go-review.googlesource.com/c/tools/+/503195
Reviewed-by: Robert Findley <rfindley@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Alan Donovan <adonovan@google.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
2023-06-13 18:47:20 +03:00
|
|
|
// The returned function may mutate m: each requested package that is not
|
|
|
|
// found is created with types.NewPackage and inserted into m.
|
|
|
|
func GetPackagesFromMap(m map[string]*types.Package) GetPackagesFunc {
|
|
|
|
return func(items []GetPackagesItem) error {
|
|
|
|
for i, item := range items {
|
|
|
|
pkg, ok := m[item.Path]
|
|
|
|
if !ok {
|
|
|
|
pkg = types.NewPackage(item.Path, item.Name)
|
|
|
|
m[item.Path] = pkg
|
|
|
|
}
|
|
|
|
items[i].Pkg = pkg
|
2023-03-15 17:27:49 +03:00
|
|
|
}
|
gopls/internal/lsp/cache: reduce importing in analysis
This CL is a substantial reorganization of the analysis driver to
ensure that export data is imported at most once per batch of packages
that are analyzed, instead of once per import edge. This greatly
reduces the amount of allocation and computation done during analysis.
In cache/analysis.go, Snapshot.Analyze (which now takes a set of
PackageIDs, instead of being called singly in a loop) constructs an
ephemeral DAG that mirrors the package graph, and then works in
parallel postorder over this graph doing analysis. It uses a single
FileSet for the whole batch of packages it creates. The subgraph
rooted at each node is effectively a types.Importer for that node,
as it represents the mapping from PackagePath to *types.Package.
We no longer bother with promises or invalidation. We rely on the fact
that the graph is relatively cheap to construct, cache hits are cheap
to process, and the whole process only occurs after an idle delay of
about a second.
Also:
- In internal/facts, optimize the fact decoder by using a callback.
Previously, it was spending a lot of time traversing the API of all
imports of a package to build a PackagePath-to-types.Package
mapping. For many packages in terraform-provider-aws this visits
over 1M objects (!!). But of course this is trivially computed from
the new representation.
- In internal/gcimporter, IImportShallow now uses a single callback to
get all the types.Package symbols from the client, potentially in
parallel (and that's what gopls does). The previous separation of
"create" and "populate" has gone away.
The analysis driver additionally exploits the getPackages callback to
efficiently read the package manifest of an export data file,
then abort with an error before proceeding to actually decode
the rest of the file.
With this change, we can process the internal/provider package of the
terraform-provider-aws repo in 20s cold, 4s hot. (Before, it would run
out of memory.)
$ go test -bench=InitialWorkspaceLoad/hashiform ./gopls/internal/regtest/bench
BenchmarkInitialWorkspaceLoad/hashiform-8 1 4014521793 ns/op 349570384 alloc_bytes 439230464 in_use_bytes 668992216 total_alloc_bytes
PASS
Fixes golang/go#60621
Change-Id: Iadeb02f57eb19dcccb639857053b897a60e0a90e
Reviewed-on: https://go-review.googlesource.com/c/tools/+/503195
Reviewed-by: Robert Findley <rfindley@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Alan Donovan <adonovan@google.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
2023-06-13 18:47:20 +03:00
|
|
|
return nil
|
2023-03-15 17:27:49 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
internal/gcimporter: supporting encoding objects from different packages
Through forwarded underlyings or type aliases, shallow export data may
reference fields or methods declared in other packages. However, the
importer was not able to canonicalize these fields or methods, because
unlike with deep export data, it had no index of types or objects in
other packages.
For example, consider:
package a
func A() *[]func() struct { X int }
package b
import "a"
var B = a.A()
The export data for package 'a' must record that the type graphs for A,
and B must share the subgraph for the interior node X, deeply within
both. With deep export data this is easy because it encodes several
packages in a single object graph, but shallow export data needs some
kind of identifier for object X deep within package b so it can be
located when b is imported independently. Objectpath is such a name.
Use the objectpath algorithm to encode these objects during shallow
export, and look them up during shallow import.
Running TestShallowStd in the internal/gcimporter package turned up only
one set of fields affected by this change: the use of the
fuzz.CorpusEntry alias from the testing/internal/testdeps package.
Therefore, the performance cost of objectpath is likely insignificant.
Also simplify exporting in iexport.go. Unlike during importing, there is
no need to track the current package.
Fixes golang/go#60676
Fixes golang/go#60819
Change-Id: I15ae14c5733d1cafd14aabd7150da4f7b851e733
Reviewed-on: https://go-review.googlesource.com/c/tools/+/503438
gopls-CI: kokoro <noreply+kokoro@google.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
2023-06-16 00:10:58 +03:00
|
|
|
func iimportCommon(fset *token.FileSet, getPackages GetPackagesFunc, data []byte, bundle bool, path string, shallow bool, reportf ReportFunc) (pkgs []*types.Package, err error) {
|
2022-10-20 21:03:47 +03:00
|
|
|
const currentVersion = iexportVersionCurrent
|
2019-09-27 03:38:16 +03:00
|
|
|
version := int64(-1)
|
2021-09-20 17:42:55 +03:00
|
|
|
if !debug {
|
|
|
|
defer func() {
|
|
|
|
if e := recover(); e != nil {
|
2021-04-15 19:49:06 +03:00
|
|
|
if bundle {
|
|
|
|
err = fmt.Errorf("%v", e)
|
|
|
|
} else if version > currentVersion {
|
2021-09-20 17:42:55 +03:00
|
|
|
err = fmt.Errorf("cannot import %q (%v), export data is newer version - update tool", path, e)
|
|
|
|
} else {
|
2023-05-12 22:32:35 +03:00
|
|
|
err = fmt.Errorf("internal error while importing %q (%v); please report an issue", path, e)
|
2021-09-20 17:42:55 +03:00
|
|
|
}
|
2018-06-13 08:33:41 +03:00
|
|
|
}
|
2021-09-20 17:42:55 +03:00
|
|
|
}()
|
|
|
|
}
|
2018-06-13 08:33:41 +03:00
|
|
|
|
2018-04-26 20:58:52 +03:00
|
|
|
r := &intReader{bytes.NewReader(data), path}
|
|
|
|
|
2021-02-16 09:17:43 +03:00
|
|
|
if bundle {
|
2023-05-12 22:32:35 +03:00
|
|
|
if v := r.uint64(); v != bundleVersion {
|
|
|
|
errorf("unknown bundle format version %d", v)
|
2021-02-16 09:17:43 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-27 03:38:16 +03:00
|
|
|
version = int64(r.uint64())
|
2018-04-26 20:58:52 +03:00
|
|
|
switch version {
|
2021-10-22 18:02:26 +03:00
|
|
|
case iexportVersionGo1_18, iexportVersionPosCol, iexportVersionGo1_11:
|
2018-04-26 20:58:52 +03:00
|
|
|
default:
|
2021-10-22 18:02:26 +03:00
|
|
|
if version > iexportVersionGo1_18 {
|
2021-09-14 20:07:03 +03:00
|
|
|
errorf("unstable iexport format version %d, just rebuild compiler and std library", version)
|
|
|
|
} else {
|
|
|
|
errorf("unknown iexport format version %d", version)
|
|
|
|
}
|
2018-04-26 20:58:52 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
sLen := int64(r.uint64())
|
2023-01-09 21:44:29 +03:00
|
|
|
var fLen int64
|
2023-01-13 19:44:12 +03:00
|
|
|
var fileOffset []uint64
|
gopls/internal/lsp/cache: reduce importing in analysis
This CL is a substantial reorganization of the analysis driver to
ensure that export data is imported at most once per batch of packages
that are analyzed, instead of once per import edge. This greatly
reduces the amount of allocation and computation done during analysis.
In cache/analysis.go, Snapshot.Analyze (which now takes a set of
PackageIDs, instead of being called singly in a loop) constructs an
ephemeral DAG that mirrors the package graph, and then works in
parallel postorder over this graph doing analysis. It uses a single
FileSet for the whole batch of packages it creates. The subgraph
rooted at each node is effectively a types.Importer for that node,
as it represents the mapping from PackagePath to *types.Package.
We no longer bother with promises or invalidation. We rely on the fact
that the graph is relatively cheap to construct, cache hits are cheap
to process, and the whole process only occurs after an idle delay of
about a second.
Also:
- In internal/facts, optimize the fact decoder by using a callback.
Previously, it was spending a lot of time traversing the API of all
imports of a package to build a PackagePath-to-types.Package
mapping. For many packages in terraform-provider-aws this visits
over 1M objects (!!). But of course this is trivially computed from
the new representation.
- In internal/gcimporter, IImportShallow now uses a single callback to
get all the types.Package symbols from the client, potentially in
parallel (and that's what gopls does). The previous separation of
"create" and "populate" has gone away.
The analysis driver additionally exploits the getPackages callback to
efficiently read the package manifest of an export data file,
then abort with an error before proceeding to actually decode
the rest of the file.
With this change, we can process the internal/provider package of the
terraform-provider-aws repo in 20s cold, 4s hot. (Before, it would run
out of memory.)
$ go test -bench=InitialWorkspaceLoad/hashiform ./gopls/internal/regtest/bench
BenchmarkInitialWorkspaceLoad/hashiform-8 1 4014521793 ns/op 349570384 alloc_bytes 439230464 in_use_bytes 668992216 total_alloc_bytes
PASS
Fixes golang/go#60621
Change-Id: Iadeb02f57eb19dcccb639857053b897a60e0a90e
Reviewed-on: https://go-review.googlesource.com/c/tools/+/503195
Reviewed-by: Robert Findley <rfindley@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Alan Donovan <adonovan@google.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
2023-06-13 18:47:20 +03:00
|
|
|
if shallow {
|
2023-01-13 19:44:12 +03:00
|
|
|
// Shallow mode uses a different position encoding.
|
2023-01-09 21:44:29 +03:00
|
|
|
fLen = int64(r.uint64())
|
2023-01-13 19:44:12 +03:00
|
|
|
fileOffset = make([]uint64, r.uint64())
|
|
|
|
for i := range fileOffset {
|
|
|
|
fileOffset[i] = r.uint64()
|
|
|
|
}
|
2023-01-09 21:44:29 +03:00
|
|
|
}
|
2018-04-26 20:58:52 +03:00
|
|
|
dLen := int64(r.uint64())
|
|
|
|
|
|
|
|
whence, _ := r.Seek(0, io.SeekCurrent)
|
|
|
|
stringData := data[whence : whence+sLen]
|
2023-01-09 21:44:29 +03:00
|
|
|
fileData := data[whence+sLen : whence+sLen+fLen]
|
|
|
|
declData := data[whence+sLen+fLen : whence+sLen+fLen+dLen]
|
|
|
|
r.Seek(sLen+fLen+dLen, io.SeekCurrent)
|
2018-04-26 20:58:52 +03:00
|
|
|
|
|
|
|
p := iimporter{
|
internal/gcimporter: supporting encoding objects from different packages
Through forwarded underlyings or type aliases, shallow export data may
reference fields or methods declared in other packages. However, the
importer was not able to canonicalize these fields or methods, because
unlike with deep export data, it had no index of types or objects in
other packages.
For example, consider:
package a
func A() *[]func() struct { X int }
package b
import "a"
var B = a.A()
The export data for package 'a' must record that the type graphs for A,
and B must share the subgraph for the interior node X, deeply within
both. With deep export data this is easy because it encodes several
packages in a single object graph, but shallow export data needs some
kind of identifier for object X deep within package b so it can be
located when b is imported independently. Objectpath is such a name.
Use the objectpath algorithm to encode these objects during shallow
export, and look them up during shallow import.
Running TestShallowStd in the internal/gcimporter package turned up only
one set of fields affected by this change: the use of the
fuzz.CorpusEntry alias from the testing/internal/testdeps package.
Therefore, the performance cost of objectpath is likely insignificant.
Also simplify exporting in iexport.go. Unlike during importing, there is
no need to track the current package.
Fixes golang/go#60676
Fixes golang/go#60819
Change-Id: I15ae14c5733d1cafd14aabd7150da4f7b851e733
Reviewed-on: https://go-review.googlesource.com/c/tools/+/503438
gopls-CI: kokoro <noreply+kokoro@google.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
2023-06-16 00:10:58 +03:00
|
|
|
version: int(version),
|
|
|
|
ipath: path,
|
|
|
|
shallow: shallow,
|
|
|
|
reportf: reportf,
|
2018-04-26 20:58:52 +03:00
|
|
|
|
|
|
|
stringData: stringData,
|
|
|
|
stringCache: make(map[uint64]string),
|
2023-01-13 19:44:12 +03:00
|
|
|
fileOffset: fileOffset,
|
2023-01-09 21:44:29 +03:00
|
|
|
fileData: fileData,
|
2023-01-13 19:44:12 +03:00
|
|
|
fileCache: make([]*token.File, len(fileOffset)),
|
2018-04-26 20:58:52 +03:00
|
|
|
pkgCache: make(map[uint64]*types.Package),
|
|
|
|
|
|
|
|
declData: declData,
|
|
|
|
pkgIndex: make(map[*types.Package]map[string]uint64),
|
|
|
|
typCache: make(map[uint64]types.Type),
|
2021-09-14 20:07:03 +03:00
|
|
|
// Separate map for typeparams, keyed by their package and unique
|
2021-10-18 19:23:03 +03:00
|
|
|
// name.
|
2021-09-14 20:07:03 +03:00
|
|
|
tparamIndex: make(map[ident]types.Type),
|
2018-04-26 20:58:52 +03:00
|
|
|
|
|
|
|
fake: fakeFileSet{
|
|
|
|
fset: fset,
|
2021-10-20 18:02:53 +03:00
|
|
|
files: make(map[string]*fileInfo),
|
2018-04-26 20:58:52 +03:00
|
|
|
},
|
|
|
|
}
|
2021-10-20 18:02:53 +03:00
|
|
|
defer p.fake.setLines() // set lines for files in fset
|
2018-04-26 20:58:52 +03:00
|
|
|
|
2019-01-07 22:50:49 +03:00
|
|
|
for i, pt := range predeclared() {
|
2018-04-26 20:58:52 +03:00
|
|
|
p.typCache[uint64(i)] = pt
|
|
|
|
}
|
|
|
|
|
gopls/internal/lsp/cache: reduce importing in analysis
This CL is a substantial reorganization of the analysis driver to
ensure that export data is imported at most once per batch of packages
that are analyzed, instead of once per import edge. This greatly
reduces the amount of allocation and computation done during analysis.
In cache/analysis.go, Snapshot.Analyze (which now takes a set of
PackageIDs, instead of being called singly in a loop) constructs an
ephemeral DAG that mirrors the package graph, and then works in
parallel postorder over this graph doing analysis. It uses a single
FileSet for the whole batch of packages it creates. The subgraph
rooted at each node is effectively a types.Importer for that node,
as it represents the mapping from PackagePath to *types.Package.
We no longer bother with promises or invalidation. We rely on the fact
that the graph is relatively cheap to construct, cache hits are cheap
to process, and the whole process only occurs after an idle delay of
about a second.
Also:
- In internal/facts, optimize the fact decoder by using a callback.
Previously, it was spending a lot of time traversing the API of all
imports of a package to build a PackagePath-to-types.Package
mapping. For many packages in terraform-provider-aws this visits
over 1M objects (!!). But of course this is trivially computed from
the new representation.
- In internal/gcimporter, IImportShallow now uses a single callback to
get all the types.Package symbols from the client, potentially in
parallel (and that's what gopls does). The previous separation of
"create" and "populate" has gone away.
The analysis driver additionally exploits the getPackages callback to
efficiently read the package manifest of an export data file,
then abort with an error before proceeding to actually decode
the rest of the file.
With this change, we can process the internal/provider package of the
terraform-provider-aws repo in 20s cold, 4s hot. (Before, it would run
out of memory.)
$ go test -bench=InitialWorkspaceLoad/hashiform ./gopls/internal/regtest/bench
BenchmarkInitialWorkspaceLoad/hashiform-8 1 4014521793 ns/op 349570384 alloc_bytes 439230464 in_use_bytes 668992216 total_alloc_bytes
PASS
Fixes golang/go#60621
Change-Id: Iadeb02f57eb19dcccb639857053b897a60e0a90e
Reviewed-on: https://go-review.googlesource.com/c/tools/+/503195
Reviewed-by: Robert Findley <rfindley@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Alan Donovan <adonovan@google.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
2023-06-13 18:47:20 +03:00
|
|
|
// Gather the relevant packages from the manifest.
|
|
|
|
items := make([]GetPackagesItem, r.uint64())
|
|
|
|
for i := range items {
|
2018-04-26 20:58:52 +03:00
|
|
|
pkgPathOff := r.uint64()
|
|
|
|
pkgPath := p.stringAt(pkgPathOff)
|
|
|
|
pkgName := p.stringAt(r.uint64())
|
|
|
|
_ = r.uint64() // package height; unused by go/types
|
|
|
|
|
|
|
|
if pkgPath == "" {
|
|
|
|
pkgPath = path
|
|
|
|
}
|
gopls/internal/lsp/cache: reduce importing in analysis
This CL is a substantial reorganization of the analysis driver to
ensure that export data is imported at most once per batch of packages
that are analyzed, instead of once per import edge. This greatly
reduces the amount of allocation and computation done during analysis.
In cache/analysis.go, Snapshot.Analyze (which now takes a set of
PackageIDs, instead of being called singly in a loop) constructs an
ephemeral DAG that mirrors the package graph, and then works in
parallel postorder over this graph doing analysis. It uses a single
FileSet for the whole batch of packages it creates. The subgraph
rooted at each node is effectively a types.Importer for that node,
as it represents the mapping from PackagePath to *types.Package.
We no longer bother with promises or invalidation. We rely on the fact
that the graph is relatively cheap to construct, cache hits are cheap
to process, and the whole process only occurs after an idle delay of
about a second.
Also:
- In internal/facts, optimize the fact decoder by using a callback.
Previously, it was spending a lot of time traversing the API of all
imports of a package to build a PackagePath-to-types.Package
mapping. For many packages in terraform-provider-aws this visits
over 1M objects (!!). But of course this is trivially computed from
the new representation.
- In internal/gcimporter, IImportShallow now uses a single callback to
get all the types.Package symbols from the client, potentially in
parallel (and that's what gopls does). The previous separation of
"create" and "populate" has gone away.
The analysis driver additionally exploits the getPackages callback to
efficiently read the package manifest of an export data file,
then abort with an error before proceeding to actually decode
the rest of the file.
With this change, we can process the internal/provider package of the
terraform-provider-aws repo in 20s cold, 4s hot. (Before, it would run
out of memory.)
$ go test -bench=InitialWorkspaceLoad/hashiform ./gopls/internal/regtest/bench
BenchmarkInitialWorkspaceLoad/hashiform-8 1 4014521793 ns/op 349570384 alloc_bytes 439230464 in_use_bytes 668992216 total_alloc_bytes
PASS
Fixes golang/go#60621
Change-Id: Iadeb02f57eb19dcccb639857053b897a60e0a90e
Reviewed-on: https://go-review.googlesource.com/c/tools/+/503195
Reviewed-by: Robert Findley <rfindley@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Alan Donovan <adonovan@google.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
2023-06-13 18:47:20 +03:00
|
|
|
items[i].Name = pkgName
|
|
|
|
items[i].Path = pkgPath
|
|
|
|
items[i].pathOffset = pkgPathOff
|
2018-04-26 20:58:52 +03:00
|
|
|
|
2022-11-03 21:55:29 +03:00
|
|
|
// Read index for package.
|
2018-04-26 20:58:52 +03:00
|
|
|
nameIndex := make(map[string]uint64)
|
2022-11-03 21:55:29 +03:00
|
|
|
nSyms := r.uint64()
|
gopls/internal/lsp/cache: reduce importing in analysis
This CL is a substantial reorganization of the analysis driver to
ensure that export data is imported at most once per batch of packages
that are analyzed, instead of once per import edge. This greatly
reduces the amount of allocation and computation done during analysis.
In cache/analysis.go, Snapshot.Analyze (which now takes a set of
PackageIDs, instead of being called singly in a loop) constructs an
ephemeral DAG that mirrors the package graph, and then works in
parallel postorder over this graph doing analysis. It uses a single
FileSet for the whole batch of packages it creates. The subgraph
rooted at each node is effectively a types.Importer for that node,
as it represents the mapping from PackagePath to *types.Package.
We no longer bother with promises or invalidation. We rely on the fact
that the graph is relatively cheap to construct, cache hits are cheap
to process, and the whole process only occurs after an idle delay of
about a second.
Also:
- In internal/facts, optimize the fact decoder by using a callback.
Previously, it was spending a lot of time traversing the API of all
imports of a package to build a PackagePath-to-types.Package
mapping. For many packages in terraform-provider-aws this visits
over 1M objects (!!). But of course this is trivially computed from
the new representation.
- In internal/gcimporter, IImportShallow now uses a single callback to
get all the types.Package symbols from the client, potentially in
parallel (and that's what gopls does). The previous separation of
"create" and "populate" has gone away.
The analysis driver additionally exploits the getPackages callback to
efficiently read the package manifest of an export data file,
then abort with an error before proceeding to actually decode
the rest of the file.
With this change, we can process the internal/provider package of the
terraform-provider-aws repo in 20s cold, 4s hot. (Before, it would run
out of memory.)
$ go test -bench=InitialWorkspaceLoad/hashiform ./gopls/internal/regtest/bench
BenchmarkInitialWorkspaceLoad/hashiform-8 1 4014521793 ns/op 349570384 alloc_bytes 439230464 in_use_bytes 668992216 total_alloc_bytes
PASS
Fixes golang/go#60621
Change-Id: Iadeb02f57eb19dcccb639857053b897a60e0a90e
Reviewed-on: https://go-review.googlesource.com/c/tools/+/503195
Reviewed-by: Robert Findley <rfindley@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Alan Donovan <adonovan@google.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
2023-06-13 18:47:20 +03:00
|
|
|
// In shallow mode, only the current package (i=0) has an index.
|
|
|
|
assert(!(shallow && i > 0 && nSyms != 0))
|
2022-11-03 21:55:29 +03:00
|
|
|
for ; nSyms > 0; nSyms-- {
|
2018-04-26 20:58:52 +03:00
|
|
|
name := p.stringAt(r.uint64())
|
|
|
|
nameIndex[name] = r.uint64()
|
|
|
|
}
|
|
|
|
|
gopls/internal/lsp/cache: reduce importing in analysis
This CL is a substantial reorganization of the analysis driver to
ensure that export data is imported at most once per batch of packages
that are analyzed, instead of once per import edge. This greatly
reduces the amount of allocation and computation done during analysis.
In cache/analysis.go, Snapshot.Analyze (which now takes a set of
PackageIDs, instead of being called singly in a loop) constructs an
ephemeral DAG that mirrors the package graph, and then works in
parallel postorder over this graph doing analysis. It uses a single
FileSet for the whole batch of packages it creates. The subgraph
rooted at each node is effectively a types.Importer for that node,
as it represents the mapping from PackagePath to *types.Package.
We no longer bother with promises or invalidation. We rely on the fact
that the graph is relatively cheap to construct, cache hits are cheap
to process, and the whole process only occurs after an idle delay of
about a second.
Also:
- In internal/facts, optimize the fact decoder by using a callback.
Previously, it was spending a lot of time traversing the API of all
imports of a package to build a PackagePath-to-types.Package
mapping. For many packages in terraform-provider-aws this visits
over 1M objects (!!). But of course this is trivially computed from
the new representation.
- In internal/gcimporter, IImportShallow now uses a single callback to
get all the types.Package symbols from the client, potentially in
parallel (and that's what gopls does). The previous separation of
"create" and "populate" has gone away.
The analysis driver additionally exploits the getPackages callback to
efficiently read the package manifest of an export data file,
then abort with an error before proceeding to actually decode
the rest of the file.
With this change, we can process the internal/provider package of the
terraform-provider-aws repo in 20s cold, 4s hot. (Before, it would run
out of memory.)
$ go test -bench=InitialWorkspaceLoad/hashiform ./gopls/internal/regtest/bench
BenchmarkInitialWorkspaceLoad/hashiform-8 1 4014521793 ns/op 349570384 alloc_bytes 439230464 in_use_bytes 668992216 total_alloc_bytes
PASS
Fixes golang/go#60621
Change-Id: Iadeb02f57eb19dcccb639857053b897a60e0a90e
Reviewed-on: https://go-review.googlesource.com/c/tools/+/503195
Reviewed-by: Robert Findley <rfindley@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Alan Donovan <adonovan@google.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
2023-06-13 18:47:20 +03:00
|
|
|
items[i].nameIndex = nameIndex
|
|
|
|
}
|
|
|
|
|
|
|
|
// Request packages all at once from the client,
|
|
|
|
// enabling a parallel implementation.
|
|
|
|
if err := getPackages(items); err != nil {
|
|
|
|
return nil, err // don't wrap this error
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the results and complete the index.
|
|
|
|
pkgList := make([]*types.Package, len(items))
|
|
|
|
for i, item := range items {
|
|
|
|
pkg := item.Pkg
|
|
|
|
if pkg == nil {
|
|
|
|
errorf("internal error: getPackages returned nil package for %q", item.Path)
|
|
|
|
} else if pkg.Path() != item.Path {
|
|
|
|
errorf("internal error: getPackages returned wrong path %q, want %q", pkg.Path(), item.Path)
|
|
|
|
} else if pkg.Name() != item.Name {
|
|
|
|
errorf("internal error: getPackages returned wrong name %s for package %q, want %s", pkg.Name(), item.Path, item.Name)
|
|
|
|
}
|
|
|
|
p.pkgCache[item.pathOffset] = pkg
|
|
|
|
p.pkgIndex[pkg] = item.nameIndex
|
2018-04-26 20:58:52 +03:00
|
|
|
pkgList[i] = pkg
|
|
|
|
}
|
2021-02-16 09:17:43 +03:00
|
|
|
|
|
|
|
if bundle {
|
|
|
|
pkgs = make([]*types.Package, r.uint64())
|
|
|
|
for i := range pkgs {
|
|
|
|
pkg := p.pkgAt(r.uint64())
|
|
|
|
imps := make([]*types.Package, r.uint64())
|
|
|
|
for j := range imps {
|
|
|
|
imps[j] = p.pkgAt(r.uint64())
|
|
|
|
}
|
|
|
|
pkg.SetImports(imps)
|
|
|
|
pkgs[i] = pkg
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if len(pkgList) == 0 {
|
|
|
|
errorf("no packages found for %s", path)
|
|
|
|
panic("unreachable")
|
|
|
|
}
|
|
|
|
pkgs = pkgList[:1]
|
|
|
|
|
|
|
|
// record all referenced packages as imports
|
|
|
|
list := append(([]*types.Package)(nil), pkgList[1:]...)
|
|
|
|
sort.Sort(byPath(list))
|
|
|
|
pkgs[0].SetImports(list)
|
2018-04-26 20:58:52 +03:00
|
|
|
}
|
2021-02-16 09:17:43 +03:00
|
|
|
|
|
|
|
for _, pkg := range pkgs {
|
|
|
|
if pkg.Complete() {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
names := make([]string, 0, len(p.pkgIndex[pkg]))
|
|
|
|
for name := range p.pkgIndex[pkg] {
|
|
|
|
names = append(names, name)
|
|
|
|
}
|
|
|
|
sort.Strings(names)
|
|
|
|
for _, name := range names {
|
|
|
|
p.doDecl(pkg, name)
|
|
|
|
}
|
|
|
|
|
|
|
|
// package was imported completely and without errors
|
|
|
|
pkg.MarkComplete()
|
2018-04-26 20:58:52 +03:00
|
|
|
}
|
|
|
|
|
2022-02-22 17:47:31 +03:00
|
|
|
// SetConstraint can't be called if the constraint type is not yet complete.
|
|
|
|
// When type params are created in the 'P' case of (*importReader).obj(),
|
|
|
|
// the associated constraint type may not be complete due to recursion.
|
|
|
|
// Therefore, we defer calling SetConstraint there, and call it here instead
|
|
|
|
// after all types are complete.
|
|
|
|
for _, d := range p.later {
|
|
|
|
typeparams.SetTypeParamConstraint(d.t, d.constraint)
|
|
|
|
}
|
|
|
|
|
2018-04-26 20:58:52 +03:00
|
|
|
for _, typ := range p.interfaceList {
|
|
|
|
typ.Complete()
|
|
|
|
}
|
|
|
|
|
2023-07-25 17:44:06 +03:00
|
|
|
// Workaround for golang/go#61561. See the doc for instanceList for details.
|
|
|
|
for _, typ := range p.instanceList {
|
|
|
|
if iface, _ := typ.Underlying().(*types.Interface); iface != nil {
|
|
|
|
iface.Complete()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-16 09:17:43 +03:00
|
|
|
return pkgs, nil
|
2018-04-26 20:58:52 +03:00
|
|
|
}
|
|
|
|
|
2022-02-22 17:47:31 +03:00
|
|
|
type setConstraintArgs struct {
|
|
|
|
t *typeparams.TypeParam
|
|
|
|
constraint types.Type
|
|
|
|
}
|
|
|
|
|
2018-04-26 20:58:52 +03:00
|
|
|
type iimporter struct {
|
2021-10-22 18:32:16 +03:00
|
|
|
version int
|
|
|
|
ipath string
|
2018-04-26 20:58:52 +03:00
|
|
|
|
internal/gcimporter: supporting encoding objects from different packages
Through forwarded underlyings or type aliases, shallow export data may
reference fields or methods declared in other packages. However, the
importer was not able to canonicalize these fields or methods, because
unlike with deep export data, it had no index of types or objects in
other packages.
For example, consider:
package a
func A() *[]func() struct { X int }
package b
import "a"
var B = a.A()
The export data for package 'a' must record that the type graphs for A,
and B must share the subgraph for the interior node X, deeply within
both. With deep export data this is easy because it encodes several
packages in a single object graph, but shallow export data needs some
kind of identifier for object X deep within package b so it can be
located when b is imported independently. Objectpath is such a name.
Use the objectpath algorithm to encode these objects during shallow
export, and look them up during shallow import.
Running TestShallowStd in the internal/gcimporter package turned up only
one set of fields affected by this change: the use of the
fuzz.CorpusEntry alias from the testing/internal/testdeps package.
Therefore, the performance cost of objectpath is likely insignificant.
Also simplify exporting in iexport.go. Unlike during importing, there is
no need to track the current package.
Fixes golang/go#60676
Fixes golang/go#60819
Change-Id: I15ae14c5733d1cafd14aabd7150da4f7b851e733
Reviewed-on: https://go-review.googlesource.com/c/tools/+/503438
gopls-CI: kokoro <noreply+kokoro@google.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
2023-06-16 00:10:58 +03:00
|
|
|
shallow bool
|
|
|
|
reportf ReportFunc // if non-nil, used to report bugs
|
2022-11-03 21:55:29 +03:00
|
|
|
|
2018-04-26 20:58:52 +03:00
|
|
|
stringData []byte
|
|
|
|
stringCache map[uint64]string
|
2023-01-13 19:44:12 +03:00
|
|
|
fileOffset []uint64 // fileOffset[i] is offset in fileData for info about file encoded as i
|
2023-01-09 21:44:29 +03:00
|
|
|
fileData []byte
|
2023-01-13 19:44:12 +03:00
|
|
|
fileCache []*token.File // memoized decoding of file encoded as i
|
2018-04-26 20:58:52 +03:00
|
|
|
pkgCache map[uint64]*types.Package
|
|
|
|
|
2021-09-14 20:07:03 +03:00
|
|
|
declData []byte
|
|
|
|
pkgIndex map[*types.Package]map[string]uint64
|
|
|
|
typCache map[uint64]types.Type
|
|
|
|
tparamIndex map[ident]types.Type
|
2018-04-26 20:58:52 +03:00
|
|
|
|
|
|
|
fake fakeFileSet
|
|
|
|
interfaceList []*types.Interface
|
2021-10-18 19:23:03 +03:00
|
|
|
|
2023-07-25 17:44:06 +03:00
|
|
|
// Workaround for the go/types bug golang/go#61561: instances produced during
|
|
|
|
// instantiation may contain incomplete interfaces. Here we only complete the
|
|
|
|
// underlying type of the instance, which is the most common case but doesn't
|
|
|
|
// handle parameterized interface literals defined deeper in the type.
|
|
|
|
instanceList []types.Type // instances for later completion (see golang/go#61561)
|
|
|
|
|
2022-02-22 17:47:31 +03:00
|
|
|
// Arguments for calls to SetConstraint that are deferred due to recursive types
|
|
|
|
later []setConstraintArgs
|
|
|
|
|
2021-10-18 19:23:03 +03:00
|
|
|
indent int // for tracing support
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *iimporter) trace(format string, args ...interface{}) {
|
|
|
|
if !trace {
|
|
|
|
// Call sites should also be guarded, but having this check here allows
|
|
|
|
// easily enabling/disabling debug trace statements.
|
|
|
|
return
|
|
|
|
}
|
|
|
|
fmt.Printf(strings.Repeat("..", p.indent)+format+"\n", args...)
|
2018-04-26 20:58:52 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func (p *iimporter) doDecl(pkg *types.Package, name string) {
|
2021-10-18 19:23:03 +03:00
|
|
|
if debug {
|
|
|
|
p.trace("import decl %s", name)
|
|
|
|
p.indent++
|
|
|
|
defer func() {
|
|
|
|
p.indent--
|
|
|
|
p.trace("=> %s", name)
|
|
|
|
}()
|
|
|
|
}
|
2018-04-26 20:58:52 +03:00
|
|
|
// See if we've already imported this declaration.
|
|
|
|
if obj := pkg.Scope().Lookup(name); obj != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
off, ok := p.pkgIndex[pkg][name]
|
|
|
|
if !ok {
|
gopls/internal/lsp/cache: reduce importing in analysis
This CL is a substantial reorganization of the analysis driver to
ensure that export data is imported at most once per batch of packages
that are analyzed, instead of once per import edge. This greatly
reduces the amount of allocation and computation done during analysis.
In cache/analysis.go, Snapshot.Analyze (which now takes a set of
PackageIDs, instead of being called singly in a loop) constructs an
ephemeral DAG that mirrors the package graph, and then works in
parallel postorder over this graph doing analysis. It uses a single
FileSet for the whole batch of packages it creates. The subgraph
rooted at each node is effectively a types.Importer for that node,
as it represents the mapping from PackagePath to *types.Package.
We no longer bother with promises or invalidation. We rely on the fact
that the graph is relatively cheap to construct, cache hits are cheap
to process, and the whole process only occurs after an idle delay of
about a second.
Also:
- In internal/facts, optimize the fact decoder by using a callback.
Previously, it was spending a lot of time traversing the API of all
imports of a package to build a PackagePath-to-types.Package
mapping. For many packages in terraform-provider-aws this visits
over 1M objects (!!). But of course this is trivially computed from
the new representation.
- In internal/gcimporter, IImportShallow now uses a single callback to
get all the types.Package symbols from the client, potentially in
parallel (and that's what gopls does). The previous separation of
"create" and "populate" has gone away.
The analysis driver additionally exploits the getPackages callback to
efficiently read the package manifest of an export data file,
then abort with an error before proceeding to actually decode
the rest of the file.
With this change, we can process the internal/provider package of the
terraform-provider-aws repo in 20s cold, 4s hot. (Before, it would run
out of memory.)
$ go test -bench=InitialWorkspaceLoad/hashiform ./gopls/internal/regtest/bench
BenchmarkInitialWorkspaceLoad/hashiform-8 1 4014521793 ns/op 349570384 alloc_bytes 439230464 in_use_bytes 668992216 total_alloc_bytes
PASS
Fixes golang/go#60621
Change-Id: Iadeb02f57eb19dcccb639857053b897a60e0a90e
Reviewed-on: https://go-review.googlesource.com/c/tools/+/503195
Reviewed-by: Robert Findley <rfindley@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Alan Donovan <adonovan@google.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
2023-06-13 18:47:20 +03:00
|
|
|
// In deep mode, the index should be complete. In shallow
|
|
|
|
// mode, we should have already recursively loaded necessary
|
|
|
|
// dependencies so the above Lookup succeeds.
|
2018-04-26 20:58:52 +03:00
|
|
|
errorf("%v.%v not in index", pkg, name)
|
|
|
|
}
|
|
|
|
|
|
|
|
r := &importReader{p: p, currPkg: pkg}
|
|
|
|
r.declReader.Reset(p.declData[off:])
|
|
|
|
|
|
|
|
r.obj(name)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *iimporter) stringAt(off uint64) string {
|
|
|
|
if s, ok := p.stringCache[off]; ok {
|
|
|
|
return s
|
|
|
|
}
|
|
|
|
|
|
|
|
slen, n := binary.Uvarint(p.stringData[off:])
|
|
|
|
if n <= 0 {
|
|
|
|
errorf("varint failed")
|
|
|
|
}
|
|
|
|
spos := off + uint64(n)
|
|
|
|
s := string(p.stringData[spos : spos+slen])
|
|
|
|
p.stringCache[off] = s
|
|
|
|
return s
|
|
|
|
}
|
|
|
|
|
2023-01-13 19:44:12 +03:00
|
|
|
func (p *iimporter) fileAt(index uint64) *token.File {
|
|
|
|
file := p.fileCache[index]
|
|
|
|
if file == nil {
|
|
|
|
off := p.fileOffset[index]
|
2023-01-13 23:12:19 +03:00
|
|
|
file = p.decodeFile(intReader{bytes.NewReader(p.fileData[off:]), p.ipath})
|
|
|
|
p.fileCache[index] = file
|
|
|
|
}
|
|
|
|
return file
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *iimporter) decodeFile(rd intReader) *token.File {
|
|
|
|
filename := p.stringAt(rd.uint64())
|
|
|
|
size := int(rd.uint64())
|
|
|
|
file := p.fake.fset.AddFile(filename, -1, size)
|
|
|
|
|
|
|
|
// SetLines requires a nondecreasing sequence.
|
|
|
|
// Because it is common for clients to derive the interval
|
|
|
|
// [start, start+len(name)] from a start position, and we
|
|
|
|
// want to ensure that the end offset is on the same line,
|
|
|
|
// we fill in the gaps of the sparse encoding with values
|
|
|
|
// that strictly increase by the largest possible amount.
|
|
|
|
// This allows us to avoid having to record the actual end
|
|
|
|
// offset of each needed line.
|
|
|
|
|
|
|
|
lines := make([]int, int(rd.uint64()))
|
|
|
|
var index, offset int
|
|
|
|
for i, n := 0, int(rd.uint64()); i < n; i++ {
|
|
|
|
index += int(rd.uint64())
|
|
|
|
offset += int(rd.uint64())
|
|
|
|
lines[index] = offset
|
|
|
|
|
|
|
|
// Ensure monotonicity between points.
|
|
|
|
for j := index - 1; j > 0 && lines[j] == 0; j-- {
|
|
|
|
lines[j] = lines[j+1] - 1
|
2023-01-09 21:44:29 +03:00
|
|
|
}
|
2023-01-13 23:12:19 +03:00
|
|
|
}
|
2023-01-09 21:44:29 +03:00
|
|
|
|
2023-01-13 23:12:19 +03:00
|
|
|
// Ensure monotonicity after last point.
|
|
|
|
for j := len(lines) - 1; j > 0 && lines[j] == 0; j-- {
|
|
|
|
size--
|
|
|
|
lines[j] = size
|
|
|
|
}
|
|
|
|
|
|
|
|
if !file.SetLines(lines) {
|
|
|
|
errorf("SetLines failed: %d", lines) // can't happen
|
2023-01-09 21:44:29 +03:00
|
|
|
}
|
|
|
|
return file
|
|
|
|
}
|
|
|
|
|
2018-04-26 20:58:52 +03:00
|
|
|
func (p *iimporter) pkgAt(off uint64) *types.Package {
|
|
|
|
if pkg, ok := p.pkgCache[off]; ok {
|
|
|
|
return pkg
|
|
|
|
}
|
|
|
|
path := p.stringAt(off)
|
|
|
|
errorf("missing package %q in %q", path, p.ipath)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *iimporter) typAt(off uint64, base *types.Named) types.Type {
|
2021-12-01 21:55:45 +03:00
|
|
|
if t, ok := p.typCache[off]; ok && canReuse(base, t) {
|
2018-04-26 20:58:52 +03:00
|
|
|
return t
|
|
|
|
}
|
|
|
|
|
|
|
|
if off < predeclReserved {
|
|
|
|
errorf("predeclared type missing from cache: %v", off)
|
|
|
|
}
|
|
|
|
|
|
|
|
r := &importReader{p: p}
|
|
|
|
r.declReader.Reset(p.declData[off-predeclReserved:])
|
|
|
|
t := r.doType(base)
|
|
|
|
|
2021-12-01 21:55:45 +03:00
|
|
|
if canReuse(base, t) {
|
2018-04-26 20:58:52 +03:00
|
|
|
p.typCache[off] = t
|
|
|
|
}
|
|
|
|
return t
|
|
|
|
}
|
|
|
|
|
2021-12-01 21:55:45 +03:00
|
|
|
// canReuse reports whether the type rhs on the RHS of the declaration for def
|
|
|
|
// may be re-used.
|
|
|
|
//
|
|
|
|
// Specifically, if def is non-nil and rhs is an interface type with methods, it
|
|
|
|
// may not be re-used because we have a convention of setting the receiver type
|
|
|
|
// for interface methods to def.
|
|
|
|
func canReuse(def *types.Named, rhs types.Type) bool {
|
|
|
|
if def == nil {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
iface, _ := rhs.(*types.Interface)
|
|
|
|
if iface == nil {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
// Don't use iface.Empty() here as iface may not be complete.
|
|
|
|
return iface.NumEmbeddeds() == 0 && iface.NumExplicitMethods() == 0
|
|
|
|
}
|
|
|
|
|
2018-04-26 20:58:52 +03:00
|
|
|
type importReader struct {
|
|
|
|
p *iimporter
|
|
|
|
declReader bytes.Reader
|
|
|
|
currPkg *types.Package
|
|
|
|
prevFile string
|
|
|
|
prevLine int64
|
2019-09-27 03:38:16 +03:00
|
|
|
prevColumn int64
|
2018-04-26 20:58:52 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func (r *importReader) obj(name string) {
|
|
|
|
tag := r.byte()
|
|
|
|
pos := r.pos()
|
|
|
|
|
|
|
|
switch tag {
|
|
|
|
case 'A':
|
|
|
|
typ := r.typ()
|
|
|
|
|
|
|
|
r.declare(types.NewTypeName(pos, r.currPkg, name, typ))
|
|
|
|
|
|
|
|
case 'C':
|
|
|
|
typ, val := r.value()
|
|
|
|
|
|
|
|
r.declare(types.NewConst(pos, r.currPkg, name, typ, val))
|
|
|
|
|
2021-09-14 20:07:03 +03:00
|
|
|
case 'F', 'G':
|
|
|
|
var tparams []*typeparams.TypeParam
|
|
|
|
if tag == 'G' {
|
|
|
|
tparams = r.tparamList()
|
|
|
|
}
|
2021-09-28 22:04:48 +03:00
|
|
|
sig := r.signature(nil, nil, tparams)
|
2018-04-26 20:58:52 +03:00
|
|
|
r.declare(types.NewFunc(pos, r.currPkg, name, sig))
|
|
|
|
|
2021-09-14 20:07:03 +03:00
|
|
|
case 'T', 'U':
|
2018-04-26 20:58:52 +03:00
|
|
|
// Types can be recursive. We need to setup a stub
|
|
|
|
// declaration before recursing.
|
|
|
|
obj := types.NewTypeName(pos, r.currPkg, name, nil)
|
|
|
|
named := types.NewNamed(obj, nil, nil)
|
2021-09-14 20:07:03 +03:00
|
|
|
// Declare obj before calling r.tparamList, so the new type name is recognized
|
|
|
|
// if used in the constraint of one of its own typeparams (see #48280).
|
2018-04-26 20:58:52 +03:00
|
|
|
r.declare(obj)
|
2021-09-14 20:07:03 +03:00
|
|
|
if tag == 'U' {
|
|
|
|
tparams := r.tparamList()
|
|
|
|
typeparams.SetForNamed(named, tparams)
|
|
|
|
}
|
2018-04-26 20:58:52 +03:00
|
|
|
|
|
|
|
underlying := r.p.typAt(r.uint64(), named).Underlying()
|
|
|
|
named.SetUnderlying(underlying)
|
|
|
|
|
|
|
|
if !isInterface(underlying) {
|
|
|
|
for n := r.uint64(); n > 0; n-- {
|
|
|
|
mpos := r.pos()
|
|
|
|
mname := r.ident()
|
|
|
|
recv := r.param()
|
|
|
|
|
2021-09-14 20:07:03 +03:00
|
|
|
// If the receiver has any targs, set those as the
|
|
|
|
// rparams of the method (since those are the
|
|
|
|
// typeparams being used in the method sig/body).
|
2021-10-18 19:23:03 +03:00
|
|
|
base := baseType(recv.Type())
|
|
|
|
assert(base != nil)
|
|
|
|
targs := typeparams.NamedTypeArgs(base)
|
2021-09-28 22:04:48 +03:00
|
|
|
var rparams []*typeparams.TypeParam
|
2021-09-20 17:42:55 +03:00
|
|
|
if targs.Len() > 0 {
|
2021-10-18 19:23:03 +03:00
|
|
|
rparams = make([]*typeparams.TypeParam, targs.Len())
|
2021-09-14 20:07:03 +03:00
|
|
|
for i := range rparams {
|
2021-09-20 17:42:55 +03:00
|
|
|
rparams[i] = targs.At(i).(*typeparams.TypeParam)
|
2021-09-14 20:07:03 +03:00
|
|
|
}
|
|
|
|
}
|
2021-09-28 22:04:48 +03:00
|
|
|
msig := r.signature(recv, rparams, nil)
|
2021-09-14 20:07:03 +03:00
|
|
|
|
2018-04-26 20:58:52 +03:00
|
|
|
named.AddMethod(types.NewFunc(mpos, r.currPkg, mname, msig))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-14 20:07:03 +03:00
|
|
|
case 'P':
|
|
|
|
// We need to "declare" a typeparam in order to have a name that
|
|
|
|
// can be referenced recursively (if needed) in the type param's
|
|
|
|
// bound.
|
2021-10-22 18:32:16 +03:00
|
|
|
if r.p.version < iexportVersionGenerics {
|
2021-09-14 20:07:03 +03:00
|
|
|
errorf("unexpected type param type")
|
|
|
|
}
|
2022-01-20 19:16:42 +03:00
|
|
|
name0 := tparamName(name)
|
2021-09-14 20:07:03 +03:00
|
|
|
tn := types.NewTypeName(pos, r.currPkg, name0, nil)
|
|
|
|
t := typeparams.NewTypeParam(tn, nil)
|
2021-10-01 05:45:40 +03:00
|
|
|
|
2021-09-14 20:07:03 +03:00
|
|
|
// To handle recursive references to the typeparam within its
|
|
|
|
// bound, save the partial type in tparamIndex before reading the bounds.
|
2022-04-14 22:47:16 +03:00
|
|
|
id := ident{r.currPkg, name}
|
2021-09-14 20:07:03 +03:00
|
|
|
r.p.tparamIndex[id] = t
|
2021-10-22 18:02:26 +03:00
|
|
|
var implicit bool
|
2021-10-22 18:32:16 +03:00
|
|
|
if r.p.version >= iexportVersionGo1_18 {
|
2021-10-22 18:02:26 +03:00
|
|
|
implicit = r.bool()
|
|
|
|
}
|
|
|
|
constraint := r.typ()
|
|
|
|
if implicit {
|
|
|
|
iface, _ := constraint.(*types.Interface)
|
|
|
|
if iface == nil {
|
|
|
|
errorf("non-interface constraint marked implicit")
|
|
|
|
}
|
|
|
|
typeparams.MarkImplicit(iface)
|
|
|
|
}
|
2022-02-22 17:47:31 +03:00
|
|
|
// The constraint type may not be complete, if we
|
|
|
|
// are in the middle of a type recursion involving type
|
|
|
|
// constraints. So, we defer SetConstraint until we have
|
|
|
|
// completely set up all types in ImportData.
|
|
|
|
r.p.later = append(r.p.later, setConstraintArgs{t: t, constraint: constraint})
|
2021-09-14 20:07:03 +03:00
|
|
|
|
2018-04-26 20:58:52 +03:00
|
|
|
case 'V':
|
|
|
|
typ := r.typ()
|
|
|
|
|
|
|
|
r.declare(types.NewVar(pos, r.currPkg, name, typ))
|
|
|
|
|
|
|
|
default:
|
|
|
|
errorf("unexpected tag: %v", tag)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *importReader) declare(obj types.Object) {
|
|
|
|
obj.Pkg().Scope().Insert(obj)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *importReader) value() (typ types.Type, val constant.Value) {
|
|
|
|
typ = r.typ()
|
2021-10-22 18:32:16 +03:00
|
|
|
if r.p.version >= iexportVersionGo1_18 {
|
|
|
|
// TODO: add support for using the kind.
|
|
|
|
_ = constant.Kind(r.int64())
|
|
|
|
}
|
2018-04-26 20:58:52 +03:00
|
|
|
|
|
|
|
switch b := typ.Underlying().(*types.Basic); b.Info() & types.IsConstType {
|
|
|
|
case types.IsBoolean:
|
|
|
|
val = constant.MakeBool(r.bool())
|
|
|
|
|
|
|
|
case types.IsString:
|
|
|
|
val = constant.MakeString(r.string())
|
|
|
|
|
|
|
|
case types.IsInteger:
|
2022-07-15 02:30:14 +03:00
|
|
|
var x big.Int
|
|
|
|
r.mpint(&x, b)
|
|
|
|
val = constant.Make(&x)
|
2018-04-26 20:58:52 +03:00
|
|
|
|
|
|
|
case types.IsFloat:
|
|
|
|
val = r.mpfloat(b)
|
|
|
|
|
|
|
|
case types.IsComplex:
|
|
|
|
re := r.mpfloat(b)
|
|
|
|
im := r.mpfloat(b)
|
|
|
|
val = constant.BinaryOp(re, token.ADD, constant.MakeImag(im))
|
|
|
|
|
|
|
|
default:
|
2019-01-07 22:50:49 +03:00
|
|
|
if b.Kind() == types.Invalid {
|
|
|
|
val = constant.MakeUnknown()
|
|
|
|
return
|
|
|
|
}
|
2018-04-26 20:58:52 +03:00
|
|
|
errorf("unexpected type %v", typ) // panics
|
|
|
|
panic("unreachable")
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func intSize(b *types.Basic) (signed bool, maxBytes uint) {
|
|
|
|
if (b.Info() & types.IsUntyped) != 0 {
|
|
|
|
return true, 64
|
|
|
|
}
|
|
|
|
|
|
|
|
switch b.Kind() {
|
|
|
|
case types.Float32, types.Complex64:
|
|
|
|
return true, 3
|
|
|
|
case types.Float64, types.Complex128:
|
|
|
|
return true, 7
|
|
|
|
}
|
|
|
|
|
|
|
|
signed = (b.Info() & types.IsUnsigned) == 0
|
|
|
|
switch b.Kind() {
|
|
|
|
case types.Int8, types.Uint8:
|
|
|
|
maxBytes = 1
|
|
|
|
case types.Int16, types.Uint16:
|
|
|
|
maxBytes = 2
|
|
|
|
case types.Int32, types.Uint32:
|
|
|
|
maxBytes = 4
|
|
|
|
default:
|
|
|
|
maxBytes = 8
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-07-15 02:30:14 +03:00
|
|
|
func (r *importReader) mpint(x *big.Int, typ *types.Basic) {
|
|
|
|
signed, maxBytes := intSize(typ)
|
2018-04-26 20:58:52 +03:00
|
|
|
|
|
|
|
maxSmall := 256 - maxBytes
|
|
|
|
if signed {
|
|
|
|
maxSmall = 256 - 2*maxBytes
|
|
|
|
}
|
|
|
|
if maxBytes == 1 {
|
|
|
|
maxSmall = 256
|
|
|
|
}
|
|
|
|
|
|
|
|
n, _ := r.declReader.ReadByte()
|
|
|
|
if uint(n) < maxSmall {
|
|
|
|
v := int64(n)
|
|
|
|
if signed {
|
|
|
|
v >>= 1
|
|
|
|
if n&1 != 0 {
|
|
|
|
v = ^v
|
|
|
|
}
|
|
|
|
}
|
2022-07-15 02:30:14 +03:00
|
|
|
x.SetInt64(v)
|
|
|
|
return
|
2018-04-26 20:58:52 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
v := -n
|
|
|
|
if signed {
|
|
|
|
v = -(n &^ 1) >> 1
|
|
|
|
}
|
|
|
|
if v < 1 || uint(v) > maxBytes {
|
|
|
|
errorf("weird decoding: %v, %v => %v", n, signed, v)
|
|
|
|
}
|
2022-07-15 02:30:14 +03:00
|
|
|
b := make([]byte, v)
|
|
|
|
io.ReadFull(&r.declReader, b)
|
|
|
|
x.SetBytes(b)
|
2018-04-26 20:58:52 +03:00
|
|
|
if signed && n&1 != 0 {
|
2022-07-15 02:30:14 +03:00
|
|
|
x.Neg(x)
|
2018-04-26 20:58:52 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-15 02:30:14 +03:00
|
|
|
func (r *importReader) mpfloat(typ *types.Basic) constant.Value {
|
|
|
|
var mant big.Int
|
|
|
|
r.mpint(&mant, typ)
|
|
|
|
var f big.Float
|
|
|
|
f.SetInt(&mant)
|
|
|
|
if f.Sign() != 0 {
|
|
|
|
f.SetMantExp(&f, int(r.int64()))
|
|
|
|
}
|
|
|
|
return constant.Make(&f)
|
2018-04-26 20:58:52 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func (r *importReader) ident() string {
|
|
|
|
return r.string()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *importReader) qualifiedIdent() (*types.Package, string) {
|
|
|
|
name := r.string()
|
|
|
|
pkg := r.pkg()
|
|
|
|
return pkg, name
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *importReader) pos() token.Pos {
|
internal/gcimporter: supporting encoding objects from different packages
Through forwarded underlyings or type aliases, shallow export data may
reference fields or methods declared in other packages. However, the
importer was not able to canonicalize these fields or methods, because
unlike with deep export data, it had no index of types or objects in
other packages.
For example, consider:
package a
func A() *[]func() struct { X int }
package b
import "a"
var B = a.A()
The export data for package 'a' must record that the type graphs for A,
and B must share the subgraph for the interior node X, deeply within
both. With deep export data this is easy because it encodes several
packages in a single object graph, but shallow export data needs some
kind of identifier for object X deep within package b so it can be
located when b is imported independently. Objectpath is such a name.
Use the objectpath algorithm to encode these objects during shallow
export, and look them up during shallow import.
Running TestShallowStd in the internal/gcimporter package turned up only
one set of fields affected by this change: the use of the
fuzz.CorpusEntry alias from the testing/internal/testdeps package.
Therefore, the performance cost of objectpath is likely insignificant.
Also simplify exporting in iexport.go. Unlike during importing, there is
no need to track the current package.
Fixes golang/go#60676
Fixes golang/go#60819
Change-Id: I15ae14c5733d1cafd14aabd7150da4f7b851e733
Reviewed-on: https://go-review.googlesource.com/c/tools/+/503438
gopls-CI: kokoro <noreply+kokoro@google.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
2023-06-16 00:10:58 +03:00
|
|
|
if r.p.shallow {
|
|
|
|
// precise offsets are encoded only in shallow mode
|
2023-01-09 21:44:29 +03:00
|
|
|
return r.posv2()
|
|
|
|
}
|
2021-10-22 18:32:16 +03:00
|
|
|
if r.p.version >= iexportVersionPosCol {
|
2019-09-27 03:38:16 +03:00
|
|
|
r.posv1()
|
|
|
|
} else {
|
|
|
|
r.posv0()
|
|
|
|
}
|
|
|
|
|
|
|
|
if r.prevFile == "" && r.prevLine == 0 && r.prevColumn == 0 {
|
|
|
|
return token.NoPos
|
|
|
|
}
|
|
|
|
return r.p.fake.pos(r.prevFile, int(r.prevLine), int(r.prevColumn))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *importReader) posv0() {
|
2018-04-26 20:58:52 +03:00
|
|
|
delta := r.int64()
|
|
|
|
if delta != deltaNewFile {
|
|
|
|
r.prevLine += delta
|
|
|
|
} else if l := r.int64(); l == -1 {
|
|
|
|
r.prevLine += deltaNewFile
|
|
|
|
} else {
|
|
|
|
r.prevFile = r.string()
|
|
|
|
r.prevLine = l
|
|
|
|
}
|
2019-09-27 03:38:16 +03:00
|
|
|
}
|
2018-04-26 20:58:52 +03:00
|
|
|
|
2019-09-27 03:38:16 +03:00
|
|
|
func (r *importReader) posv1() {
|
|
|
|
delta := r.int64()
|
|
|
|
r.prevColumn += delta >> 1
|
|
|
|
if delta&1 != 0 {
|
|
|
|
delta = r.int64()
|
|
|
|
r.prevLine += delta >> 1
|
|
|
|
if delta&1 != 0 {
|
|
|
|
r.prevFile = r.string()
|
|
|
|
}
|
2018-04-26 20:58:52 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-09 21:44:29 +03:00
|
|
|
func (r *importReader) posv2() token.Pos {
|
|
|
|
file := r.uint64()
|
|
|
|
if file == 0 {
|
|
|
|
return token.NoPos
|
|
|
|
}
|
|
|
|
tf := r.p.fileAt(file - 1)
|
|
|
|
return tf.Pos(int(r.uint64()))
|
|
|
|
}
|
|
|
|
|
2018-04-26 20:58:52 +03:00
|
|
|
func (r *importReader) typ() types.Type {
|
|
|
|
return r.p.typAt(r.uint64(), nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
func isInterface(t types.Type) bool {
|
|
|
|
_, ok := t.(*types.Interface)
|
|
|
|
return ok
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *importReader) pkg() *types.Package { return r.p.pkgAt(r.uint64()) }
|
|
|
|
func (r *importReader) string() string { return r.p.stringAt(r.uint64()) }
|
|
|
|
|
2021-10-18 19:23:03 +03:00
|
|
|
func (r *importReader) doType(base *types.Named) (res types.Type) {
|
|
|
|
k := r.kind()
|
|
|
|
if debug {
|
|
|
|
r.p.trace("importing type %d (base: %s)", k, base)
|
|
|
|
r.p.indent++
|
|
|
|
defer func() {
|
|
|
|
r.p.indent--
|
|
|
|
r.p.trace("=> %s", res)
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
switch k {
|
2018-04-26 20:58:52 +03:00
|
|
|
default:
|
|
|
|
errorf("unexpected kind tag in %q: %v", r.p.ipath, k)
|
|
|
|
return nil
|
|
|
|
|
|
|
|
case definedType:
|
|
|
|
pkg, name := r.qualifiedIdent()
|
|
|
|
r.p.doDecl(pkg, name)
|
|
|
|
return pkg.Scope().Lookup(name).(*types.TypeName).Type()
|
|
|
|
case pointerType:
|
|
|
|
return types.NewPointer(r.typ())
|
|
|
|
case sliceType:
|
|
|
|
return types.NewSlice(r.typ())
|
|
|
|
case arrayType:
|
|
|
|
n := r.uint64()
|
|
|
|
return types.NewArray(r.typ(), int64(n))
|
|
|
|
case chanType:
|
|
|
|
dir := chanDir(int(r.uint64()))
|
|
|
|
return types.NewChan(dir, r.typ())
|
|
|
|
case mapType:
|
|
|
|
return types.NewMap(r.typ(), r.typ())
|
|
|
|
case signatureType:
|
|
|
|
r.currPkg = r.pkg()
|
2021-09-28 22:04:48 +03:00
|
|
|
return r.signature(nil, nil, nil)
|
2018-04-26 20:58:52 +03:00
|
|
|
|
|
|
|
case structType:
|
|
|
|
r.currPkg = r.pkg()
|
|
|
|
|
|
|
|
fields := make([]*types.Var, r.uint64())
|
|
|
|
tags := make([]string, len(fields))
|
|
|
|
for i := range fields {
|
internal/gcimporter: supporting encoding objects from different packages
Through forwarded underlyings or type aliases, shallow export data may
reference fields or methods declared in other packages. However, the
importer was not able to canonicalize these fields or methods, because
unlike with deep export data, it had no index of types or objects in
other packages.
For example, consider:
package a
func A() *[]func() struct { X int }
package b
import "a"
var B = a.A()
The export data for package 'a' must record that the type graphs for A,
and B must share the subgraph for the interior node X, deeply within
both. With deep export data this is easy because it encodes several
packages in a single object graph, but shallow export data needs some
kind of identifier for object X deep within package b so it can be
located when b is imported independently. Objectpath is such a name.
Use the objectpath algorithm to encode these objects during shallow
export, and look them up during shallow import.
Running TestShallowStd in the internal/gcimporter package turned up only
one set of fields affected by this change: the use of the
fuzz.CorpusEntry alias from the testing/internal/testdeps package.
Therefore, the performance cost of objectpath is likely insignificant.
Also simplify exporting in iexport.go. Unlike during importing, there is
no need to track the current package.
Fixes golang/go#60676
Fixes golang/go#60819
Change-Id: I15ae14c5733d1cafd14aabd7150da4f7b851e733
Reviewed-on: https://go-review.googlesource.com/c/tools/+/503438
gopls-CI: kokoro <noreply+kokoro@google.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
2023-06-16 00:10:58 +03:00
|
|
|
var field *types.Var
|
|
|
|
if r.p.shallow {
|
|
|
|
field, _ = r.objectPathObject().(*types.Var)
|
|
|
|
}
|
|
|
|
|
2018-04-26 20:58:52 +03:00
|
|
|
fpos := r.pos()
|
|
|
|
fname := r.ident()
|
|
|
|
ftyp := r.typ()
|
|
|
|
emb := r.bool()
|
|
|
|
tag := r.string()
|
|
|
|
|
internal/gcimporter: supporting encoding objects from different packages
Through forwarded underlyings or type aliases, shallow export data may
reference fields or methods declared in other packages. However, the
importer was not able to canonicalize these fields or methods, because
unlike with deep export data, it had no index of types or objects in
other packages.
For example, consider:
package a
func A() *[]func() struct { X int }
package b
import "a"
var B = a.A()
The export data for package 'a' must record that the type graphs for A,
and B must share the subgraph for the interior node X, deeply within
both. With deep export data this is easy because it encodes several
packages in a single object graph, but shallow export data needs some
kind of identifier for object X deep within package b so it can be
located when b is imported independently. Objectpath is such a name.
Use the objectpath algorithm to encode these objects during shallow
export, and look them up during shallow import.
Running TestShallowStd in the internal/gcimporter package turned up only
one set of fields affected by this change: the use of the
fuzz.CorpusEntry alias from the testing/internal/testdeps package.
Therefore, the performance cost of objectpath is likely insignificant.
Also simplify exporting in iexport.go. Unlike during importing, there is
no need to track the current package.
Fixes golang/go#60676
Fixes golang/go#60819
Change-Id: I15ae14c5733d1cafd14aabd7150da4f7b851e733
Reviewed-on: https://go-review.googlesource.com/c/tools/+/503438
gopls-CI: kokoro <noreply+kokoro@google.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
2023-06-16 00:10:58 +03:00
|
|
|
// Either this is not a shallow import, the field is local, or the
|
|
|
|
// encoded objectPath failed to produce an object (a bug).
|
|
|
|
//
|
|
|
|
// Even in this last, buggy case, fall back on creating a new field. As
|
|
|
|
// discussed in iexport.go, this is not correct, but mostly works and is
|
|
|
|
// preferable to failing (for now at least).
|
|
|
|
if field == nil {
|
|
|
|
field = types.NewField(fpos, r.currPkg, fname, ftyp, emb)
|
|
|
|
}
|
|
|
|
|
|
|
|
fields[i] = field
|
2018-04-26 20:58:52 +03:00
|
|
|
tags[i] = tag
|
|
|
|
}
|
|
|
|
return types.NewStruct(fields, tags)
|
|
|
|
|
|
|
|
case interfaceType:
|
|
|
|
r.currPkg = r.pkg()
|
|
|
|
|
2018-06-13 08:33:41 +03:00
|
|
|
embeddeds := make([]types.Type, r.uint64())
|
2018-04-26 20:58:52 +03:00
|
|
|
for i := range embeddeds {
|
|
|
|
_ = r.pos()
|
2018-06-13 08:33:41 +03:00
|
|
|
embeddeds[i] = r.typ()
|
2018-04-26 20:58:52 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
methods := make([]*types.Func, r.uint64())
|
|
|
|
for i := range methods {
|
internal/gcimporter: supporting encoding objects from different packages
Through forwarded underlyings or type aliases, shallow export data may
reference fields or methods declared in other packages. However, the
importer was not able to canonicalize these fields or methods, because
unlike with deep export data, it had no index of types or objects in
other packages.
For example, consider:
package a
func A() *[]func() struct { X int }
package b
import "a"
var B = a.A()
The export data for package 'a' must record that the type graphs for A,
and B must share the subgraph for the interior node X, deeply within
both. With deep export data this is easy because it encodes several
packages in a single object graph, but shallow export data needs some
kind of identifier for object X deep within package b so it can be
located when b is imported independently. Objectpath is such a name.
Use the objectpath algorithm to encode these objects during shallow
export, and look them up during shallow import.
Running TestShallowStd in the internal/gcimporter package turned up only
one set of fields affected by this change: the use of the
fuzz.CorpusEntry alias from the testing/internal/testdeps package.
Therefore, the performance cost of objectpath is likely insignificant.
Also simplify exporting in iexport.go. Unlike during importing, there is
no need to track the current package.
Fixes golang/go#60676
Fixes golang/go#60819
Change-Id: I15ae14c5733d1cafd14aabd7150da4f7b851e733
Reviewed-on: https://go-review.googlesource.com/c/tools/+/503438
gopls-CI: kokoro <noreply+kokoro@google.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
2023-06-16 00:10:58 +03:00
|
|
|
var method *types.Func
|
|
|
|
if r.p.shallow {
|
|
|
|
method, _ = r.objectPathObject().(*types.Func)
|
|
|
|
}
|
|
|
|
|
2018-04-26 20:58:52 +03:00
|
|
|
mpos := r.pos()
|
|
|
|
mname := r.ident()
|
|
|
|
|
|
|
|
// TODO(mdempsky): Matches bimport.go, but I
|
|
|
|
// don't agree with this.
|
|
|
|
var recv *types.Var
|
|
|
|
if base != nil {
|
|
|
|
recv = types.NewVar(token.NoPos, r.currPkg, "", base)
|
|
|
|
}
|
2021-09-28 22:04:48 +03:00
|
|
|
msig := r.signature(recv, nil, nil)
|
internal/gcimporter: supporting encoding objects from different packages
Through forwarded underlyings or type aliases, shallow export data may
reference fields or methods declared in other packages. However, the
importer was not able to canonicalize these fields or methods, because
unlike with deep export data, it had no index of types or objects in
other packages.
For example, consider:
package a
func A() *[]func() struct { X int }
package b
import "a"
var B = a.A()
The export data for package 'a' must record that the type graphs for A,
and B must share the subgraph for the interior node X, deeply within
both. With deep export data this is easy because it encodes several
packages in a single object graph, but shallow export data needs some
kind of identifier for object X deep within package b so it can be
located when b is imported independently. Objectpath is such a name.
Use the objectpath algorithm to encode these objects during shallow
export, and look them up during shallow import.
Running TestShallowStd in the internal/gcimporter package turned up only
one set of fields affected by this change: the use of the
fuzz.CorpusEntry alias from the testing/internal/testdeps package.
Therefore, the performance cost of objectpath is likely insignificant.
Also simplify exporting in iexport.go. Unlike during importing, there is
no need to track the current package.
Fixes golang/go#60676
Fixes golang/go#60819
Change-Id: I15ae14c5733d1cafd14aabd7150da4f7b851e733
Reviewed-on: https://go-review.googlesource.com/c/tools/+/503438
gopls-CI: kokoro <noreply+kokoro@google.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
2023-06-16 00:10:58 +03:00
|
|
|
|
|
|
|
if method == nil {
|
|
|
|
method = types.NewFunc(mpos, r.currPkg, mname, msig)
|
|
|
|
}
|
|
|
|
methods[i] = method
|
2018-04-26 20:58:52 +03:00
|
|
|
}
|
|
|
|
|
2018-06-13 20:56:57 +03:00
|
|
|
typ := newInterface(methods, embeddeds)
|
2018-04-26 20:58:52 +03:00
|
|
|
r.p.interfaceList = append(r.p.interfaceList, typ)
|
|
|
|
return typ
|
2021-09-14 20:07:03 +03:00
|
|
|
|
|
|
|
case typeParamType:
|
2021-10-22 18:32:16 +03:00
|
|
|
if r.p.version < iexportVersionGenerics {
|
2021-09-14 20:07:03 +03:00
|
|
|
errorf("unexpected type param type")
|
|
|
|
}
|
|
|
|
pkg, name := r.qualifiedIdent()
|
2022-04-14 22:47:16 +03:00
|
|
|
id := ident{pkg, name}
|
2021-09-14 20:07:03 +03:00
|
|
|
if t, ok := r.p.tparamIndex[id]; ok {
|
|
|
|
// We're already in the process of importing this typeparam.
|
|
|
|
return t
|
|
|
|
}
|
|
|
|
// Otherwise, import the definition of the typeparam now.
|
|
|
|
r.p.doDecl(pkg, name)
|
|
|
|
return r.p.tparamIndex[id]
|
|
|
|
|
2021-09-15 03:46:30 +03:00
|
|
|
case instanceType:
|
2021-10-22 18:32:16 +03:00
|
|
|
if r.p.version < iexportVersionGenerics {
|
2021-09-14 20:07:03 +03:00
|
|
|
errorf("unexpected instantiation type")
|
|
|
|
}
|
|
|
|
// pos does not matter for instances: they are positioned on the original
|
|
|
|
// type.
|
|
|
|
_ = r.pos()
|
|
|
|
len := r.uint64()
|
|
|
|
targs := make([]types.Type, len)
|
|
|
|
for i := range targs {
|
|
|
|
targs[i] = r.typ()
|
|
|
|
}
|
|
|
|
baseType := r.typ()
|
|
|
|
// The imported instantiated type doesn't include any methods, so
|
|
|
|
// we must always use the methods of the base (orig) type.
|
|
|
|
// TODO provide a non-nil *Environment
|
|
|
|
t, _ := typeparams.Instantiate(nil, baseType, targs, false)
|
2023-07-25 17:44:06 +03:00
|
|
|
|
|
|
|
// Workaround for golang/go#61561. See the doc for instanceList for details.
|
|
|
|
r.p.instanceList = append(r.p.instanceList, t)
|
2021-09-14 20:07:03 +03:00
|
|
|
return t
|
|
|
|
|
|
|
|
case unionType:
|
2021-10-22 18:32:16 +03:00
|
|
|
if r.p.version < iexportVersionGenerics {
|
2021-09-14 20:07:03 +03:00
|
|
|
errorf("unexpected instantiation type")
|
|
|
|
}
|
|
|
|
terms := make([]*typeparams.Term, r.uint64())
|
|
|
|
for i := range terms {
|
|
|
|
terms[i] = typeparams.NewTerm(r.bool(), r.typ())
|
|
|
|
}
|
|
|
|
return typeparams.NewUnion(terms)
|
2018-04-26 20:58:52 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *importReader) kind() itag {
|
|
|
|
return itag(r.uint64())
|
|
|
|
}
|
|
|
|
|
internal/gcimporter: supporting encoding objects from different packages
Through forwarded underlyings or type aliases, shallow export data may
reference fields or methods declared in other packages. However, the
importer was not able to canonicalize these fields or methods, because
unlike with deep export data, it had no index of types or objects in
other packages.
For example, consider:
package a
func A() *[]func() struct { X int }
package b
import "a"
var B = a.A()
The export data for package 'a' must record that the type graphs for A,
and B must share the subgraph for the interior node X, deeply within
both. With deep export data this is easy because it encodes several
packages in a single object graph, but shallow export data needs some
kind of identifier for object X deep within package b so it can be
located when b is imported independently. Objectpath is such a name.
Use the objectpath algorithm to encode these objects during shallow
export, and look them up during shallow import.
Running TestShallowStd in the internal/gcimporter package turned up only
one set of fields affected by this change: the use of the
fuzz.CorpusEntry alias from the testing/internal/testdeps package.
Therefore, the performance cost of objectpath is likely insignificant.
Also simplify exporting in iexport.go. Unlike during importing, there is
no need to track the current package.
Fixes golang/go#60676
Fixes golang/go#60819
Change-Id: I15ae14c5733d1cafd14aabd7150da4f7b851e733
Reviewed-on: https://go-review.googlesource.com/c/tools/+/503438
gopls-CI: kokoro <noreply+kokoro@google.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
2023-06-16 00:10:58 +03:00
|
|
|
// objectPathObject is the inverse of exportWriter.objectPath.
|
|
|
|
//
|
|
|
|
// In shallow mode, certain fields and methods may need to be looked up in an
|
|
|
|
// imported package. See the doc for exportWriter.objectPath for a full
|
|
|
|
// explanation.
|
|
|
|
func (r *importReader) objectPathObject() types.Object {
|
|
|
|
objPath := objectpath.Path(r.string())
|
|
|
|
if objPath == "" {
|
|
|
|
return nil
|
|
|
|
}
|
2023-07-18 01:01:44 +03:00
|
|
|
pkg := r.pkg()
|
internal/gcimporter: supporting encoding objects from different packages
Through forwarded underlyings or type aliases, shallow export data may
reference fields or methods declared in other packages. However, the
importer was not able to canonicalize these fields or methods, because
unlike with deep export data, it had no index of types or objects in
other packages.
For example, consider:
package a
func A() *[]func() struct { X int }
package b
import "a"
var B = a.A()
The export data for package 'a' must record that the type graphs for A,
and B must share the subgraph for the interior node X, deeply within
both. With deep export data this is easy because it encodes several
packages in a single object graph, but shallow export data needs some
kind of identifier for object X deep within package b so it can be
located when b is imported independently. Objectpath is such a name.
Use the objectpath algorithm to encode these objects during shallow
export, and look them up during shallow import.
Running TestShallowStd in the internal/gcimporter package turned up only
one set of fields affected by this change: the use of the
fuzz.CorpusEntry alias from the testing/internal/testdeps package.
Therefore, the performance cost of objectpath is likely insignificant.
Also simplify exporting in iexport.go. Unlike during importing, there is
no need to track the current package.
Fixes golang/go#60676
Fixes golang/go#60819
Change-Id: I15ae14c5733d1cafd14aabd7150da4f7b851e733
Reviewed-on: https://go-review.googlesource.com/c/tools/+/503438
gopls-CI: kokoro <noreply+kokoro@google.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
2023-06-16 00:10:58 +03:00
|
|
|
obj, err := objectpath.Object(pkg, objPath)
|
|
|
|
if err != nil {
|
|
|
|
if r.p.reportf != nil {
|
|
|
|
r.p.reportf("failed to find object for objectPath %q: %v", objPath, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return obj
|
|
|
|
}
|
|
|
|
|
2021-09-28 22:04:48 +03:00
|
|
|
func (r *importReader) signature(recv *types.Var, rparams []*typeparams.TypeParam, tparams []*typeparams.TypeParam) *types.Signature {
|
2018-04-26 20:58:52 +03:00
|
|
|
params := r.paramList()
|
|
|
|
results := r.paramList()
|
|
|
|
variadic := params.Len() > 0 && r.bool()
|
2021-09-28 22:04:48 +03:00
|
|
|
return typeparams.NewSignatureType(recv, rparams, tparams, params, results, variadic)
|
2018-04-26 20:58:52 +03:00
|
|
|
}
|
|
|
|
|
2021-09-14 20:07:03 +03:00
|
|
|
func (r *importReader) tparamList() []*typeparams.TypeParam {
|
|
|
|
n := r.uint64()
|
|
|
|
if n == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
xs := make([]*typeparams.TypeParam, n)
|
|
|
|
for i := range xs {
|
|
|
|
// Note: the standard library importer is tolerant of nil types here,
|
|
|
|
// though would panic in SetTypeParams.
|
|
|
|
xs[i] = r.typ().(*typeparams.TypeParam)
|
|
|
|
}
|
|
|
|
return xs
|
|
|
|
}
|
|
|
|
|
2018-04-26 20:58:52 +03:00
|
|
|
func (r *importReader) paramList() *types.Tuple {
|
|
|
|
xs := make([]*types.Var, r.uint64())
|
|
|
|
for i := range xs {
|
|
|
|
xs[i] = r.param()
|
|
|
|
}
|
|
|
|
return types.NewTuple(xs...)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *importReader) param() *types.Var {
|
|
|
|
pos := r.pos()
|
|
|
|
name := r.ident()
|
|
|
|
typ := r.typ()
|
|
|
|
return types.NewParam(pos, r.currPkg, name, typ)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *importReader) bool() bool {
|
|
|
|
return r.uint64() != 0
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *importReader) int64() int64 {
|
|
|
|
n, err := binary.ReadVarint(&r.declReader)
|
|
|
|
if err != nil {
|
|
|
|
errorf("readVarint: %v", err)
|
|
|
|
}
|
|
|
|
return n
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *importReader) uint64() uint64 {
|
|
|
|
n, err := binary.ReadUvarint(&r.declReader)
|
|
|
|
if err != nil {
|
|
|
|
errorf("readUvarint: %v", err)
|
|
|
|
}
|
|
|
|
return n
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *importReader) byte() byte {
|
|
|
|
x, err := r.declReader.ReadByte()
|
|
|
|
if err != nil {
|
|
|
|
errorf("declReader.ReadByte: %v", err)
|
|
|
|
}
|
|
|
|
return x
|
|
|
|
}
|
2021-09-14 20:07:03 +03:00
|
|
|
|
|
|
|
func baseType(typ types.Type) *types.Named {
|
|
|
|
// pointer receivers are never types.Named types
|
|
|
|
if p, _ := typ.(*types.Pointer); p != nil {
|
|
|
|
typ = p.Elem()
|
|
|
|
}
|
|
|
|
// receiver base types are always (possibly generic) types.Named types
|
|
|
|
n, _ := typ.(*types.Named)
|
|
|
|
return n
|
|
|
|
}
|