2018-04-01 01:55:55 -07: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 iexport.go for the export data format.
|
|
|
|
|
|
|
|
|
|
package gc
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"cmd/compile/internal/types"
|
|
|
|
|
"cmd/internal/bio"
|
[dev.link] cmd/internal/goobj2: add index fingerprint to object file
The new object files use indices for symbol references, instead
of names. Fundamental to the design, it requires that the
importing and imported packages have consistent view of symbol
indices. The Go command should already ensure this, when using
"go build". But in case it goes wrong, it could lead to obscure
errors like run-time crashes. It would be better to check the
index consistency at build time.
To do that, we add a fingerprint to each object file, which is
a hash of symbol indices. In the object file it records the
fingerprints of all imported packages, as well as its own
fingerprint. At link time, the linker checks that a package's
fingerprint matches the fingerprint recorded in the importing
packages, and issue an error if they don't match.
This CL does the first part: introducing the fingerprint in the
object file, and propagating fingerprints through
importing/exporting by the compiler. It is not yet used by the
linker. Next CL will do.
Change-Id: I0aa372da652e4afb11f2867cb71689a3e3f9966e
Reviewed-on: https://go-review.googlesource.com/c/go/+/229617
Reviewed-by: Austin Clements <austin@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
Reviewed-by: Jeremy Faller <jeremy@golang.org>
2020-04-22 19:21:30 -04:00
|
|
|
"cmd/internal/goobj2"
|
[dev.link] cmd/compile, cmd/asm: assign index to symbols
We are planning to use indices for symbol references, instead of
symbol names. Here we assign indices to symbols defined in the
package being compiled, and propagate the indices to the
dependent packages in the export data.
A symbol is referenced by a tuple, (package index, symbol index).
Normally, for a given symbol, this index is unique, and the
symbol index is globally consistent (but with exceptions, see
below). The package index is local to a compilation. For example,
when compiling the fmt package, fmt.Println gets assigned index
25, then all packages that reference fmt.Println will refer it
as (X, 25) with some X. X is the index for the fmt package, which
may differ in different compilations.
There are some symbols that do not have clear package affiliation,
such as dupOK symbols and linknamed symbols. We cannot give them
globally consistent indices. We categorize them as non-package
symbols, assign them with package index 1 and a symbol index that
is only meaningful locally.
Currently nothing will consume the indices.
All this is behind a flag, -newobj. The flag needs to be set for
all builds (-gcflags=all=-newobj -asmflags=all=-newobj), or none.
Change-Id: I18e489c531e9a9fbc668519af92c6116b7308cab
Reviewed-on: https://go-review.googlesource.com/c/go/+/196029
Reviewed-by: Than McIntosh <thanm@google.com>
2019-09-11 16:17:01 -04:00
|
|
|
"cmd/internal/obj"
|
2018-04-01 01:55:55 -07:00
|
|
|
"cmd/internal/src"
|
|
|
|
|
"encoding/binary"
|
|
|
|
|
"fmt"
|
2020-05-05 13:12:01 -04:00
|
|
|
"io"
|
2018-04-01 01:55:55 -07:00
|
|
|
"math/big"
|
|
|
|
|
"os"
|
|
|
|
|
"strings"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// An iimporterAndOffset identifies an importer and an offset within
|
|
|
|
|
// its data section.
|
|
|
|
|
type iimporterAndOffset struct {
|
|
|
|
|
p *iimporter
|
|
|
|
|
off uint64
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
// declImporter maps from imported identifiers to an importer
|
|
|
|
|
// and offset where that identifier's declaration can be read.
|
|
|
|
|
declImporter = map[*types.Sym]iimporterAndOffset{}
|
|
|
|
|
|
|
|
|
|
// inlineImporter is like declImporter, but for inline bodies
|
|
|
|
|
// for function and method symbols.
|
|
|
|
|
inlineImporter = map[*types.Sym]iimporterAndOffset{}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func expandDecl(n *Node) {
|
|
|
|
|
if n.Op != ONONAME {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
r := importReaderFor(n, declImporter)
|
|
|
|
|
if r == nil {
|
|
|
|
|
// Can happen if user tries to reference an undeclared name.
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
r.doDecl(n)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func expandInline(fn *Node) {
|
|
|
|
|
if fn.Func.Inl.Body != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
r := importReaderFor(fn, inlineImporter)
|
|
|
|
|
if r == nil {
|
|
|
|
|
Fatalf("missing import reader for %v", fn)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
r.doInline(fn)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func importReaderFor(n *Node, importers map[*types.Sym]iimporterAndOffset) *importReader {
|
|
|
|
|
x, ok := importers[n.Sym]
|
|
|
|
|
if !ok {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return x.p.newReader(x.off, n.Sym.Pkg)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type intReader struct {
|
|
|
|
|
*bio.Reader
|
|
|
|
|
pkg *types.Pkg
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *intReader) int64() int64 {
|
|
|
|
|
i, err := binary.ReadVarint(r.Reader)
|
|
|
|
|
if err != nil {
|
|
|
|
|
yyerror("import %q: read error: %v", r.pkg.Path, err)
|
|
|
|
|
errorexit()
|
|
|
|
|
}
|
|
|
|
|
return i
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *intReader) uint64() uint64 {
|
|
|
|
|
i, err := binary.ReadUvarint(r.Reader)
|
|
|
|
|
if err != nil {
|
|
|
|
|
yyerror("import %q: read error: %v", r.pkg.Path, err)
|
|
|
|
|
errorexit()
|
|
|
|
|
}
|
|
|
|
|
return i
|
|
|
|
|
}
|
|
|
|
|
|
[dev.link] cmd/internal/goobj2: add index fingerprint to object file
The new object files use indices for symbol references, instead
of names. Fundamental to the design, it requires that the
importing and imported packages have consistent view of symbol
indices. The Go command should already ensure this, when using
"go build". But in case it goes wrong, it could lead to obscure
errors like run-time crashes. It would be better to check the
index consistency at build time.
To do that, we add a fingerprint to each object file, which is
a hash of symbol indices. In the object file it records the
fingerprints of all imported packages, as well as its own
fingerprint. At link time, the linker checks that a package's
fingerprint matches the fingerprint recorded in the importing
packages, and issue an error if they don't match.
This CL does the first part: introducing the fingerprint in the
object file, and propagating fingerprints through
importing/exporting by the compiler. It is not yet used by the
linker. Next CL will do.
Change-Id: I0aa372da652e4afb11f2867cb71689a3e3f9966e
Reviewed-on: https://go-review.googlesource.com/c/go/+/229617
Reviewed-by: Austin Clements <austin@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
Reviewed-by: Jeremy Faller <jeremy@golang.org>
2020-04-22 19:21:30 -04:00
|
|
|
func iimport(pkg *types.Pkg, in *bio.Reader) (fingerprint goobj2.FingerprintType) {
|
2018-04-01 01:55:55 -07:00
|
|
|
ir := &intReader{in, pkg}
|
|
|
|
|
|
|
|
|
|
version := ir.uint64()
|
|
|
|
|
if version != iexportVersion {
|
|
|
|
|
yyerror("import %q: unknown export format version %d", pkg.Path, version)
|
|
|
|
|
errorexit()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sLen := ir.uint64()
|
|
|
|
|
dLen := ir.uint64()
|
|
|
|
|
|
|
|
|
|
// Map string (and data) section into memory as a single large
|
|
|
|
|
// string. This reduces heap fragmentation and allows
|
|
|
|
|
// returning individual substrings very efficiently.
|
|
|
|
|
data, err := mapFile(in.File(), in.Offset(), int64(sLen+dLen))
|
|
|
|
|
if err != nil {
|
|
|
|
|
yyerror("import %q: mapping input: %v", pkg.Path, err)
|
|
|
|
|
errorexit()
|
|
|
|
|
}
|
|
|
|
|
stringData := data[:sLen]
|
|
|
|
|
declData := data[sLen:]
|
|
|
|
|
|
2019-05-08 18:46:04 -04:00
|
|
|
in.MustSeek(int64(sLen+dLen), os.SEEK_CUR)
|
2018-04-01 01:55:55 -07:00
|
|
|
|
|
|
|
|
p := &iimporter{
|
|
|
|
|
ipkg: pkg,
|
|
|
|
|
|
|
|
|
|
pkgCache: map[uint64]*types.Pkg{},
|
|
|
|
|
posBaseCache: map[uint64]*src.PosBase{},
|
|
|
|
|
typCache: map[uint64]*types.Type{},
|
|
|
|
|
|
|
|
|
|
stringData: stringData,
|
|
|
|
|
declData: declData,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for i, pt := range predeclared() {
|
|
|
|
|
p.typCache[uint64(i)] = pt
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Declaration index.
|
|
|
|
|
for nPkgs := ir.uint64(); nPkgs > 0; nPkgs-- {
|
|
|
|
|
pkg := p.pkgAt(ir.uint64())
|
|
|
|
|
pkgName := p.stringAt(ir.uint64())
|
|
|
|
|
pkgHeight := int(ir.uint64())
|
|
|
|
|
if pkg.Name == "" {
|
|
|
|
|
pkg.Name = pkgName
|
|
|
|
|
pkg.Height = pkgHeight
|
|
|
|
|
numImport[pkgName]++
|
|
|
|
|
|
|
|
|
|
// TODO(mdempsky): This belongs somewhere else.
|
|
|
|
|
pkg.Lookup("_").Def = asTypesNode(nblank)
|
|
|
|
|
} else {
|
|
|
|
|
if pkg.Name != pkgName {
|
|
|
|
|
Fatalf("conflicting package names %v and %v for path %q", pkg.Name, pkgName, pkg.Path)
|
|
|
|
|
}
|
|
|
|
|
if pkg.Height != pkgHeight {
|
|
|
|
|
Fatalf("conflicting package heights %v and %v for path %q", pkg.Height, pkgHeight, pkg.Path)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for nSyms := ir.uint64(); nSyms > 0; nSyms-- {
|
|
|
|
|
s := pkg.Lookup(p.stringAt(ir.uint64()))
|
|
|
|
|
off := ir.uint64()
|
|
|
|
|
|
|
|
|
|
if _, ok := declImporter[s]; ok {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
declImporter[s] = iimporterAndOffset{p, off}
|
|
|
|
|
|
|
|
|
|
// Create stub declaration. If used, this will
|
|
|
|
|
// be overwritten by expandDecl.
|
|
|
|
|
if s.Def != nil {
|
|
|
|
|
Fatalf("unexpected definition for %v: %v", s, asNode(s.Def))
|
|
|
|
|
}
|
|
|
|
|
s.Def = asTypesNode(npos(src.NoXPos, dclname(s)))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Inline body index.
|
|
|
|
|
for nPkgs := ir.uint64(); nPkgs > 0; nPkgs-- {
|
|
|
|
|
pkg := p.pkgAt(ir.uint64())
|
|
|
|
|
|
|
|
|
|
for nSyms := ir.uint64(); nSyms > 0; nSyms-- {
|
|
|
|
|
s := pkg.Lookup(p.stringAt(ir.uint64()))
|
|
|
|
|
off := ir.uint64()
|
|
|
|
|
|
|
|
|
|
if _, ok := inlineImporter[s]; ok {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
inlineImporter[s] = iimporterAndOffset{p, off}
|
|
|
|
|
}
|
|
|
|
|
}
|
[dev.link] cmd/internal/goobj2: add index fingerprint to object file
The new object files use indices for symbol references, instead
of names. Fundamental to the design, it requires that the
importing and imported packages have consistent view of symbol
indices. The Go command should already ensure this, when using
"go build". But in case it goes wrong, it could lead to obscure
errors like run-time crashes. It would be better to check the
index consistency at build time.
To do that, we add a fingerprint to each object file, which is
a hash of symbol indices. In the object file it records the
fingerprints of all imported packages, as well as its own
fingerprint. At link time, the linker checks that a package's
fingerprint matches the fingerprint recorded in the importing
packages, and issue an error if they don't match.
This CL does the first part: introducing the fingerprint in the
object file, and propagating fingerprints through
importing/exporting by the compiler. It is not yet used by the
linker. Next CL will do.
Change-Id: I0aa372da652e4afb11f2867cb71689a3e3f9966e
Reviewed-on: https://go-review.googlesource.com/c/go/+/229617
Reviewed-by: Austin Clements <austin@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
Reviewed-by: Jeremy Faller <jeremy@golang.org>
2020-04-22 19:21:30 -04:00
|
|
|
|
|
|
|
|
// Fingerprint
|
2020-05-05 13:12:01 -04:00
|
|
|
n, err := io.ReadFull(in, fingerprint[:])
|
[dev.link] cmd/internal/goobj2: add index fingerprint to object file
The new object files use indices for symbol references, instead
of names. Fundamental to the design, it requires that the
importing and imported packages have consistent view of symbol
indices. The Go command should already ensure this, when using
"go build". But in case it goes wrong, it could lead to obscure
errors like run-time crashes. It would be better to check the
index consistency at build time.
To do that, we add a fingerprint to each object file, which is
a hash of symbol indices. In the object file it records the
fingerprints of all imported packages, as well as its own
fingerprint. At link time, the linker checks that a package's
fingerprint matches the fingerprint recorded in the importing
packages, and issue an error if they don't match.
This CL does the first part: introducing the fingerprint in the
object file, and propagating fingerprints through
importing/exporting by the compiler. It is not yet used by the
linker. Next CL will do.
Change-Id: I0aa372da652e4afb11f2867cb71689a3e3f9966e
Reviewed-on: https://go-review.googlesource.com/c/go/+/229617
Reviewed-by: Austin Clements <austin@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
Reviewed-by: Jeremy Faller <jeremy@golang.org>
2020-04-22 19:21:30 -04:00
|
|
|
if err != nil || n != len(fingerprint) {
|
|
|
|
|
yyerror("import %s: error reading fingerprint", pkg.Path)
|
|
|
|
|
errorexit()
|
|
|
|
|
}
|
|
|
|
|
return fingerprint
|
2018-04-01 01:55:55 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type iimporter struct {
|
|
|
|
|
ipkg *types.Pkg
|
|
|
|
|
|
|
|
|
|
pkgCache map[uint64]*types.Pkg
|
|
|
|
|
posBaseCache map[uint64]*src.PosBase
|
|
|
|
|
typCache map[uint64]*types.Type
|
|
|
|
|
|
|
|
|
|
stringData string
|
|
|
|
|
declData string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (p *iimporter) stringAt(off uint64) string {
|
|
|
|
|
var x [binary.MaxVarintLen64]byte
|
|
|
|
|
n := copy(x[:], p.stringData[off:])
|
|
|
|
|
|
|
|
|
|
slen, n := binary.Uvarint(x[:n])
|
|
|
|
|
if n <= 0 {
|
|
|
|
|
Fatalf("varint failed")
|
|
|
|
|
}
|
|
|
|
|
spos := off + uint64(n)
|
|
|
|
|
return p.stringData[spos : spos+slen]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (p *iimporter) posBaseAt(off uint64) *src.PosBase {
|
|
|
|
|
if posBase, ok := p.posBaseCache[off]; ok {
|
|
|
|
|
return posBase
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
file := p.stringAt(off)
|
|
|
|
|
posBase := src.NewFileBase(file, file)
|
|
|
|
|
p.posBaseCache[off] = posBase
|
|
|
|
|
return posBase
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (p *iimporter) pkgAt(off uint64) *types.Pkg {
|
|
|
|
|
if pkg, ok := p.pkgCache[off]; ok {
|
|
|
|
|
return pkg
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pkg := p.ipkg
|
|
|
|
|
if pkgPath := p.stringAt(off); pkgPath != "" {
|
|
|
|
|
pkg = types.NewPkg(pkgPath, "")
|
|
|
|
|
}
|
|
|
|
|
p.pkgCache[off] = pkg
|
|
|
|
|
return pkg
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// An importReader keeps state for reading an individual imported
|
|
|
|
|
// object (declaration or inline body).
|
|
|
|
|
type importReader struct {
|
|
|
|
|
strings.Reader
|
|
|
|
|
p *iimporter
|
|
|
|
|
|
2019-09-23 14:25:22 -07:00
|
|
|
currPkg *types.Pkg
|
|
|
|
|
prevBase *src.PosBase
|
|
|
|
|
prevLine int64
|
|
|
|
|
prevColumn int64
|
2018-04-01 01:55:55 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (p *iimporter) newReader(off uint64, pkg *types.Pkg) *importReader {
|
|
|
|
|
r := &importReader{
|
|
|
|
|
p: p,
|
|
|
|
|
currPkg: pkg,
|
|
|
|
|
}
|
|
|
|
|
// (*strings.Reader).Reset wasn't added until Go 1.7, and we
|
|
|
|
|
// need to build with Go 1.4.
|
|
|
|
|
r.Reader = *strings.NewReader(p.declData[off:])
|
|
|
|
|
return r
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *importReader) string() string { return r.p.stringAt(r.uint64()) }
|
|
|
|
|
func (r *importReader) posBase() *src.PosBase { return r.p.posBaseAt(r.uint64()) }
|
|
|
|
|
func (r *importReader) pkg() *types.Pkg { return r.p.pkgAt(r.uint64()) }
|
|
|
|
|
|
|
|
|
|
func (r *importReader) setPkg() {
|
|
|
|
|
r.currPkg = r.pkg()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *importReader) doDecl(n *Node) {
|
|
|
|
|
if n.Op != ONONAME {
|
|
|
|
|
Fatalf("doDecl: unexpected Op for %v: %v", n.Sym, n.Op)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tag := r.byte()
|
|
|
|
|
pos := r.pos()
|
|
|
|
|
|
|
|
|
|
switch tag {
|
|
|
|
|
case 'A':
|
|
|
|
|
typ := r.typ()
|
|
|
|
|
|
|
|
|
|
importalias(r.p.ipkg, pos, n.Sym, typ)
|
|
|
|
|
|
|
|
|
|
case 'C':
|
|
|
|
|
typ, val := r.value()
|
|
|
|
|
|
|
|
|
|
importconst(r.p.ipkg, pos, n.Sym, typ, val)
|
|
|
|
|
|
|
|
|
|
case 'F':
|
|
|
|
|
typ := r.signature(nil)
|
|
|
|
|
|
|
|
|
|
importfunc(r.p.ipkg, pos, n.Sym, typ)
|
|
|
|
|
r.funcExt(n)
|
|
|
|
|
|
|
|
|
|
case 'T':
|
|
|
|
|
// Types can be recursive. We need to setup a stub
|
|
|
|
|
// declaration before recursing.
|
|
|
|
|
t := importtype(r.p.ipkg, pos, n.Sym)
|
|
|
|
|
|
2018-07-06 12:14:22 -07:00
|
|
|
// We also need to defer width calculations until
|
|
|
|
|
// after the underlying type has been assigned.
|
2019-08-30 14:54:21 -07:00
|
|
|
defercheckwidth()
|
2018-04-01 01:55:55 -07:00
|
|
|
underlying := r.typ()
|
2019-08-30 15:59:16 -07:00
|
|
|
setUnderlying(t, underlying)
|
2019-08-30 14:54:21 -07:00
|
|
|
resumecheckwidth()
|
2018-04-01 01:55:55 -07:00
|
|
|
|
|
|
|
|
if underlying.IsInterface() {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ms := make([]*types.Field, r.uint64())
|
|
|
|
|
for i := range ms {
|
|
|
|
|
mpos := r.pos()
|
|
|
|
|
msym := r.ident()
|
|
|
|
|
recv := r.param()
|
|
|
|
|
mtyp := r.signature(recv)
|
|
|
|
|
|
|
|
|
|
f := types.NewField()
|
|
|
|
|
f.Pos = mpos
|
|
|
|
|
f.Sym = msym
|
|
|
|
|
f.Type = mtyp
|
|
|
|
|
ms[i] = f
|
|
|
|
|
|
|
|
|
|
m := newfuncnamel(mpos, methodSym(recv.Type, msym))
|
|
|
|
|
m.Type = mtyp
|
|
|
|
|
m.SetClass(PFUNC)
|
2018-11-01 12:20:28 -04:00
|
|
|
// methodSym already marked m.Sym as a function.
|
2018-04-01 01:55:55 -07:00
|
|
|
|
|
|
|
|
// (comment from parser.go)
|
|
|
|
|
// inl.C's inlnode in on a dotmeth node expects to find the inlineable body as
|
|
|
|
|
// (dotmeth's type).Nname.Inl, and dotmeth's type has been pulled
|
|
|
|
|
// out by typecheck's lookdot as this $$.ttype. So by providing
|
|
|
|
|
// this back link here we avoid special casing there.
|
|
|
|
|
mtyp.SetNname(asTypesNode(m))
|
|
|
|
|
}
|
|
|
|
|
t.Methods().Set(ms)
|
|
|
|
|
|
|
|
|
|
for _, m := range ms {
|
|
|
|
|
r.methExt(m)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case 'V':
|
|
|
|
|
typ := r.typ()
|
|
|
|
|
|
|
|
|
|
importvar(r.p.ipkg, pos, n.Sym, typ)
|
|
|
|
|
r.varExt(n)
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
Fatalf("unexpected tag: %v", tag)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (p *importReader) value() (typ *types.Type, v Val) {
|
|
|
|
|
typ = p.typ()
|
|
|
|
|
|
|
|
|
|
switch constTypeOf(typ) {
|
|
|
|
|
case CTNIL:
|
|
|
|
|
v.U = &NilVal{}
|
|
|
|
|
case CTBOOL:
|
|
|
|
|
v.U = p.bool()
|
|
|
|
|
case CTSTR:
|
|
|
|
|
v.U = p.string()
|
|
|
|
|
case CTINT:
|
|
|
|
|
x := new(Mpint)
|
|
|
|
|
x.Rune = typ == types.Idealrune
|
|
|
|
|
p.mpint(&x.Val, typ)
|
|
|
|
|
v.U = x
|
|
|
|
|
case CTFLT:
|
|
|
|
|
x := newMpflt()
|
|
|
|
|
p.float(x, typ)
|
|
|
|
|
v.U = x
|
|
|
|
|
case CTCPLX:
|
|
|
|
|
x := newMpcmplx()
|
|
|
|
|
p.float(&x.Real, typ)
|
|
|
|
|
p.float(&x.Imag, typ)
|
|
|
|
|
v.U = x
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (p *importReader) mpint(x *big.Int, typ *types.Type) {
|
|
|
|
|
signed, maxBytes := intSize(typ)
|
|
|
|
|
|
|
|
|
|
maxSmall := 256 - maxBytes
|
|
|
|
|
if signed {
|
|
|
|
|
maxSmall = 256 - 2*maxBytes
|
|
|
|
|
}
|
|
|
|
|
if maxBytes == 1 {
|
|
|
|
|
maxSmall = 256
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
n, _ := p.ReadByte()
|
|
|
|
|
if uint(n) < maxSmall {
|
|
|
|
|
v := int64(n)
|
|
|
|
|
if signed {
|
|
|
|
|
v >>= 1
|
|
|
|
|
if n&1 != 0 {
|
|
|
|
|
v = ^v
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
x.SetInt64(v)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
v := -n
|
|
|
|
|
if signed {
|
|
|
|
|
v = -(n &^ 1) >> 1
|
|
|
|
|
}
|
|
|
|
|
if v < 1 || uint(v) > maxBytes {
|
|
|
|
|
Fatalf("weird decoding: %v, %v => %v", n, signed, v)
|
|
|
|
|
}
|
|
|
|
|
b := make([]byte, v)
|
|
|
|
|
p.Read(b)
|
|
|
|
|
x.SetBytes(b)
|
|
|
|
|
if signed && n&1 != 0 {
|
|
|
|
|
x.Neg(x)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (p *importReader) float(x *Mpflt, typ *types.Type) {
|
|
|
|
|
var mant big.Int
|
|
|
|
|
p.mpint(&mant, typ)
|
|
|
|
|
m := x.Val.SetInt(&mant)
|
|
|
|
|
if m.Sign() == 0 {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
m.SetMantExp(m, int(p.int64()))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *importReader) ident() *types.Sym {
|
|
|
|
|
name := r.string()
|
|
|
|
|
if name == "" {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
pkg := r.currPkg
|
|
|
|
|
if types.IsExported(name) {
|
|
|
|
|
pkg = localpkg
|
|
|
|
|
}
|
|
|
|
|
return pkg.Lookup(name)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *importReader) qualifiedIdent() *types.Sym {
|
|
|
|
|
name := r.string()
|
|
|
|
|
pkg := r.pkg()
|
|
|
|
|
return pkg.Lookup(name)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *importReader) pos() src.XPos {
|
|
|
|
|
delta := r.int64()
|
2019-09-23 14:25:22 -07:00
|
|
|
r.prevColumn += delta >> 1
|
|
|
|
|
if delta&1 != 0 {
|
|
|
|
|
delta = r.int64()
|
|
|
|
|
r.prevLine += delta >> 1
|
|
|
|
|
if delta&1 != 0 {
|
|
|
|
|
r.prevBase = r.posBase()
|
|
|
|
|
}
|
2018-04-01 01:55:55 -07:00
|
|
|
}
|
|
|
|
|
|
2019-09-23 14:25:22 -07:00
|
|
|
if (r.prevBase == nil || r.prevBase.AbsFilename() == "") && r.prevLine == 0 && r.prevColumn == 0 {
|
2018-04-01 01:55:55 -07:00
|
|
|
// TODO(mdempsky): Remove once we reliably write
|
|
|
|
|
// position information for all nodes.
|
|
|
|
|
return src.NoXPos
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if r.prevBase == nil {
|
|
|
|
|
Fatalf("missing posbase")
|
|
|
|
|
}
|
2019-09-23 14:25:22 -07:00
|
|
|
pos := src.MakePos(r.prevBase, uint(r.prevLine), uint(r.prevColumn))
|
2018-04-01 01:55:55 -07:00
|
|
|
return Ctxt.PosTable.XPos(pos)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *importReader) typ() *types.Type {
|
|
|
|
|
return r.p.typAt(r.uint64())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (p *iimporter) typAt(off uint64) *types.Type {
|
|
|
|
|
t, ok := p.typCache[off]
|
|
|
|
|
if !ok {
|
|
|
|
|
if off < predeclReserved {
|
|
|
|
|
Fatalf("predeclared type missing from cache: %d", off)
|
|
|
|
|
}
|
|
|
|
|
t = p.newReader(off-predeclReserved, nil).typ1()
|
|
|
|
|
p.typCache[off] = t
|
|
|
|
|
}
|
|
|
|
|
return t
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *importReader) typ1() *types.Type {
|
|
|
|
|
switch k := r.kind(); k {
|
|
|
|
|
default:
|
|
|
|
|
Fatalf("unexpected kind tag in %q: %v", r.p.ipkg.Path, k)
|
|
|
|
|
return nil
|
|
|
|
|
|
|
|
|
|
case definedType:
|
|
|
|
|
// We might be called from within doInline, in which
|
|
|
|
|
// case Sym.Def can point to declared parameters
|
|
|
|
|
// instead of the top-level types. Also, we don't
|
|
|
|
|
// support inlining functions with local defined
|
|
|
|
|
// types. Therefore, this must be a package-scope
|
|
|
|
|
// type.
|
|
|
|
|
n := asNode(r.qualifiedIdent().PkgDef())
|
|
|
|
|
if n.Op == ONONAME {
|
|
|
|
|
expandDecl(n)
|
|
|
|
|
}
|
|
|
|
|
if n.Op != OTYPE {
|
|
|
|
|
Fatalf("expected OTYPE, got %v: %v, %v", n.Op, n.Sym, n)
|
|
|
|
|
}
|
|
|
|
|
return n.Type
|
|
|
|
|
case pointerType:
|
|
|
|
|
return types.NewPtr(r.typ())
|
|
|
|
|
case sliceType:
|
|
|
|
|
return types.NewSlice(r.typ())
|
|
|
|
|
case arrayType:
|
|
|
|
|
n := r.uint64()
|
|
|
|
|
return types.NewArray(r.typ(), int64(n))
|
|
|
|
|
case chanType:
|
|
|
|
|
dir := types.ChanDir(r.uint64())
|
|
|
|
|
return types.NewChan(r.typ(), dir)
|
|
|
|
|
case mapType:
|
|
|
|
|
return types.NewMap(r.typ(), r.typ())
|
|
|
|
|
|
|
|
|
|
case signatureType:
|
|
|
|
|
r.setPkg()
|
|
|
|
|
return r.signature(nil)
|
|
|
|
|
|
|
|
|
|
case structType:
|
|
|
|
|
r.setPkg()
|
|
|
|
|
|
|
|
|
|
fs := make([]*types.Field, r.uint64())
|
|
|
|
|
for i := range fs {
|
|
|
|
|
pos := r.pos()
|
|
|
|
|
sym := r.ident()
|
|
|
|
|
typ := r.typ()
|
|
|
|
|
emb := r.bool()
|
|
|
|
|
note := r.string()
|
|
|
|
|
|
|
|
|
|
f := types.NewField()
|
|
|
|
|
f.Pos = pos
|
|
|
|
|
f.Sym = sym
|
|
|
|
|
f.Type = typ
|
|
|
|
|
if emb {
|
|
|
|
|
f.Embedded = 1
|
|
|
|
|
}
|
|
|
|
|
f.Note = note
|
|
|
|
|
fs[i] = f
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
t := types.New(TSTRUCT)
|
|
|
|
|
t.SetPkg(r.currPkg)
|
|
|
|
|
t.SetFields(fs)
|
|
|
|
|
return t
|
|
|
|
|
|
|
|
|
|
case interfaceType:
|
|
|
|
|
r.setPkg()
|
|
|
|
|
|
|
|
|
|
embeddeds := make([]*types.Field, r.uint64())
|
|
|
|
|
for i := range embeddeds {
|
|
|
|
|
pos := r.pos()
|
|
|
|
|
typ := r.typ()
|
|
|
|
|
|
|
|
|
|
f := types.NewField()
|
|
|
|
|
f.Pos = pos
|
|
|
|
|
f.Type = typ
|
|
|
|
|
embeddeds[i] = f
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
methods := make([]*types.Field, r.uint64())
|
|
|
|
|
for i := range methods {
|
|
|
|
|
pos := r.pos()
|
|
|
|
|
sym := r.ident()
|
|
|
|
|
typ := r.signature(fakeRecvField())
|
|
|
|
|
|
|
|
|
|
f := types.NewField()
|
|
|
|
|
f.Pos = pos
|
|
|
|
|
f.Sym = sym
|
|
|
|
|
f.Type = typ
|
|
|
|
|
methods[i] = f
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
t := types.New(TINTER)
|
|
|
|
|
t.SetPkg(r.currPkg)
|
|
|
|
|
t.SetInterface(append(embeddeds, methods...))
|
2018-07-06 12:14:22 -07:00
|
|
|
|
|
|
|
|
// Ensure we expand the interface in the frontend (#25055).
|
|
|
|
|
checkwidth(t)
|
|
|
|
|
|
2018-04-01 01:55:55 -07:00
|
|
|
return t
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *importReader) kind() itag {
|
|
|
|
|
return itag(r.uint64())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *importReader) signature(recv *types.Field) *types.Type {
|
|
|
|
|
params := r.paramList()
|
|
|
|
|
results := r.paramList()
|
|
|
|
|
if n := len(params); n > 0 {
|
2018-11-18 08:34:38 -08:00
|
|
|
params[n-1].SetIsDDD(r.bool())
|
2018-04-01 01:55:55 -07:00
|
|
|
}
|
|
|
|
|
t := functypefield(recv, params, results)
|
|
|
|
|
t.SetPkg(r.currPkg)
|
|
|
|
|
return t
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *importReader) paramList() []*types.Field {
|
|
|
|
|
fs := make([]*types.Field, r.uint64())
|
|
|
|
|
for i := range fs {
|
|
|
|
|
fs[i] = r.param()
|
|
|
|
|
}
|
|
|
|
|
return fs
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *importReader) param() *types.Field {
|
|
|
|
|
f := types.NewField()
|
|
|
|
|
f.Pos = r.pos()
|
|
|
|
|
f.Sym = r.ident()
|
|
|
|
|
f.Type = r.typ()
|
|
|
|
|
return f
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *importReader) bool() bool {
|
|
|
|
|
return r.uint64() != 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *importReader) int64() int64 {
|
|
|
|
|
n, err := binary.ReadVarint(r)
|
|
|
|
|
if err != nil {
|
|
|
|
|
Fatalf("readVarint: %v", err)
|
|
|
|
|
}
|
|
|
|
|
return n
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *importReader) uint64() uint64 {
|
|
|
|
|
n, err := binary.ReadUvarint(r)
|
|
|
|
|
if err != nil {
|
|
|
|
|
Fatalf("readVarint: %v", err)
|
|
|
|
|
}
|
|
|
|
|
return n
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *importReader) byte() byte {
|
|
|
|
|
x, err := r.ReadByte()
|
|
|
|
|
if err != nil {
|
|
|
|
|
Fatalf("declReader.ReadByte: %v", err)
|
|
|
|
|
}
|
|
|
|
|
return x
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Compiler-specific extensions.
|
|
|
|
|
|
|
|
|
|
func (r *importReader) varExt(n *Node) {
|
|
|
|
|
r.linkname(n.Sym)
|
[dev.link] cmd/compile, cmd/asm: assign index to symbols
We are planning to use indices for symbol references, instead of
symbol names. Here we assign indices to symbols defined in the
package being compiled, and propagate the indices to the
dependent packages in the export data.
A symbol is referenced by a tuple, (package index, symbol index).
Normally, for a given symbol, this index is unique, and the
symbol index is globally consistent (but with exceptions, see
below). The package index is local to a compilation. For example,
when compiling the fmt package, fmt.Println gets assigned index
25, then all packages that reference fmt.Println will refer it
as (X, 25) with some X. X is the index for the fmt package, which
may differ in different compilations.
There are some symbols that do not have clear package affiliation,
such as dupOK symbols and linknamed symbols. We cannot give them
globally consistent indices. We categorize them as non-package
symbols, assign them with package index 1 and a symbol index that
is only meaningful locally.
Currently nothing will consume the indices.
All this is behind a flag, -newobj. The flag needs to be set for
all builds (-gcflags=all=-newobj -asmflags=all=-newobj), or none.
Change-Id: I18e489c531e9a9fbc668519af92c6116b7308cab
Reviewed-on: https://go-review.googlesource.com/c/go/+/196029
Reviewed-by: Than McIntosh <thanm@google.com>
2019-09-11 16:17:01 -04:00
|
|
|
r.symIdx(n.Sym)
|
2018-04-01 01:55:55 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *importReader) funcExt(n *Node) {
|
|
|
|
|
r.linkname(n.Sym)
|
[dev.link] cmd/compile, cmd/asm: assign index to symbols
We are planning to use indices for symbol references, instead of
symbol names. Here we assign indices to symbols defined in the
package being compiled, and propagate the indices to the
dependent packages in the export data.
A symbol is referenced by a tuple, (package index, symbol index).
Normally, for a given symbol, this index is unique, and the
symbol index is globally consistent (but with exceptions, see
below). The package index is local to a compilation. For example,
when compiling the fmt package, fmt.Println gets assigned index
25, then all packages that reference fmt.Println will refer it
as (X, 25) with some X. X is the index for the fmt package, which
may differ in different compilations.
There are some symbols that do not have clear package affiliation,
such as dupOK symbols and linknamed symbols. We cannot give them
globally consistent indices. We categorize them as non-package
symbols, assign them with package index 1 and a symbol index that
is only meaningful locally.
Currently nothing will consume the indices.
All this is behind a flag, -newobj. The flag needs to be set for
all builds (-gcflags=all=-newobj -asmflags=all=-newobj), or none.
Change-Id: I18e489c531e9a9fbc668519af92c6116b7308cab
Reviewed-on: https://go-review.googlesource.com/c/go/+/196029
Reviewed-by: Than McIntosh <thanm@google.com>
2019-09-11 16:17:01 -04:00
|
|
|
r.symIdx(n.Sym)
|
2018-04-01 01:55:55 -07:00
|
|
|
|
|
|
|
|
// Escape analysis.
|
2019-11-01 18:12:27 +07:00
|
|
|
for _, fs := range &types.RecvsParams {
|
2018-04-01 01:55:55 -07:00
|
|
|
for _, f := range fs(n.Type).FieldSlice() {
|
|
|
|
|
f.Note = r.string()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Inline body.
|
|
|
|
|
if u := r.uint64(); u > 0 {
|
|
|
|
|
n.Func.Inl = &Inline{
|
|
|
|
|
Cost: int32(u - 1),
|
|
|
|
|
}
|
2018-07-26 12:46:50 +03:00
|
|
|
n.Func.Endlineno = r.pos()
|
2018-04-01 01:55:55 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *importReader) methExt(m *types.Field) {
|
|
|
|
|
if r.bool() {
|
|
|
|
|
m.SetNointerface(true)
|
|
|
|
|
}
|
|
|
|
|
r.funcExt(asNode(m.Type.Nname()))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *importReader) linkname(s *types.Sym) {
|
|
|
|
|
s.Linkname = r.string()
|
|
|
|
|
}
|
|
|
|
|
|
[dev.link] cmd/compile, cmd/asm: assign index to symbols
We are planning to use indices for symbol references, instead of
symbol names. Here we assign indices to symbols defined in the
package being compiled, and propagate the indices to the
dependent packages in the export data.
A symbol is referenced by a tuple, (package index, symbol index).
Normally, for a given symbol, this index is unique, and the
symbol index is globally consistent (but with exceptions, see
below). The package index is local to a compilation. For example,
when compiling the fmt package, fmt.Println gets assigned index
25, then all packages that reference fmt.Println will refer it
as (X, 25) with some X. X is the index for the fmt package, which
may differ in different compilations.
There are some symbols that do not have clear package affiliation,
such as dupOK symbols and linknamed symbols. We cannot give them
globally consistent indices. We categorize them as non-package
symbols, assign them with package index 1 and a symbol index that
is only meaningful locally.
Currently nothing will consume the indices.
All this is behind a flag, -newobj. The flag needs to be set for
all builds (-gcflags=all=-newobj -asmflags=all=-newobj), or none.
Change-Id: I18e489c531e9a9fbc668519af92c6116b7308cab
Reviewed-on: https://go-review.googlesource.com/c/go/+/196029
Reviewed-by: Than McIntosh <thanm@google.com>
2019-09-11 16:17:01 -04:00
|
|
|
func (r *importReader) symIdx(s *types.Sym) {
|
2020-05-01 19:13:30 -04:00
|
|
|
lsym := s.Linksym()
|
|
|
|
|
idx := int32(r.int64())
|
|
|
|
|
if idx != -1 {
|
|
|
|
|
if s.Linkname != "" {
|
|
|
|
|
Fatalf("bad index for linknamed symbol: %v %d\n", lsym, idx)
|
[dev.link] cmd/compile, cmd/asm: assign index to symbols
We are planning to use indices for symbol references, instead of
symbol names. Here we assign indices to symbols defined in the
package being compiled, and propagate the indices to the
dependent packages in the export data.
A symbol is referenced by a tuple, (package index, symbol index).
Normally, for a given symbol, this index is unique, and the
symbol index is globally consistent (but with exceptions, see
below). The package index is local to a compilation. For example,
when compiling the fmt package, fmt.Println gets assigned index
25, then all packages that reference fmt.Println will refer it
as (X, 25) with some X. X is the index for the fmt package, which
may differ in different compilations.
There are some symbols that do not have clear package affiliation,
such as dupOK symbols and linknamed symbols. We cannot give them
globally consistent indices. We categorize them as non-package
symbols, assign them with package index 1 and a symbol index that
is only meaningful locally.
Currently nothing will consume the indices.
All this is behind a flag, -newobj. The flag needs to be set for
all builds (-gcflags=all=-newobj -asmflags=all=-newobj), or none.
Change-Id: I18e489c531e9a9fbc668519af92c6116b7308cab
Reviewed-on: https://go-review.googlesource.com/c/go/+/196029
Reviewed-by: Than McIntosh <thanm@google.com>
2019-09-11 16:17:01 -04:00
|
|
|
}
|
2020-05-01 19:13:30 -04:00
|
|
|
lsym.SymIdx = idx
|
|
|
|
|
lsym.Set(obj.AttrIndexed, true)
|
[dev.link] cmd/compile, cmd/asm: assign index to symbols
We are planning to use indices for symbol references, instead of
symbol names. Here we assign indices to symbols defined in the
package being compiled, and propagate the indices to the
dependent packages in the export data.
A symbol is referenced by a tuple, (package index, symbol index).
Normally, for a given symbol, this index is unique, and the
symbol index is globally consistent (but with exceptions, see
below). The package index is local to a compilation. For example,
when compiling the fmt package, fmt.Println gets assigned index
25, then all packages that reference fmt.Println will refer it
as (X, 25) with some X. X is the index for the fmt package, which
may differ in different compilations.
There are some symbols that do not have clear package affiliation,
such as dupOK symbols and linknamed symbols. We cannot give them
globally consistent indices. We categorize them as non-package
symbols, assign them with package index 1 and a symbol index that
is only meaningful locally.
Currently nothing will consume the indices.
All this is behind a flag, -newobj. The flag needs to be set for
all builds (-gcflags=all=-newobj -asmflags=all=-newobj), or none.
Change-Id: I18e489c531e9a9fbc668519af92c6116b7308cab
Reviewed-on: https://go-review.googlesource.com/c/go/+/196029
Reviewed-by: Than McIntosh <thanm@google.com>
2019-09-11 16:17:01 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-01 01:55:55 -07:00
|
|
|
func (r *importReader) doInline(n *Node) {
|
|
|
|
|
if len(n.Func.Inl.Body) != 0 {
|
|
|
|
|
Fatalf("%v already has inline body", n)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
funchdr(n)
|
|
|
|
|
body := r.stmtList()
|
|
|
|
|
funcbody()
|
|
|
|
|
if body == nil {
|
|
|
|
|
//
|
|
|
|
|
// Make sure empty body is not interpreted as
|
|
|
|
|
// no inlineable body (see also parser.fnbody)
|
|
|
|
|
// (not doing so can cause significant performance
|
|
|
|
|
// degradation due to unnecessary calls to empty
|
|
|
|
|
// functions).
|
|
|
|
|
body = []*Node{}
|
|
|
|
|
}
|
|
|
|
|
n.Func.Inl.Body = body
|
|
|
|
|
|
|
|
|
|
importlist = append(importlist, n)
|
|
|
|
|
|
|
|
|
|
if Debug['E'] > 0 && Debug['m'] > 2 {
|
|
|
|
|
if Debug['m'] > 3 {
|
|
|
|
|
fmt.Printf("inl body for %v %#v: %+v\n", n, n.Type, asNodes(n.Func.Inl.Body))
|
|
|
|
|
} else {
|
|
|
|
|
fmt.Printf("inl body for %v %#v: %v\n", n, n.Type, asNodes(n.Func.Inl.Body))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
// Inlined function bodies
|
|
|
|
|
|
|
|
|
|
// Approach: Read nodes and use them to create/declare the same data structures
|
|
|
|
|
// as done originally by the (hidden) parser by closely following the parser's
|
|
|
|
|
// original code. In other words, "parsing" the import data (which happens to
|
|
|
|
|
// be encoded in binary rather textual form) is the best way at the moment to
|
|
|
|
|
// re-establish the syntax tree's invariants. At some future point we might be
|
|
|
|
|
// able to avoid this round-about way and create the rewritten nodes directly,
|
|
|
|
|
// possibly avoiding a lot of duplicate work (name resolution, type checking).
|
|
|
|
|
//
|
|
|
|
|
// Refined nodes (e.g., ODOTPTR as a refinement of OXDOT) are exported as their
|
|
|
|
|
// unrefined nodes (since this is what the importer uses). The respective case
|
|
|
|
|
// entries are unreachable in the importer.
|
|
|
|
|
|
|
|
|
|
func (r *importReader) stmtList() []*Node {
|
|
|
|
|
var list []*Node
|
|
|
|
|
for {
|
|
|
|
|
n := r.node()
|
|
|
|
|
if n == nil {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
// OBLOCK nodes may be created when importing ODCL nodes - unpack them
|
|
|
|
|
if n.Op == OBLOCK {
|
|
|
|
|
list = append(list, n.List.Slice()...)
|
|
|
|
|
} else {
|
|
|
|
|
list = append(list, n)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
return list
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *importReader) exprList() []*Node {
|
|
|
|
|
var list []*Node
|
|
|
|
|
for {
|
|
|
|
|
n := r.expr()
|
|
|
|
|
if n == nil {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
list = append(list, n)
|
|
|
|
|
}
|
|
|
|
|
return list
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *importReader) expr() *Node {
|
|
|
|
|
n := r.node()
|
|
|
|
|
if n != nil && n.Op == OBLOCK {
|
|
|
|
|
Fatalf("unexpected block node: %v", n)
|
|
|
|
|
}
|
|
|
|
|
return n
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO(gri) split into expr and stmt
|
|
|
|
|
func (r *importReader) node() *Node {
|
|
|
|
|
switch op := r.op(); op {
|
|
|
|
|
// expressions
|
|
|
|
|
// case OPAREN:
|
|
|
|
|
// unreachable - unpacked by exporter
|
|
|
|
|
|
|
|
|
|
case OLITERAL:
|
|
|
|
|
pos := r.pos()
|
|
|
|
|
typ, val := r.value()
|
|
|
|
|
|
|
|
|
|
n := npos(pos, nodlit(val))
|
|
|
|
|
n.Type = typ
|
|
|
|
|
return n
|
|
|
|
|
|
|
|
|
|
case ONONAME:
|
|
|
|
|
return mkname(r.qualifiedIdent())
|
|
|
|
|
|
|
|
|
|
case ONAME:
|
|
|
|
|
return mkname(r.ident())
|
|
|
|
|
|
|
|
|
|
// case OPACK, ONONAME:
|
|
|
|
|
// unreachable - should have been resolved by typechecking
|
|
|
|
|
|
|
|
|
|
case OTYPE:
|
|
|
|
|
return typenod(r.typ())
|
|
|
|
|
|
|
|
|
|
// case OTARRAY, OTMAP, OTCHAN, OTSTRUCT, OTINTER, OTFUNC:
|
|
|
|
|
// unreachable - should have been resolved by typechecking
|
|
|
|
|
|
|
|
|
|
// case OCLOSURE:
|
|
|
|
|
// unimplemented
|
|
|
|
|
|
2019-09-25 00:26:06 -07:00
|
|
|
// case OPTRLIT:
|
|
|
|
|
// unreachable - mapped to case OADDR below by exporter
|
2018-04-01 01:55:55 -07:00
|
|
|
|
|
|
|
|
case OSTRUCTLIT:
|
|
|
|
|
// TODO(mdempsky): Export position information for OSTRUCTKEY nodes.
|
|
|
|
|
savedlineno := lineno
|
|
|
|
|
lineno = r.pos()
|
|
|
|
|
n := nodl(lineno, OCOMPLIT, nil, typenod(r.typ()))
|
|
|
|
|
n.List.Set(r.elemList()) // special handling of field names
|
|
|
|
|
lineno = savedlineno
|
|
|
|
|
return n
|
|
|
|
|
|
|
|
|
|
// case OARRAYLIT, OSLICELIT, OMAPLIT:
|
|
|
|
|
// unreachable - mapped to case OCOMPLIT below by exporter
|
|
|
|
|
|
|
|
|
|
case OCOMPLIT:
|
|
|
|
|
n := nodl(r.pos(), OCOMPLIT, nil, typenod(r.typ()))
|
|
|
|
|
n.List.Set(r.exprList())
|
|
|
|
|
return n
|
|
|
|
|
|
|
|
|
|
case OKEY:
|
|
|
|
|
pos := r.pos()
|
|
|
|
|
left, right := r.exprsOrNil()
|
|
|
|
|
return nodl(pos, OKEY, left, right)
|
|
|
|
|
|
|
|
|
|
// case OSTRUCTKEY:
|
|
|
|
|
// unreachable - handled in case OSTRUCTLIT by elemList
|
|
|
|
|
|
|
|
|
|
// case OCALLPART:
|
|
|
|
|
// unimplemented
|
|
|
|
|
|
|
|
|
|
// case OXDOT, ODOT, ODOTPTR, ODOTINTER, ODOTMETH:
|
|
|
|
|
// unreachable - mapped to case OXDOT below by exporter
|
|
|
|
|
|
|
|
|
|
case OXDOT:
|
|
|
|
|
// see parser.new_dotname
|
|
|
|
|
return npos(r.pos(), nodSym(OXDOT, r.expr(), r.ident()))
|
|
|
|
|
|
|
|
|
|
// case ODOTTYPE, ODOTTYPE2:
|
|
|
|
|
// unreachable - mapped to case ODOTTYPE below by exporter
|
|
|
|
|
|
|
|
|
|
case ODOTTYPE:
|
|
|
|
|
n := nodl(r.pos(), ODOTTYPE, r.expr(), nil)
|
|
|
|
|
n.Type = r.typ()
|
|
|
|
|
return n
|
|
|
|
|
|
|
|
|
|
// case OINDEX, OINDEXMAP, OSLICE, OSLICESTR, OSLICEARR, OSLICE3, OSLICE3ARR:
|
|
|
|
|
// unreachable - mapped to cases below by exporter
|
|
|
|
|
|
|
|
|
|
case OINDEX:
|
|
|
|
|
return nodl(r.pos(), op, r.expr(), r.expr())
|
|
|
|
|
|
|
|
|
|
case OSLICE, OSLICE3:
|
|
|
|
|
n := nodl(r.pos(), op, r.expr(), nil)
|
|
|
|
|
low, high := r.exprsOrNil()
|
|
|
|
|
var max *Node
|
|
|
|
|
if n.Op.IsSlice3() {
|
|
|
|
|
max = r.expr()
|
|
|
|
|
}
|
|
|
|
|
n.SetSliceBounds(low, high, max)
|
|
|
|
|
return n
|
|
|
|
|
|
2018-11-18 08:34:38 -08:00
|
|
|
// case OCONV, OCONVIFACE, OCONVNOP, OBYTES2STR, ORUNES2STR, OSTR2BYTES, OSTR2RUNES, ORUNESTR:
|
2018-04-01 01:55:55 -07:00
|
|
|
// unreachable - mapped to OCONV case below by exporter
|
|
|
|
|
|
|
|
|
|
case OCONV:
|
|
|
|
|
n := nodl(r.pos(), OCONV, r.expr(), nil)
|
|
|
|
|
n.Type = r.typ()
|
|
|
|
|
return n
|
|
|
|
|
|
|
|
|
|
case OCOPY, OCOMPLEX, OREAL, OIMAG, OAPPEND, OCAP, OCLOSE, ODELETE, OLEN, OMAKE, ONEW, OPANIC, ORECOVER, OPRINT, OPRINTN:
|
|
|
|
|
n := npos(r.pos(), builtinCall(op))
|
|
|
|
|
n.List.Set(r.exprList())
|
|
|
|
|
if op == OAPPEND {
|
2018-11-18 08:34:38 -08:00
|
|
|
n.SetIsDDD(r.bool())
|
2018-04-01 01:55:55 -07:00
|
|
|
}
|
|
|
|
|
return n
|
|
|
|
|
|
|
|
|
|
// case OCALL, OCALLFUNC, OCALLMETH, OCALLINTER, OGETG:
|
|
|
|
|
// unreachable - mapped to OCALL case below by exporter
|
|
|
|
|
|
|
|
|
|
case OCALL:
|
cmd/compile: rewrite f(g()) for multi-value g() during typecheck
This is a re-attempt at CL 153841, which caused two regressions:
1. crypto/ecdsa failed to build with -gcflags=-l=4. This was because
when "t1, t2, ... := g(); f(t1, t2, ...)" was exported, we were losing
the first assignment from the call's Ninit field.
2. net/http/pprof failed to run with -gcflags=-N. This is due to a
conflict with CL 159717: as of that CL, package-scope initialization
statements are executed within the "init.ializer" function, rather
than the "init" function, and the generated temp variables need to be
moved accordingly too.
[Rest of description is as before.]
This CL moves order.go's copyRet logic for rewriting f(g()) into t1,
t2, ... := g(); f(t1, t2, ...) earlier into typecheck. This allows the
rest of the compiler to stop worrying about multi-value functions
appearing outside of OAS2FUNC nodes.
This changes compiler behavior in a few observable ways:
1. Typechecking error messages for builtin functions now use general
case error messages rather than unnecessarily differing ones.
2. Because f(g()) is rewritten before inlining, saved inline bodies
now see the rewritten form too. This could be addressed, but doesn't
seem worthwhile.
3. Most notably, this simplifies escape analysis and fixes a memory
corruption issue in esc.go. See #29197 for details.
Fixes #15992.
Fixes #29197.
Change-Id: I930b10f7e27af68a0944d6c9bfc8707c3fab27a4
Reviewed-on: https://go-review.googlesource.com/c/go/+/166983
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
2018-12-12 11:15:37 -08:00
|
|
|
n := nodl(r.pos(), OCALL, nil, nil)
|
|
|
|
|
n.Ninit.Set(r.stmtList())
|
|
|
|
|
n.Left = r.expr()
|
2018-04-01 01:55:55 -07:00
|
|
|
n.List.Set(r.exprList())
|
2018-11-18 08:34:38 -08:00
|
|
|
n.SetIsDDD(r.bool())
|
2018-04-01 01:55:55 -07:00
|
|
|
return n
|
|
|
|
|
|
|
|
|
|
case OMAKEMAP, OMAKECHAN, OMAKESLICE:
|
|
|
|
|
n := npos(r.pos(), builtinCall(OMAKE))
|
|
|
|
|
n.List.Append(typenod(r.typ()))
|
|
|
|
|
n.List.Append(r.exprList()...)
|
|
|
|
|
return n
|
|
|
|
|
|
|
|
|
|
// unary expressions
|
2018-11-18 08:34:38 -08:00
|
|
|
case OPLUS, ONEG, OADDR, OBITNOT, ODEREF, ONOT, ORECV:
|
2018-04-01 01:55:55 -07:00
|
|
|
return nodl(r.pos(), op, r.expr(), nil)
|
|
|
|
|
|
|
|
|
|
// binary expressions
|
|
|
|
|
case OADD, OAND, OANDAND, OANDNOT, ODIV, OEQ, OGE, OGT, OLE, OLT,
|
|
|
|
|
OLSH, OMOD, OMUL, ONE, OOR, OOROR, ORSH, OSEND, OSUB, OXOR:
|
|
|
|
|
return nodl(r.pos(), op, r.expr(), r.expr())
|
|
|
|
|
|
|
|
|
|
case OADDSTR:
|
|
|
|
|
pos := r.pos()
|
|
|
|
|
list := r.exprList()
|
|
|
|
|
x := npos(pos, list[0])
|
|
|
|
|
for _, y := range list[1:] {
|
|
|
|
|
x = nodl(pos, OADD, x, y)
|
|
|
|
|
}
|
|
|
|
|
return x
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------
|
|
|
|
|
// statements
|
|
|
|
|
case ODCL:
|
|
|
|
|
pos := r.pos()
|
|
|
|
|
lhs := npos(pos, dclname(r.ident()))
|
|
|
|
|
typ := typenod(r.typ())
|
|
|
|
|
return npos(pos, liststmt(variter([]*Node{lhs}, typ, nil))) // TODO(gri) avoid list creation
|
|
|
|
|
|
|
|
|
|
// case ODCLFIELD:
|
|
|
|
|
// unimplemented
|
|
|
|
|
|
|
|
|
|
// case OAS, OASWB:
|
|
|
|
|
// unreachable - mapped to OAS case below by exporter
|
|
|
|
|
|
|
|
|
|
case OAS:
|
|
|
|
|
return nodl(r.pos(), OAS, r.expr(), r.expr())
|
|
|
|
|
|
|
|
|
|
case OASOP:
|
|
|
|
|
n := nodl(r.pos(), OASOP, nil, nil)
|
|
|
|
|
n.SetSubOp(r.op())
|
|
|
|
|
n.Left = r.expr()
|
|
|
|
|
if !r.bool() {
|
|
|
|
|
n.Right = nodintconst(1)
|
|
|
|
|
n.SetImplicit(true)
|
|
|
|
|
} else {
|
|
|
|
|
n.Right = r.expr()
|
|
|
|
|
}
|
|
|
|
|
return n
|
|
|
|
|
|
|
|
|
|
// case OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV:
|
|
|
|
|
// unreachable - mapped to OAS2 case below by exporter
|
|
|
|
|
|
|
|
|
|
case OAS2:
|
|
|
|
|
n := nodl(r.pos(), OAS2, nil, nil)
|
|
|
|
|
n.List.Set(r.exprList())
|
|
|
|
|
n.Rlist.Set(r.exprList())
|
|
|
|
|
return n
|
|
|
|
|
|
|
|
|
|
case ORETURN:
|
|
|
|
|
n := nodl(r.pos(), ORETURN, nil, nil)
|
|
|
|
|
n.List.Set(r.exprList())
|
|
|
|
|
return n
|
|
|
|
|
|
|
|
|
|
// case ORETJMP:
|
|
|
|
|
// unreachable - generated by compiler for trampolin routines (not exported)
|
|
|
|
|
|
2018-11-18 08:34:38 -08:00
|
|
|
case OGO, ODEFER:
|
2018-04-01 01:55:55 -07:00
|
|
|
return nodl(r.pos(), op, r.expr(), nil)
|
|
|
|
|
|
|
|
|
|
case OIF:
|
|
|
|
|
n := nodl(r.pos(), OIF, nil, nil)
|
|
|
|
|
n.Ninit.Set(r.stmtList())
|
|
|
|
|
n.Left = r.expr()
|
|
|
|
|
n.Nbody.Set(r.stmtList())
|
|
|
|
|
n.Rlist.Set(r.stmtList())
|
|
|
|
|
return n
|
|
|
|
|
|
|
|
|
|
case OFOR:
|
|
|
|
|
n := nodl(r.pos(), OFOR, nil, nil)
|
|
|
|
|
n.Ninit.Set(r.stmtList())
|
|
|
|
|
n.Left, n.Right = r.exprsOrNil()
|
|
|
|
|
n.Nbody.Set(r.stmtList())
|
|
|
|
|
return n
|
|
|
|
|
|
|
|
|
|
case ORANGE:
|
|
|
|
|
n := nodl(r.pos(), ORANGE, nil, nil)
|
|
|
|
|
n.List.Set(r.stmtList())
|
|
|
|
|
n.Right = r.expr()
|
|
|
|
|
n.Nbody.Set(r.stmtList())
|
|
|
|
|
return n
|
|
|
|
|
|
|
|
|
|
case OSELECT, OSWITCH:
|
|
|
|
|
n := nodl(r.pos(), op, nil, nil)
|
|
|
|
|
n.Ninit.Set(r.stmtList())
|
|
|
|
|
n.Left, _ = r.exprsOrNil()
|
|
|
|
|
n.List.Set(r.stmtList())
|
|
|
|
|
return n
|
|
|
|
|
|
2019-09-17 18:32:04 -07:00
|
|
|
case OCASE:
|
|
|
|
|
n := nodl(r.pos(), OCASE, nil, nil)
|
2018-04-01 01:55:55 -07:00
|
|
|
n.List.Set(r.exprList())
|
|
|
|
|
// TODO(gri) eventually we must declare variables for type switch
|
|
|
|
|
// statements (type switch statements are not yet exported)
|
|
|
|
|
n.Nbody.Set(r.stmtList())
|
|
|
|
|
return n
|
|
|
|
|
|
|
|
|
|
case OFALL:
|
|
|
|
|
n := nodl(r.pos(), OFALL, nil, nil)
|
|
|
|
|
return n
|
|
|
|
|
|
|
|
|
|
case OBREAK, OCONTINUE:
|
|
|
|
|
pos := r.pos()
|
|
|
|
|
left, _ := r.exprsOrNil()
|
|
|
|
|
if left != nil {
|
|
|
|
|
left = newname(left.Sym)
|
|
|
|
|
}
|
|
|
|
|
return nodl(pos, op, left, nil)
|
|
|
|
|
|
|
|
|
|
// case OEMPTY:
|
|
|
|
|
// unreachable - not emitted by exporter
|
|
|
|
|
|
|
|
|
|
case OGOTO, OLABEL:
|
2018-10-26 20:10:23 -07:00
|
|
|
n := nodl(r.pos(), op, nil, nil)
|
|
|
|
|
n.Sym = lookup(r.string())
|
|
|
|
|
return n
|
2018-04-01 01:55:55 -07:00
|
|
|
|
|
|
|
|
case OEND:
|
|
|
|
|
return nil
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
Fatalf("cannot import %v (%d) node\n"+
|
2018-12-17 14:42:29 -08:00
|
|
|
"\t==> please file an issue and assign to gri@", op, int(op))
|
2018-04-01 01:55:55 -07:00
|
|
|
panic("unreachable") // satisfy compiler
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *importReader) op() Op {
|
|
|
|
|
return Op(r.uint64())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *importReader) elemList() []*Node {
|
|
|
|
|
c := r.uint64()
|
|
|
|
|
list := make([]*Node, c)
|
|
|
|
|
for i := range list {
|
|
|
|
|
s := r.ident()
|
|
|
|
|
list[i] = nodSym(OSTRUCTKEY, r.expr(), s)
|
|
|
|
|
}
|
|
|
|
|
return list
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *importReader) exprsOrNil() (a, b *Node) {
|
|
|
|
|
ab := r.uint64()
|
|
|
|
|
if ab&1 != 0 {
|
|
|
|
|
a = r.expr()
|
|
|
|
|
}
|
|
|
|
|
if ab&2 != 0 {
|
2018-06-18 14:38:45 -07:00
|
|
|
b = r.node()
|
2018-04-01 01:55:55 -07:00
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|