[dev.ssa] Merge remote-tracking branch 'origin/master' into mergebranch

Semi-regular merge from tip to dev.ssa.

Conflicts:
	src/runtime/sys_windows_amd64.s

Change-Id: I5f733130049c810e6ceacd46dad85faebca52b29
This commit is contained in:
Keith Randall 2016-01-19 09:59:21 -08:00
commit 23d5810c8f
218 changed files with 4017 additions and 811 deletions

View file

@ -70,6 +70,7 @@ Andrew Williams <williams.andrew@gmail.com>
Andrey Mirtchovski <mirtchovski@gmail.com>
Andrey Petrov <andrey.petrov@shazow.net>
Andriy Lytvynov <lytvynov.a.v@gmail.com>
Andy Balholm <andy@balholm.com>
Andy Davis <andy@bigandian.com>
Andy Maloney <asmaloney@gmail.com>
Anfernee Yongkun Gui <anfernee.gui@gmail.com>
@ -142,6 +143,7 @@ Christopher Guiney <chris@guiney.net>
Christopher Nielsen <m4dh4tt3r@gmail.com>
Christopher Redden <christopher.redden@gmail.com>
Christopher Wedgwood <cw@f00f.org>
CL Sung <clsung@gmail.com> <cl_sung@htc.com>
Clement Skau <clementskau@gmail.com>
CloudFlare Inc.
Colin Kennedy <moshen.colin@gmail.com>
@ -187,6 +189,7 @@ Devon H. O'Dell <devon.odell@gmail.com>
Dhiru Kholia <dhiru.kholia@gmail.com>
Didier Spezia <didier.06@gmail.com>
Dimitri Tcaciuc <dtcaciuc@gmail.com>
Dirk Gadsden <dirk@esherido.com>
Dmitri Shuralyov <shurcooL@gmail.com>
Dmitriy Shelenin <deemok@googlemail.com> <deemok@gmail.com>
Dmitry Chestnykh <dchest@gmail.com>
@ -526,6 +529,7 @@ Pieter Droogendijk <pieter@binky.org.uk>
Pietro Gagliardi <pietro10@mac.com>
Preetam Jinka <pj@preet.am>
Quan Yong Zhai <qyzhai@gmail.com>
Quentin Perez <qperez@ocs.online.net>
Quoc-Viet Nguyen <afelion@gmail.com>
RackTop Systems Inc.
Raif S. Naffah <go@naffah-raif.name>
@ -644,6 +648,7 @@ William Josephson <wjosephson@gmail.com>
William Orr <will@worrbase.com> <ay1244@gmail.com>
Xia Bin <snyh@snyh.org>
Xing Xing <mikespook@gmail.com>
Yahoo Inc.
Yann Kerhervé <yann.kerherve@gmail.com>
Yao Zhang <lunaria21@gmail.com>
Yasuharu Goto <matope.ono@gmail.com>

View file

@ -103,6 +103,7 @@ Andrew Williams <williams.andrew@gmail.com>
Andrey Mirtchovski <mirtchovski@gmail.com>
Andrey Petrov <andrey.petrov@shazow.net>
Andriy Lytvynov <lytvynov.a.v@gmail.com>
Andy Balholm <andy@balholm.com>
Andy Davis <andy@bigandian.com>
Andy Maloney <asmaloney@gmail.com>
Anfernee Yongkun Gui <anfernee.gui@gmail.com>
@ -203,8 +204,10 @@ Christopher Nielsen <m4dh4tt3r@gmail.com>
Christopher Redden <christopher.redden@gmail.com>
Christopher Swenson <cswenson@google.com>
Christopher Wedgwood <cw@f00f.org>
CL Sung <clsung@gmail.com> <cl_sung@htc.com>
Clement Skau <clementskau@gmail.com>
Colby Ranger <cranger@google.com>
Colin Cross <ccross@android.com>
Colin Kennedy <moshen.colin@gmail.com>
Conrad Meyer <cemeyer@cs.washington.edu>
Corey Thomasson <cthom.lists@gmail.com>
@ -260,11 +263,13 @@ Dean Prichard <dean.prichard@gmail.com>
Denis Bernard <db047h@gmail.com>
Denis Brandolini <denis.brandolini@gmail.com>
Derek Buitenhuis <derek.buitenhuis@gmail.com>
Derek Che <drc@yahoo-inc.com>
Derek Parker <parkerderek86@gmail.com>
Devon H. O'Dell <devon.odell@gmail.com>
Dhiru Kholia <dhiru.kholia@gmail.com>
Didier Spezia <didier.06@gmail.com>
Dimitri Tcaciuc <dtcaciuc@gmail.com>
Dirk Gadsden <dirk@esherido.com>
Dmitri Shuralyov <shurcooL@gmail.com>
Dmitriy Shelenin <deemok@googlemail.com> <deemok@gmail.com>
Dmitriy Vyukov <dvyukov@google.com>
@ -702,6 +707,7 @@ Pieter Droogendijk <pieter@binky.org.uk>
Pietro Gagliardi <pietro10@mac.com>
Preetam Jinka <pj@preet.am>
Quan Yong Zhai <qyzhai@gmail.com>
Quentin Perez <qperez@ocs.online.net>
Quoc-Viet Nguyen <afelion@gmail.com>
Rahul Chaudhry <rahulchaudhry@chromium.org>
Raif S. Naffah <go@naffah-raif.name>

View file

@ -178,13 +178,18 @@ pkg encoding/asn1, const TagUTCTime = 23
pkg encoding/asn1, const TagUTCTime ideal-int
pkg encoding/asn1, const TagUTF8String = 12
pkg encoding/asn1, const TagUTF8String ideal-int
pkg go/build, const AllowVendor = 8
pkg go/build, const AllowVendor ImportMode
pkg go/build, const IgnoreVendor = 8
pkg go/build, const IgnoreVendor ImportMode
pkg go/build, type Package struct, InvalidGoFiles []string
pkg go/constant, func ToComplex(Value) Value
pkg go/constant, func ToFloat(Value) Value
pkg go/constant, func ToInt(Value) Value
pkg go/constant, type Value interface, ExactString() string
pkg go/types, method (*Package) SetName(string)
pkg go/types, type ImportMode int
pkg go/types, type ImporterFrom interface { Import, ImportFrom }
pkg go/types, type ImporterFrom interface, Import(string) (*Package, error)
pkg go/types, type ImporterFrom interface, ImportFrom(string, string, ImportMode) (*Package, error)
pkg html/template, func IsTrue(interface{}) (bool, bool)
pkg html/template, method (*Template) DefinedTemplates() string
pkg image, func NewNYCbCrA(Rectangle, YCbCrSubsampleRatio) *NYCbCrA
@ -241,6 +246,8 @@ pkg net/http, const StatusRequestHeaderFieldsTooLarge = 431
pkg net/http, const StatusRequestHeaderFieldsTooLarge ideal-int
pkg net/http, const StatusTooManyRequests = 429
pkg net/http, const StatusTooManyRequests ideal-int
pkg net/http, const StatusUnavailableForLegalReasons = 451
pkg net/http, const StatusUnavailableForLegalReasons ideal-int
pkg net/http, type Transport struct, ExpectContinueTimeout time.Duration
pkg net/http, type Transport struct, TLSNextProto map[string]func(string, *tls.Conn) RoundTripper
pkg net/http, var ErrSkipAltProtocol error

View file

@ -510,6 +510,13 @@ the stack pointer may change during any function call:
even pointers to stack data must not be kept in local variables.
</p>
<p>
Assembly functions should always be given Go prototypes,
both to provide pointer information for the arguments and results
and to let <code>go</code> <code>vet</code> check that
the offsets being used to access them are correct.
</p>
<h2 id="architectures">Architecture-specific details</h2>
<p>

View file

@ -24,21 +24,31 @@ A similar explanation is available as a
<h2 id="Organization">Code organization</h2>
<h3 id="Overview">Overview</h3>
<ul>
<li>Go programmers typically keep all their Go code in a single <i>workspace</i>.</li>
<li>A workspace contains many version control <i>repositories</i>
(managed by Git, for example).</li>
<li>Each repository contains one or more <i>packages</i>.</li>
<li>Each package consists of one or more Go source files in a single directory.</li>
<li>The path to a package's directory determines its <i>import path</i>.</li>
</ul>
<p>
Note that this differs from other programming environments in which every
project has a separate workspace and workspaces are closely tied to version
control repositories.
</p>
<h3 id="Workspaces">Workspaces</h3>
<p>
The <code>go</code> tool is designed to work with open source code maintained
in public repositories. Although you don't need to publish your code, the model
for how the environment is set up works the same whether you do or not.
</p>
<p>
Go code must be kept inside a <i>workspace</i>.
A workspace is a directory hierarchy with three directories at its root:
</p>
<ul>
<li><code>src</code> contains Go source files organized into packages (one package per directory),
<li><code>src</code> contains Go source files,
<li><code>pkg</code> contains package objects, and
<li><code>bin</code> contains executable commands.
</ul>
@ -77,16 +87,25 @@ src/
stringutil/
reverse.go # package source
reverse_test.go # test source
<a href="https://golang.org/x/image/">golang.org/x/image/</a>
.git/ # Git repository metadata
bmp/
reader.go # package source
writer.go # package source
... (many more repositories and packages omitted) ...
</pre>
<p>
This workspace contains one repository (<code>example</code>)
comprising two commands (<code>hello</code> and <code>outyet</code>)
and one library (<code>stringutil</code>).
The tree above shows a workspace containing two repositories
(<code>example</code> and <code>image</code>).
The <code>example</code> repository contains two commands (<code>hello</code>
and <code>outyet</code>) and one library (<code>stringutil</code>).
The <code>image</code> repository contains the <code>bmp</code> package
and <a href="https://godoc.org/golang.org/x/image">several others</a>.
</p>
<p>
A typical workspace would contain many source repositories containing many
A typical workspace contains many source repositories containing many
packages and commands. Most Go programmers keep <i>all</i> their Go source code
and dependencies in a single workspace.
</p>
@ -133,10 +152,16 @@ please see
<a href="/cmd/go/#hdr-GOPATH_environment_variable"><code>go help gopath</code></a>
</p>
<h3 id="PackagePaths">Package paths</h3>
<h3 id="ImportPaths">Import paths</h3>
<p>
The packages from the standard library are given short paths such as
An <i>import path</i> is a string that uniquely identifies a package.
A package's import path corresponds to its location inside a workspace
or in a remote repository (explained below).
</p>
<p>
The packages from the standard library are given short import paths such as
<code>"fmt"</code> and <code>"net/http"</code>.
For your own packages, you must choose a base path that is unlikely to
collide with future additions to the standard library or other external

View file

@ -55,6 +55,13 @@ See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.5.2">Go
1.5.2 milestone</a> on our issue tracker for details.
</p>
<p>
go1.5.3 (released 2016/01/13) includes a security fix to the <code>math/big</code> package
affecting the <code>crypto/tls</code> package.
See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.5.3">Go 1.5.3 milestone on our issue tracker</a>
and the <a href="https://golang.org/s/go153announce">release announcement</a> for details.
</p>
<h2 id="go1.4">go1.4 (released 2014/12/10)</h2>
<p>

View file

@ -471,6 +471,16 @@ and <code>"page"</code> blocks to reuse the formatting for another page.
<ul>
<li>
The <a href="/pkg/archive/tar/"><code>archive/tar</code></a> package's
implementation corrects many bugs in rare corner cases of the file format.
One visible change is that the
<a href="/pkg/archive/tar/#Reader"><code>Reader</code></a> type's
<a href="/pkg/archive/tar/#Reader.Read"><code>Read</code></a> method
now presents the content of special file types as being empty,
returning <code>io.EOF</code> immediately.
</li>
<li>
In the <a href="/pkg/archive/zip/"><code>archive/zip</code></a> package, the
<a href="/pkg/archive/zip/#Reader"><code>Reader</code></a> type now has a
@ -514,6 +524,12 @@ now report
<a href="/pkg/io/#EOF"><code>io.EOF</code></a>.
</li>
<li>
The <a href="/pkg/crypto/cipher/"><code>crypto/cipher</code></a> package now
overwrites the destination buffer in the event of a GCM decryption failure.
This is to allow the AESNI code to avoid using a temporary buffer.
</li>
<li>
The <a href="/pkg/crypto/tls/"><code>crypto/tls</code></a> package
has a variety of minor changes.
@ -568,6 +584,17 @@ Also in the <a href="/pkg/encoding/asn1/"><code>encoding/asn1</code></a> package
<a href="/pkg/encoding/asn1/#Unmarshal"><code>Unmarshal</code></a> now rejects various non-standard integer and length encodings.
</li>
<li>
The <a href="/pkg/encoding/base64"><code>encoding/base64</code></a> package's
<a href="/pkg/encoding/base64/#Decoder"><code>Decoder</code></a> has been fixed
to process the final bytes of its input. Previously it processed as many four-byte tokens as
possible but ignore the remainder, up to three bytes.
The <code>Decoder</code> therefore now handles inputs in unpadded encodings (like
<a href="/pkg/encoding/base64/#RawURLEncoding">RawURLEncoding</a>) correctly,
but it also rejects inputs in padded encodings that are truncated or end with invalid bytes,
such as trailing spaces.
</li>
<li>
The <a href="/pkg/encoding/json/"><code>encoding/json</code></a> package
now checks the syntax of a

View file

@ -1,46 +1,62 @@
<!--{
"Title": "Getting Help",
"Title": "Help",
"Path": "/help/"
}-->
<img class="gopher" src="/doc/gopher/help.png"/>
<p>
Need help with Go? Try these resources.
</p>
<div id="manual-nav"></div>
<h3 id="faq"><a href="/doc/faq">Frequently Asked Questions (FAQ)</a></h3>
<p>Answers to common questions about Go.</p>
<h2 id="help">Get help</h2>
<h3 id="playground"><a href="/play">The Go Playground</a></h3>
<p>A place to write, run, and share Go code.</p>
<img class="gopher" src="/doc/gopher/help.png"/>
<h3 id="wiki"><a href="/wiki">The Go Wiki</a></h3>
<p>A wiki maintained by the Go community.</p>
<h3 id="mailinglist"><a href="//groups.google.com/group/golang-nuts">Go Nuts Mailing List</a></h3>
<h3 id="mailinglist"><a href="https://groups.google.com/group/golang-nuts">Go Nuts Mailing List</a></h3>
<p>
Search the <a href="//groups.google.com/group/golang-nuts">golang-nuts</a>
Search the <a href="https://groups.google.com/group/golang-nuts">golang-nuts</a>
archives and consult the <a href="/doc/go_faq.html">FAQ</a> and
<a href="//golang.org/wiki">wiki</a> before posting.
</p>
<h3 id="forum"><a href="https://forum.golangbridge.org/">Go Forum</a></h3>
<p>
The <a href="https://forum.golangbridge.org/">Go Forum</a> is an alternate discussion
forum for Go programmers.
</p>
<h3 id="slack"><a href="https://blog.gopheracademy.com/gophers-slack-community/">Gopher Slack</a></h3>
<p>Get live support from the official Go slack channel.</p>
<h3 id="irc"><a href="irc:irc.freenode.net/go-nuts">Go IRC Channel</a></h3>
<p>Get live support at <b>#go-nuts</b> on <b>irc.freenode.net</b>, the official
Go IRC channel.</p>
<h3 id="pluscom"><a href="https://plus.google.com/communities/114112804251407510571">The Go+ community</a></h3>
<p>The Google+ community for Go enthusiasts.</p>
<h3 id="faq"><a href="/doc/faq">Frequently Asked Questions (FAQ)</a></h3>
<p>Answers to common questions about Go.</p>
<h3 id="plus"><a href="https://plus.google.com/101406623878176903605/posts">The Go Programming Language at Google+</a></h3>
<p>The Go project's Google+ page.</p>
<h2 id="inform">Stay informed</h2>
<h3 id="twitter"><a href="//twitter.com/golang">@golang at Twitter</a></h3>
<h3 id="announce"><a href="https://groups.google.com/group/golang-announce">Go Announcements Mailing List</a></h3>
<p>
Subscribe to
<a href="https://groups.google.com/group/golang-announce">golang-announce</a>
for important announcements, such as the availability of new Go releases.
</p>
<h3 id="blog"><a href="//blog.golang.org">Go Blog</a></h3>
<p>The Go project's official blog.</p>
<h3 id="twitter"><a href="https://twitter.com/golang">@golang at Twitter</a></h3>
<p>The Go project's official Twitter account.</p>
<p>Tweeting about your problem with the <code>#golang</code> hashtag usually
generates some helpful responses.</p>
<h3 id="pluscom"><a href="https://plus.google.com/communities/114112804251407510571">Go+ community</a></h3>
<p>A Google+ community for Go enthusiasts.</p>
<h3 id="reddit"><a href="https://reddit.com/r/golang">golang sub-Reddit</a></h3>
<p>
The <a href="https://reddit.com/r/golang">golang sub-Reddit</a> is a place
for Go news and discussion.
</p>
<h2 id="community">Community resources</h2>
<h3 id="go_user_groups"><a href="/wiki/GoUserGroups">Go User Groups</a></h3>
<p>
@ -48,6 +64,12 @@ Each month in places around the world, groups of Go programmers ("gophers")
meet to talk about Go. Find a chapter near you.
</p>
<h3 id="playground"><a href="/play">Go Playground</a></h3>
<p>A place to write, run, and share Go code.</p>
<h3 id="wiki"><a href="/wiki">Go Wiki</a></h3>
<p>A wiki maintained by the Go community.</p>
<h3 id="conduct"><a href="/conduct">Code of Conduct</a></h3>
<p>
Guidelines for participating in Go community spaces

View file

@ -106,6 +106,16 @@ var ptrTests = []ptrTest{
body: `i := 0; p := &S{p:&i, s:[]unsafe.Pointer{nil}}; C.f(&p.s[0])`,
fail: false,
},
{
// Passing the address of a slice of an array that is
// an element in a struct, with a type conversion.
name: "slice-ok-3",
c: `void f(void* p) {}`,
imports: []string{"unsafe"},
support: `type S struct { p *int; a [4]byte }`,
body: `i := 0; p := &S{p:&i}; s := p.a[:]; C.f(unsafe.Pointer(&s[0]))`,
fail: false,
},
{
// Passing the address of a static variable with no
// pointers doesn't matter.

View file

@ -110,13 +110,13 @@ func test7978(t *testing.T) {
go issue7978go()
// test in c code, before callback
issue7978wait(0, 1)
issue7978check(t, "runtime.cgocall(", "", 1)
issue7978check(t, "_Cfunc_issue7978c(", "", 1)
// test in go code, during callback
issue7978wait(2, 3)
issue7978check(t, "test.issue7978cb(", "test.issue7978go", 3)
// test in c code, after callback
issue7978wait(4, 5)
issue7978check(t, "runtime.cgocall(", "runtime.cgocallback", 1)
issue7978check(t, "_Cfunc_issue7978c(", "_cgoexpwrap", 1)
// test in go code, after return from cgo
issue7978wait(6, 7)
issue7978check(t, "test.issue7978go(", "", 3)

View file

@ -2,15 +2,44 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "p.h"
#include "libgo.h"
static void (*oldHandler)(int, siginfo_t*, void*);
static void handler(int signo, siginfo_t* info, void* ctxt) {
if (oldHandler) {
oldHandler(signo, info, ctxt);
}
}
int main(void) {
struct sigaction sa;
struct sigaction osa;
int32_t res;
// Install our own signal handler.
memset(&sa, 0, sizeof sa);
sa.sa_sigaction = handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_ONSTACK | SA_SIGINFO;
memset(&osa, 0, sizeof osa);
sigemptyset(&osa.sa_mask);
if (sigaction(SIGSEGV, &sa, &osa) < 0) {
perror("sigaction");
return 2;
}
if (osa.sa_handler == SIG_DFL || (osa.sa_flags&SA_ONSTACK) == 0) {
fprintf(stderr, "Go runtime did not install signal handler\n");
return 2;
}
oldHandler = osa.sa_sigaction;
if (!DidInitRun()) {
fprintf(stderr, "ERROR: buildmode=c-archive init should run\n");
return 2;
@ -21,6 +50,16 @@ int main(void) {
return 2;
}
// Make sure our signal handler is still the one in use.
if (sigaction(SIGSEGV, NULL, &sa) < 0) {
perror("sigaction check");
return 2;
}
if (sa.sa_sigaction != handler) {
fprintf(stderr, "ERROR: wrong signal handler: %p != %p\n", sa.sa_sigaction, handler);
return 2;
}
res = FromPkg();
if (res != 1024) {
fprintf(stderr, "ERROR: FromPkg()=%d, want 1024\n", res);

View file

@ -27,7 +27,7 @@ fi
# Directory where cgo headers and outputs will be installed.
# The installation directory format varies depending on the platform.
installdir=pkg/${goos}_${goarch}_testcshared_shared
if [ "${goos}/${goarch}" == "darwin/amd64" ]; then
if [ "${goos}" == "darwin" ]; then
installdir=pkg/${goos}_${goarch}_testcshared
fi

View file

@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
# Copyright 2015 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.

View file

@ -33,7 +33,7 @@ if [ "$pattern" = "" ]; then
fi
# put linux, nacl first in the target list to get all the architectures up front.
targets="$((ls runtime | sed -n 's/^rt0_\(.*\)_\(.*\)\.s/\1-\2/p'; echo linux-386-387 linux-arm-arm5) | sort | egrep -v android-arm | egrep "$pattern" | egrep 'linux|nacl')
targets="$((ls runtime | sed -n 's/^rt0_\(.*\)_\(.*\)\.s/\1-\2/p'; echo linux-386-387 linux-arm-arm5) | sort | sed -e 's|linux-mips64x|linux-mips64 linux-mips64le|' | egrep -v android-arm | egrep "$pattern" | egrep 'linux|nacl')
$(ls runtime | sed -n 's/^rt0_\(.*\)_\(.*\)\.s/\1-\2/p' | egrep -v 'android-arm|darwin-arm' | egrep "$pattern" | egrep -v 'linux|nacl')"
./make.bash || exit 1

View file

@ -125,11 +125,19 @@ The C types __int128_t and __uint128_t are represented by [16]byte.
To access a struct, union, or enum type directly, prefix it with
struct_, union_, or enum_, as in C.struct_stat.
The size of any C type T is available as C.sizeof_T, as in
C.sizeof_struct_stat.
As Go doesn't have support for C's union type in the general case,
C's union types are represented as a Go byte array with the same length.
Go structs cannot embed fields with C types.
Go code can not refer to zero-sized fields that occur at the end of
non-empty C structs. To get the address of such a field (which is the
only operation you can do with a zero-sized field) you must take the
address of the struct and add the size of the struct.
Cgo translates C types into equivalent unexported Go types.
Because the translations are unexported, a Go package should not
expose C types in its exported API: a C type used in one Go package

View file

@ -626,9 +626,7 @@ func (p *Package) rewriteCall(f *File, call *ast.CallExpr, name *Name) {
// Add optional additional arguments for an address
// expression.
if u, ok := call.Args[i].(*ast.UnaryExpr); ok && u.Op == token.AND {
c.Args = p.checkAddrArgs(f, c.Args, u.X)
}
c.Args = p.checkAddrArgs(f, c.Args, call.Args[i])
// _cgoCheckPointer returns interface{}.
// We need to type assert that to the type we want.
@ -773,7 +771,19 @@ func (p *Package) hasPointer(f *File, t ast.Expr, top bool) bool {
// only pass the slice or array if we can refer to it without side
// effects.
func (p *Package) checkAddrArgs(f *File, args []ast.Expr, x ast.Expr) []ast.Expr {
index, ok := x.(*ast.IndexExpr)
// Strip type conversions.
for {
c, ok := x.(*ast.CallExpr)
if !ok || len(c.Args) != 1 || !p.isType(c.Fun) {
break
}
x = c.Args[0]
}
u, ok := x.(*ast.UnaryExpr)
if !ok || u.Op != token.AND {
return args
}
index, ok := u.X.(*ast.IndexExpr)
if !ok {
// This is the address of something that is not an
// index expression. We only need to examine the
@ -804,6 +814,42 @@ func (p *Package) hasSideEffects(f *File, x ast.Expr) bool {
return found
}
// isType returns whether the expression is definitely a type.
// This is conservative--it returns false for an unknown identifier.
func (p *Package) isType(t ast.Expr) bool {
switch t := t.(type) {
case *ast.SelectorExpr:
if t.Sel.Name != "Pointer" {
return false
}
id, ok := t.X.(*ast.Ident)
if !ok {
return false
}
return id.Name == "unsafe"
case *ast.Ident:
// TODO: This ignores shadowing.
switch t.Name {
case "unsafe.Pointer", "bool", "byte",
"complex64", "complex128",
"error",
"float32", "float64",
"int", "int8", "int16", "int32", "int64",
"rune", "string",
"uint", "uint8", "uint16", "uint32", "uint64", "uintptr":
return true
}
case *ast.StarExpr:
return p.isType(t.X)
case *ast.ArrayType, *ast.StructType, *ast.FuncType, *ast.InterfaceType,
*ast.MapType, *ast.ChanType:
return true
}
return false
}
// unsafeCheckPointerName is given the Go version of a C type. If the
// type uses unsafe.Pointer, we arrange to build a version of
// _cgoCheckPointer that returns that type. This avoids using a type
@ -832,6 +878,8 @@ func (p *Package) unsafeCheckPointerName(t ast.Expr) string {
func (p *Package) hasUnsafePointer(t ast.Expr) bool {
switch t := t.(type) {
case *ast.Ident:
// We don't see a SelectorExpr for unsafe.Pointer;
// this is created by code in this file.
return t.Name == "unsafe.Pointer"
case *ast.ArrayType:
return p.hasUnsafePointer(t.Elt)

View file

@ -34,6 +34,7 @@ var progtable = [x86.ALAST]obj.ProgInfo{
obj.ACHECKNIL: {Flags: gc.LeftRead},
obj.AVARDEF: {Flags: gc.Pseudo | gc.RightWrite},
obj.AVARKILL: {Flags: gc.Pseudo | gc.RightWrite},
obj.AVARLIVE: {Flags: gc.Pseudo | gc.LeftRead},
// NOP is an internal no-op that also stands
// for USED and SET annotations, not the Intel opcode.

View file

@ -1366,6 +1366,7 @@ func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) int {
obj.AFUNCDATA,
obj.AVARDEF,
obj.AVARKILL,
obj.AVARLIVE,
obj.AUSEFIELD:
return 0
}

View file

@ -33,6 +33,7 @@ var progtable = [arm.ALAST]obj.ProgInfo{
obj.ACHECKNIL: {Flags: gc.LeftRead},
obj.AVARDEF: {Flags: gc.Pseudo | gc.RightWrite},
obj.AVARKILL: {Flags: gc.Pseudo | gc.RightWrite},
obj.AVARLIVE: {Flags: gc.Pseudo | gc.LeftRead},
// NOP is an internal no-op that also stands
// for USED and SET annotations, not the Intel opcode.

View file

@ -711,6 +711,7 @@ func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) int {
obj.AFUNCDATA,
obj.AVARDEF,
obj.AVARKILL,
obj.AVARLIVE,
obj.AUSEFIELD:
return 0
}

View file

@ -34,6 +34,7 @@ var progtable = [arm64.ALAST]obj.ProgInfo{
obj.ACHECKNIL: {Flags: gc.LeftRead},
obj.AVARDEF: {Flags: gc.Pseudo | gc.RightWrite},
obj.AVARKILL: {Flags: gc.Pseudo | gc.RightWrite},
obj.AVARLIVE: {Flags: gc.Pseudo | gc.LeftRead},
// NOP is an internal no-op that also stands
// for USED and SET annotations, not the Power opcode.

View file

@ -634,6 +634,7 @@ func evconst(n *Node) {
var wr int
var v Val
var norig *Node
var nn *Node
if nr == nil {
// copy numeric value to avoid modifying
// nl, in case someone still refers to it (e.g. iota).
@ -1115,15 +1116,21 @@ ret:
return
settrue:
norig = saveorig(n)
*n = *Nodbool(true)
n.Orig = norig
nn = Nodbool(true)
nn.Orig = saveorig(n)
if !iscmp[n.Op] {
nn.Type = nl.Type
}
*n = *nn
return
setfalse:
norig = saveorig(n)
*n = *Nodbool(false)
n.Orig = norig
nn = Nodbool(false)
nn.Orig = saveorig(n)
if !iscmp[n.Op] {
nn.Type = nl.Type
}
*n = *nn
return
illegal:

View file

@ -305,6 +305,7 @@ type NodeEscState struct {
Escloopdepth int32 // -1: global, 0: return variables, 1:function top level, increased inside function for every loop or label to mark scopes
Esclevel Level
Walkgen uint32
Maxextraloopdepth int32
}
func (e *EscState) nodeEscState(n *Node) *NodeEscState {
@ -1579,7 +1580,13 @@ func funcOutputAndInput(dst, src *Node) bool {
src.Op == ONAME && src.Class == PPARAM && src.Name.Curfn == dst.Name.Curfn
}
const NOTALOOPDEPTH = -1
func escwalk(e *EscState, level Level, dst *Node, src *Node) {
escwalkBody(e, level, dst, src, NOTALOOPDEPTH)
}
func escwalkBody(e *EscState, level Level, dst *Node, src *Node, extraloopdepth int32) {
if src.Op == OLITERAL {
return
}
@ -1590,16 +1597,29 @@ func escwalk(e *EscState, level Level, dst *Node, src *Node) {
// convergence.
level = level.min(srcE.Esclevel)
if level == srcE.Esclevel {
// Have we been here already with an extraloopdepth,
// or is the extraloopdepth provided no improvement on
// what's already been seen?
if srcE.Maxextraloopdepth >= extraloopdepth || srcE.Escloopdepth >= extraloopdepth {
return
}
srcE.Maxextraloopdepth = extraloopdepth
}
} else { // srcE.Walkgen < e.walkgen -- first time, reset this.
srcE.Maxextraloopdepth = NOTALOOPDEPTH
}
srcE.Walkgen = e.walkgen
srcE.Esclevel = level
modSrcLoopdepth := srcE.Escloopdepth
if extraloopdepth > modSrcLoopdepth {
modSrcLoopdepth = extraloopdepth
}
if Debug['m'] > 1 {
fmt.Printf("escwalk: level:%d depth:%d %.*s op=%v %v(%v) scope:%v[%d]\n",
level, e.pdepth, e.pdepth, "\t\t\t\t\t\t\t\t\t\t", Oconv(int(src.Op), 0), Nconv(src, obj.FmtShort), Jconv(src, obj.FmtShort), e.curfnSym(src), srcE.Escloopdepth)
fmt.Printf("escwalk: level:%d depth:%d %.*s op=%v %v(%v) scope:%v[%d] extraloopdepth=%v\n",
level, e.pdepth, e.pdepth, "\t\t\t\t\t\t\t\t\t\t", Oconv(int(src.Op), 0), Nconv(src, obj.FmtShort), Jconv(src, obj.FmtShort), e.curfnSym(src), srcE.Escloopdepth, extraloopdepth)
}
e.pdepth++
@ -1638,7 +1658,7 @@ func escwalk(e *EscState, level Level, dst *Node, src *Node) {
}
}
leaks = level.int() <= 0 && level.guaranteedDereference() <= 0 && dstE.Escloopdepth < srcE.Escloopdepth
leaks = level.int() <= 0 && level.guaranteedDereference() <= 0 && dstE.Escloopdepth < modSrcLoopdepth
switch src.Op {
case ONAME:
@ -1650,7 +1670,7 @@ func escwalk(e *EscState, level Level, dst *Node, src *Node) {
Warnl(int(src.Lineno), "leaking param content: %v", Nconv(src, obj.FmtShort))
} else {
Warnl(int(src.Lineno), "leaking param content: %v level=%v dst.eld=%v src.eld=%v dst=%v",
Nconv(src, obj.FmtShort), level, dstE.Escloopdepth, srcE.Escloopdepth, Nconv(dst, obj.FmtShort))
Nconv(src, obj.FmtShort), level, dstE.Escloopdepth, modSrcLoopdepth, Nconv(dst, obj.FmtShort))
}
}
} else {
@ -1660,7 +1680,7 @@ func escwalk(e *EscState, level Level, dst *Node, src *Node) {
Warnl(int(src.Lineno), "leaking param: %v", Nconv(src, obj.FmtShort))
} else {
Warnl(int(src.Lineno), "leaking param: %v level=%v dst.eld=%v src.eld=%v dst=%v",
Nconv(src, obj.FmtShort), level, dstE.Escloopdepth, srcE.Escloopdepth, Nconv(dst, obj.FmtShort))
Nconv(src, obj.FmtShort), level, dstE.Escloopdepth, modSrcLoopdepth, Nconv(dst, obj.FmtShort))
}
}
}
@ -1686,14 +1706,16 @@ func escwalk(e *EscState, level Level, dst *Node, src *Node) {
}
if Debug['m'] > 1 {
Warnl(int(src.Lineno), "%v escapes to heap, level=%v, dst.eld=%v, src.eld=%v",
Nconv(p, obj.FmtShort), level, dstE.Escloopdepth, srcE.Escloopdepth)
Nconv(p, obj.FmtShort), level, dstE.Escloopdepth, modSrcLoopdepth)
} else {
Warnl(int(src.Lineno), "%v escapes to heap", Nconv(p, obj.FmtShort))
}
}
}
escwalkBody(e, level.dec(), dst, src.Left, modSrcLoopdepth)
extraloopdepth = modSrcLoopdepth // passes to recursive case, seems likely a no-op
} else {
escwalk(e, level.dec(), dst, src.Left)
}
case OAPPEND:
escwalk(e, level, dst, src.List.N)
@ -1704,6 +1726,7 @@ func escwalk(e *EscState, level Level, dst *Node, src *Node) {
if Debug['m'] != 0 {
Warnl(int(src.Lineno), "%v escapes to heap", Nconv(src, obj.FmtShort))
}
extraloopdepth = modSrcLoopdepth
}
// similar to a slice arraylit and its args.
level = level.dec()
@ -1737,6 +1760,7 @@ func escwalk(e *EscState, level Level, dst *Node, src *Node) {
if Debug['m'] != 0 {
Warnl(int(src.Lineno), "%v escapes to heap", Nconv(src, obj.FmtShort))
}
extraloopdepth = modSrcLoopdepth
}
case ODOT,
@ -1778,12 +1802,19 @@ func escwalk(e *EscState, level Level, dst *Node, src *Node) {
recurse:
level = level.copy()
for ll := srcE.Escflowsrc; ll != nil; ll = ll.Next {
escwalk(e, level, dst, ll.N)
escwalkBody(e, level, dst, ll.N, extraloopdepth)
}
e.pdepth--
}
// This special tag is applied to uintptr variables
// that we believe may hold unsafe.Pointers for
// calls into assembly functions.
// It is logically a constant, but using a var
// lets us take the address below to get a *string.
var unsafeUintptrTag = "unsafe-uintptr"
func esctag(e *EscState, func_ *Node) {
func_.Esc = EscFuncTagged
@ -1798,6 +1829,29 @@ func esctag(e *EscState, func_ *Node) {
}
}
// Assume that uintptr arguments must be held live across the call.
// This is most important for syscall.Syscall.
// See golang.org/issue/13372.
// This really doesn't have much to do with escape analysis per se,
// but we are reusing the ability to annotate an individual function
// argument and pass those annotations along to importing code.
narg := 0
for t := getinargx(func_.Type).Type; t != nil; t = t.Down {
narg++
if t.Type.Etype == TUINTPTR {
if Debug['m'] != 0 {
var name string
if t.Sym != nil {
name = t.Sym.Name
} else {
name = fmt.Sprintf("arg#%d", narg)
}
Warnl(int(func_.Lineno), "%v assuming %v is unsafe uintptr", funcSym(func_), name)
}
t.Note = &unsafeUintptrTag
}
}
return
}

View file

@ -0,0 +1,102 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gc
import "testing"
// For GO386=387, make sure fucomi* opcodes are not used
// for comparison operations.
// Note that this test will fail only on a Pentium MMX
// processor (with GOARCH=386 GO386=387), as it just runs
// some code and looks for an unimplemented instruction fault.
//go:noinline
func compare1(a, b float64) bool {
return a < b
}
//go:noinline
func compare2(a, b float32) bool {
return a < b
}
func TestFloatCompare(t *testing.T) {
if !compare1(3, 5) {
t.Errorf("compare1 returned false")
}
if !compare2(3, 5) {
t.Errorf("compare2 returned false")
}
}
// For GO386=387, make sure fucomi* opcodes are not used
// for float->int conversions.
//go:noinline
func cvt1(a float64) uint64 {
return uint64(a)
}
//go:noinline
func cvt2(a float64) uint32 {
return uint32(a)
}
//go:noinline
func cvt3(a float32) uint64 {
return uint64(a)
}
//go:noinline
func cvt4(a float32) uint32 {
return uint32(a)
}
//go:noinline
func cvt5(a float64) int64 {
return int64(a)
}
//go:noinline
func cvt6(a float64) int32 {
return int32(a)
}
//go:noinline
func cvt7(a float32) int64 {
return int64(a)
}
//go:noinline
func cvt8(a float32) int32 {
return int32(a)
}
func TestFloatConvert(t *testing.T) {
if got := cvt1(3.5); got != 3 {
t.Errorf("cvt1 got %d, wanted 3", got)
}
if got := cvt2(3.5); got != 3 {
t.Errorf("cvt2 got %d, wanted 3", got)
}
if got := cvt3(3.5); got != 3 {
t.Errorf("cvt3 got %d, wanted 3", got)
}
if got := cvt4(3.5); got != 3 {
t.Errorf("cvt4 got %d, wanted 3", got)
}
if got := cvt5(3.5); got != 3 {
t.Errorf("cvt5 got %d, wanted 3", got)
}
if got := cvt6(3.5); got != 3 {
t.Errorf("cvt6 got %d, wanted 3", got)
}
if got := cvt7(3.5); got != 3 {
t.Errorf("cvt7 got %d, wanted 3", got)
}
if got := cvt8(3.5); got != 3 {
t.Errorf("cvt8 got %d, wanted 3", got)
}
}

View file

@ -607,6 +607,9 @@ func Tempname(nn *Node, t *Type) {
n.Esc = EscNever
n.Name.Curfn = Curfn
Curfn.Func.Dcl = list(Curfn.Func.Dcl, n)
if Debug['h'] != 0 {
println("H", n, n.Orig, funcSym(Curfn).Name)
}
dowidth(t)
n.Xoffset = 0
@ -870,6 +873,9 @@ func gen(n *Node) {
case OVARKILL:
gvarkill(n.Left)
case OVARLIVE:
gvarlive(n.Left)
}
ret:

View file

@ -185,7 +185,7 @@ func fixautoused(p *obj.Prog) {
continue
}
if (p.As == obj.AVARDEF || p.As == obj.AVARKILL) && p.To.Node != nil && !((p.To.Node).(*Node)).Used {
if (p.As == obj.AVARDEF || p.As == obj.AVARKILL || p.As == obj.AVARLIVE) && p.To.Node != nil && !((p.To.Node).(*Node)).Used {
// Cannot remove VARDEF instruction, because - unlike TYPE handled above -
// VARDEFs are interspersed with other code, and a jump might be using the
// VARDEF as a target. Replace with a no-op instead. A later pass will remove

View file

@ -694,7 +694,13 @@ func importfile(f *Val, line int) {
errorexit()
}
if f.U.(string) == "unsafe" {
path_ := f.U.(string)
if mapped, ok := importMap[path_]; ok {
path_ = mapped
}
if path_ == "unsafe" {
if safemode != 0 {
Yyerror("cannot import package unsafe")
errorexit()
@ -706,12 +712,6 @@ func importfile(f *Val, line int) {
return
}
path_ := f.U.(string)
if mapped, ok := importMap[path_]; ok {
path_ = mapped
}
if islocalname(path_) {
if path_[0] == '/' {
Yyerror("import path cannot be absolute path")

View file

@ -243,6 +243,13 @@ func cleantempnopop(mark *NodeList, order *Order, out **NodeList) {
var kill *Node
for l := order.temp; l != mark; l = l.Next {
if l.N.Name.Keepalive {
l.N.Name.Keepalive = false
l.N.Addrtaken = true // ensure SSA keeps the l.N variable
kill = Nod(OVARLIVE, l.N, nil)
typecheck(&kill, Etop)
*out = list(*out, kill)
}
kill = Nod(OVARKILL, l.N, nil)
typecheck(&kill, Etop)
*out = list(*out, kill)
@ -375,6 +382,28 @@ func ordercall(n *Node, order *Order) {
orderexpr(&n.Left, order, nil)
orderexpr(&n.Right, order, nil) // ODDDARG temp
ordercallargs(&n.List, order)
if n.Op == OCALLFUNC {
for l, t := n.List, getinargx(n.Left.Type).Type; l != nil && t != nil; l, t = l.Next, t.Down {
// Check for "unsafe-uintptr" tag provided by escape analysis.
// If present and the argument is really a pointer being converted
// to uintptr, arrange for the pointer to be kept alive until the call
// returns, by copying it into a temp and marking that temp
// still alive when we pop the temp stack.
if t.Note != nil && *t.Note == unsafeUintptrTag {
xp := &l.N
for (*xp).Op == OCONVNOP && !Isptr[(*xp).Type.Etype] {
xp = &(*xp).Left
}
x := *xp
if Isptr[x.Type.Etype] {
x = ordercopyexpr(x, x.Type, order, 0)
x.Name.Keepalive = true
*xp = x
}
}
}
}
}
// Ordermapassign appends n to order->out, introducing temporaries
@ -464,7 +493,7 @@ func orderstmt(n *Node, order *Order) {
default:
Fatalf("orderstmt %v", Oconv(int(n.Op), 0))
case OVARKILL:
case OVARKILL, OVARLIVE:
order.out = list(order.out, n)
case OAS:

View file

@ -95,9 +95,13 @@ func gvardefx(n *Node, as int) {
switch n.Class {
case PAUTO, PPARAM, PPARAMOUT:
if as == obj.AVARLIVE {
Thearch.Gins(as, n, nil)
} else {
Thearch.Gins(as, nil, n)
}
}
}
func Gvardef(n *Node) {
gvardefx(n, obj.AVARDEF)
@ -107,13 +111,17 @@ func gvarkill(n *Node) {
gvardefx(n, obj.AVARKILL)
}
func gvarlive(n *Node) {
gvardefx(n, obj.AVARLIVE)
}
func removevardef(firstp *obj.Prog) {
for p := firstp; p != nil; p = p.Link {
for p.Link != nil && (p.Link.As == obj.AVARDEF || p.Link.As == obj.AVARKILL) {
for p.Link != nil && (p.Link.As == obj.AVARDEF || p.Link.As == obj.AVARKILL || p.Link.As == obj.AVARLIVE) {
p.Link = p.Link.Link
}
if p.To.Type == obj.TYPE_BRANCH {
for p.To.Val.(*obj.Prog) != nil && (p.To.Val.(*obj.Prog).As == obj.AVARDEF || p.To.Val.(*obj.Prog).As == obj.AVARKILL) {
for p.To.Val.(*obj.Prog) != nil && (p.To.Val.(*obj.Prog).As == obj.AVARDEF || p.To.Val.(*obj.Prog).As == obj.AVARKILL || p.To.Val.(*obj.Prog).As == obj.AVARLIVE) {
p.To.Val = p.To.Val.(*obj.Prog).Link
}
}

View file

@ -809,7 +809,7 @@ func checkauto(fn *Node, p *obj.Prog, n *Node) {
return
}
fmt.Printf("checkauto %v: %v (%p; class=%d) not found in %v\n", Curfn, n, n, n.Class, p)
fmt.Printf("checkauto %v: %v (%p; class=%d) not found in %p %v\n", funcSym(Curfn), n, n, n.Class, p, p)
for l := fn.Func.Dcl; l != nil; l = l.Next {
fmt.Printf("\t%v (%p; class=%d)\n", l.N, l.N, l.N.Class)
}

View file

@ -143,7 +143,7 @@ func instrumentnode(np **Node, init **NodeList, wr int, skip int) {
goto ret
// can't matter
case OCFUNC, OVARKILL:
case OCFUNC, OVARKILL, OVARLIVE:
goto ret
case OBLOCK:

View file

@ -1073,6 +1073,9 @@ func regopt(firstp *obj.Prog) {
for f := firstf; f != nil; f = f.Link {
p := f.Prog
// AVARLIVE must be considered a use, do not skip it.
// Otherwise the variable will be optimized away,
// and the whole point of AVARLIVE is to keep it on the stack.
if p.As == obj.AVARDEF || p.As == obj.AVARKILL {
continue
}

View file

@ -849,6 +849,13 @@ func (s *state) stmt(n *Node) {
s.vars[&memVar] = s.newValue1A(ssa.OpVarKill, ssa.TypeMem, n.Left, s.mem())
}
case OVARLIVE:
// Insert a varlive op to record that a variable is still live.
if !n.Left.Addrtaken {
s.Fatalf("VARLIVE variable %s must have Addrtaken set", n.Left)
}
s.vars[&memVar] = s.newValue1A(ssa.OpVarLive, ssa.TypeMem, n.Left, s.mem())
case OCHECKNIL:
p := s.expr(n.Left)
s.nilCheck(p)
@ -4122,6 +4129,8 @@ func (s *genState) genValue(v *ssa.Value) {
Gvardef(v.Aux.(*Node))
case ssa.OpVarKill:
gvarkill(v.Aux.(*Node))
case ssa.OpVarLive:
gvarlive(v.Aux.(*Node))
case ssa.OpAMD64LoweredNilCheck:
// Optimization - if the subsequent block has a load or store
// at the same address, we don't need to issue this instruction.

View file

@ -128,6 +128,7 @@ type Name struct {
Captured bool // is the variable captured by a closure
Byval bool // is the variable captured by value or by reference
Needzero bool // if it contains pointers, needs to be zeroed on function entry
Keepalive bool // mark value live across unknown assembly call
}
type Param struct {
@ -342,6 +343,7 @@ const (
OCFUNC // reference to c function pointer (not go func value)
OCHECKNIL // emit code to ensure pointer/interface not nil
OVARKILL // variable is dead
OVARLIVE // variable is alive
// thearch-specific registers
OREGISTER // a register, such as AX.

View file

@ -687,8 +687,6 @@ OpSwitch:
n.Left = l
n.Right = r
}
} else if n.Op == OANDAND || n.Op == OOROR {
evconst(n)
}
if et == TSTRING {
@ -2025,7 +2023,8 @@ OpSwitch:
OEMPTY,
OGOTO,
OXFALL,
OVARKILL:
OVARKILL,
OVARLIVE:
ok |= Etop
break OpSwitch

View file

@ -216,7 +216,8 @@ func walkstmt(np **Node) {
ODCLCONST,
ODCLTYPE,
OCHECKNIL,
OVARKILL:
OVARKILL,
OVARLIVE:
break
case OBLOCK:

View file

@ -688,6 +688,7 @@ func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) int {
obj.AFUNCDATA,
obj.AVARDEF,
obj.AVARKILL,
obj.AVARLIVE,
obj.AUSEFIELD:
return 0
}

View file

@ -34,6 +34,7 @@ var progtable = [mips.ALAST]obj.ProgInfo{
obj.ACHECKNIL: {Flags: gc.LeftRead},
obj.AVARDEF: {Flags: gc.Pseudo | gc.RightWrite},
obj.AVARKILL: {Flags: gc.Pseudo | gc.RightWrite},
obj.AVARLIVE: {Flags: gc.Pseudo | gc.LeftRead},
// NOP is an internal no-op that also stands
// for USED and SET annotations, not the MIPS opcode.

View file

@ -953,6 +953,7 @@ func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) int {
obj.AFUNCDATA,
obj.AVARDEF,
obj.AVARKILL,
obj.AVARLIVE,
obj.AUSEFIELD:
return 0
}

View file

@ -34,6 +34,7 @@ var progtable = [ppc64.ALAST]obj.ProgInfo{
obj.ACHECKNIL: {Flags: gc.LeftRead},
obj.AVARDEF: {Flags: gc.Pseudo | gc.RightWrite},
obj.AVARKILL: {Flags: gc.Pseudo | gc.RightWrite},
obj.AVARLIVE: {Flags: gc.Pseudo | gc.LeftRead},
// NOP is an internal no-op that also stands
// for USED and SET annotations, not the Power opcode.

View file

@ -373,6 +373,7 @@ var genericOps = []opData{
{name: "VarDef", typ: "Mem"}, // aux is a *gc.Node of a variable that is about to be initialized. arg0=mem, returns mem
{name: "VarKill"}, // aux is a *gc.Node of a variable that is known to be dead. arg0=mem, returns mem
{name: "VarLive"}, // aux is a *gc.Node of a variable that must be kept live. arg0=mem, returns mem
}
// kind control successors implicit exit

View file

@ -21,7 +21,7 @@ func checkLower(f *Func) {
continue // lowered
}
switch v.Op {
case OpSP, OpSB, OpInitMem, OpArg, OpPhi, OpVarDef, OpVarKill:
case OpSP, OpSB, OpInitMem, OpArg, OpPhi, OpVarDef, OpVarKill, OpVarLive:
continue // ok not to lower
}
s := "not lowered: " + v.Op.String() + " " + v.Type.SimpleString()

View file

@ -552,6 +552,7 @@ const (
OpFwdRef
OpVarDef
OpVarKill
OpVarLive
)
var opcodeTable = [...]opInfo{
@ -4310,6 +4311,10 @@ var opcodeTable = [...]opInfo{
name: "VarKill",
generic: true,
},
{
name: "VarLive",
generic: true,
},
}
func (o Op) Asm() int { return opcodeTable[o].asm }

View file

@ -764,9 +764,7 @@ func bgen_float(n *gc.Node, wantTrue bool, likely int, to *obj.Prog) {
gc.Cgen(nr, &tmp)
gc.Cgen(nl, &tmp)
}
gins(x86.AFUCOMIP, &tmp, &n2)
gins(x86.AFMOVDP, &tmp, &tmp) // annoying pop but still better than STSW+SAHF
gins(x86.AFUCOMPP, &tmp, &n2)
} else {
// TODO(rsc): The moves back and forth to memory
// here are for truncating the value to 32 bits.
@ -783,9 +781,9 @@ func bgen_float(n *gc.Node, wantTrue bool, likely int, to *obj.Prog) {
gc.Cgen(nl, &t2)
gmove(&t2, &tmp)
gins(x86.AFCOMFP, &t1, &tmp)
}
gins(x86.AFSTSW, nil, &ax)
gins(x86.ASAHF, nil, nil)
}
} else {
// Not 387
if !nl.Addable {

View file

@ -1198,14 +1198,17 @@ func floatmove(f *gc.Node, t *gc.Node) {
// if 0 > v { answer = 0 }
gins(x86.AFMOVD, &zerof, &f0)
gins(x86.AFUCOMIP, &f0, &f1)
gins(x86.AFUCOMP, &f0, &f1)
gins(x86.AFSTSW, nil, &ax)
gins(x86.ASAHF, nil, nil)
p1 := gc.Gbranch(optoas(gc.OGT, gc.Types[tt]), nil, 0)
// if 1<<64 <= v { answer = 0 too }
gins(x86.AFMOVD, &two64f, &f0)
gins(x86.AFUCOMIP, &f0, &f1)
gins(x86.AFUCOMP, &f0, &f1)
gins(x86.AFSTSW, nil, &ax)
gins(x86.ASAHF, nil, nil)
p2 := gc.Gbranch(optoas(gc.OGT, gc.Types[tt]), nil, 0)
gc.Patch(p1, gc.Pc)
gins(x86.AFMOVVP, &f0, t) // don't care about t, but will pop the stack
@ -1235,7 +1238,9 @@ func floatmove(f *gc.Node, t *gc.Node) {
// actual work
gins(x86.AFMOVD, &two63f, &f0)
gins(x86.AFUCOMIP, &f0, &f1)
gins(x86.AFUCOMP, &f0, &f1)
gins(x86.AFSTSW, nil, &ax)
gins(x86.ASAHF, nil, nil)
p2 = gc.Gbranch(optoas(gc.OLE, gc.Types[tt]), nil, 0)
gins(x86.AFMOVVP, &f0, t)
p3 := gc.Gbranch(obj.AJMP, nil, 0)

View file

@ -40,6 +40,7 @@ var progtable = [x86.ALAST]obj.ProgInfo{
obj.ACHECKNIL: {Flags: gc.LeftRead},
obj.AVARDEF: {Flags: gc.Pseudo | gc.RightWrite},
obj.AVARKILL: {Flags: gc.Pseudo | gc.RightWrite},
obj.AVARLIVE: {Flags: gc.Pseudo | gc.LeftRead},
// NOP is an internal no-op that also stands
// for USED and SET annotations, not the Intel opcode.
@ -91,7 +92,11 @@ var progtable = [x86.ALAST]obj.ProgInfo{
x86.AFCOMDPP: {Flags: gc.SizeD | gc.LeftAddr | gc.RightRead},
x86.AFCOMF: {Flags: gc.SizeF | gc.LeftAddr | gc.RightRead},
x86.AFCOMFP: {Flags: gc.SizeF | gc.LeftAddr | gc.RightRead},
x86.AFUCOMIP: {Flags: gc.SizeF | gc.LeftAddr | gc.RightRead},
// NOTE(khr): don't use FUCOMI* instructions, not available
// on Pentium MMX. See issue 13923.
//x86.AFUCOMIP: {Flags: gc.SizeF | gc.LeftAddr | gc.RightRead},
x86.AFUCOMP: {Flags: gc.SizeD | gc.LeftRead | gc.RightRead},
x86.AFUCOMPP: {Flags: gc.SizeD | gc.LeftRead | gc.RightRead},
x86.AFCHS: {Flags: gc.SizeD | RightRdwr}, // also SizeF
x86.AFDIVDP: {Flags: gc.SizeD | gc.LeftAddr | RightRdwr},

View file

@ -656,7 +656,7 @@ func (t *tester) supportedBuildmode(mode string) bool {
case "c-shared":
switch pair {
case "linux-386", "linux-amd64", "linux-arm", "linux-arm64",
"darwin-amd64",
"darwin-amd64", "darwin-386",
"android-arm", "android-arm64", "android-386":
return true
}
@ -913,6 +913,12 @@ func (t *tester) cgoTestSO(dt *distTest, testpath string) error {
s = "DYLD_LIBRARY_PATH"
}
cmd.Env = mergeEnvLists([]string{s + "=."}, os.Environ())
// On FreeBSD 64-bit architectures, the 32-bit linker looks for
// different environment variables.
if t.goos == "freebsd" && t.gohostarch == "386" {
cmd.Env = mergeEnvLists([]string{"LD_32_LIBRARY_PATH=."}, cmd.Env)
}
}
return cmd.Run()
}

View file

@ -461,7 +461,7 @@ func main() {
}
}
if gohostarch == "arm" {
if gohostarch == "arm" || gohostarch == "mips64" || gohostarch == "mips64le" {
maxbg = min(maxbg, runtime.NumCPU())
}
bginit()

View file

@ -354,7 +354,7 @@ func buildModeInit() {
case "linux/amd64", "linux/arm", "linux/arm64", "linux/386",
"android/amd64", "android/arm", "android/arm64", "android/386":
codegenArg = "-shared"
case "darwin/amd64":
case "darwin/amd64", "darwin/386":
default:
fatalf("-buildmode=c-shared not supported on %s\n", platform)
}
@ -822,7 +822,9 @@ func goFilesPackage(gofiles []string) *Package {
pkg := new(Package)
pkg.local = true
pkg.cmdline = true
stk.push("main")
pkg.load(&stk, bp, err)
stk.pop()
pkg.localPrefix = dirToImportPath(dir)
pkg.ImportPath = "command-line-arguments"
pkg.target = ""
@ -999,13 +1001,22 @@ func (b *builder) action1(mode buildMode, depMode buildMode, p *Package, looksha
// Install header for cgo in c-archive and c-shared modes.
if p.usesCgo() && (buildBuildmode == "c-archive" || buildBuildmode == "c-shared") {
hdrTarget := a.target[:len(a.target)-len(filepath.Ext(a.target))] + ".h"
if buildContext.Compiler == "gccgo" {
// For the header file, remove the "lib"
// added by go/build, so we generate pkg.h
// rather than libpkg.h.
dir, file := filepath.Split(hdrTarget)
file = strings.TrimPrefix(file, "lib")
hdrTarget = filepath.Join(dir, file)
}
ah := &action{
p: a.p,
deps: []*action{a.deps[0]},
f: (*builder).installHeader,
pkgdir: a.pkgdir,
objdir: a.objdir,
target: a.target[:len(a.target)-len(filepath.Ext(a.target))] + ".h",
target: hdrTarget,
}
a.deps = append(a.deps, ah)
}
@ -2711,6 +2722,10 @@ func (tools gccgoToolchain) ld(b *builder, root *action, out string, allactions
// libffi.
ldflags = append(ldflags, "-Wl,-r", "-nostdlib", "-Wl,--whole-archive", "-lgolibbegin", "-Wl,--no-whole-archive")
if b.gccSupportsNoPie() {
ldflags = append(ldflags, "-no-pie")
}
// We are creating an object file, so we don't want a build ID.
ldflags = b.disableBuildID(ldflags)
@ -2718,7 +2733,7 @@ func (tools gccgoToolchain) ld(b *builder, root *action, out string, allactions
out = out + ".o"
case "c-shared":
ldflags = append(ldflags, "-shared", "-nostdlib", "-Wl,--whole-archive", "-lgolibbegin", "-Wl,--no-whole-archive", "-lgo", "-lgcc_s", "-lgcc")
ldflags = append(ldflags, "-shared", "-nostdlib", "-Wl,--whole-archive", "-lgolibbegin", "-Wl,--no-whole-archive", "-lgo", "-lgcc_s", "-lgcc", "-lc", "-lgcc")
default:
fatalf("-buildmode=%s not supported for gccgo", ldBuildmode)
@ -2902,6 +2917,36 @@ func (b *builder) ccompilerCmd(envvar, defcmd, objdir string) []string {
return a
}
// On systems with PIE (position independent executables) enabled by default,
// -no-pie must be passed when doing a partial link with -Wl,-r. But -no-pie is
// not supported by all compilers.
func (b *builder) gccSupportsNoPie() bool {
if goos != "linux" {
// On some BSD platforms, error messages from the
// compiler make it to the console despite cmd.Std*
// all being nil. As -no-pie is only required on linux
// systems so far, we only test there.
return false
}
src := filepath.Join(b.work, "trivial.c")
if err := ioutil.WriteFile(src, []byte{}, 0666); err != nil {
return false
}
cmdArgs := b.gccCmd(b.work)
cmdArgs = append(cmdArgs, "-no-pie", "-c", "trivial.c")
if buildN || buildX {
b.showcmd(b.work, "%s", joinUnambiguously(cmdArgs))
if buildN {
return false
}
}
cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...)
cmd.Dir = b.work
cmd.Env = envForDir(cmd.Dir, os.Environ())
out, err := cmd.CombinedOutput()
return err == nil && !bytes.Contains(out, []byte("unrecognized"))
}
// gccArchArgs returns arguments to pass to gcc based on the architecture.
func (b *builder) gccArchArgs() []string {
switch goarch {
@ -3158,6 +3203,10 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofi
}
ldflags := stringList(bareLDFLAGS, "-Wl,-r", "-nostdlib", staticLibs)
if b.gccSupportsNoPie() {
ldflags = append(ldflags, "-no-pie")
}
// We are creating an object file, so we don't want a build ID.
ldflags = b.disableBuildID(ldflags)

View file

@ -961,6 +961,16 @@ func TestInternalPackagesOutsideGOROOTAreRespected(t *testing.T) {
tg.grepBoth("use of internal package not allowed", "wrote error message for testdata/testinternal2")
}
func TestRunInternal(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
dir := filepath.Join(tg.pwd(), "testdata")
tg.setenv("GOPATH", dir)
tg.run("run", filepath.Join(dir, "src/run/good.go"))
tg.runFail("run", filepath.Join(dir, "src/run/bad.go"))
tg.grepStderr("use of internal package not allowed", "unexpected error for run/bad.go")
}
func testMove(t *testing.T, vcs, url, base, config string) {
testenv.MustHaveExternalNetwork(t)

View file

@ -348,11 +348,9 @@ func loadImport(path, srcDir string, parent *Package, stk *importStack, importPo
// TODO: After Go 1, decide when to pass build.AllowBinary here.
// See issue 3268 for mistakes to avoid.
buildMode := build.ImportComment
if go15VendorExperiment && mode&useVendor != 0 && path == origPath {
// We've already searched the vendor directories and didn't find anything.
// Let Import search them again so that, if the package is not found anywhere,
// the error includes the vendor directories in the list of places considered.
buildMode |= build.AllowVendor
if !go15VendorExperiment || mode&useVendor == 0 || path != origPath {
// Not vendoring, or we already found the vendored path.
buildMode |= build.IgnoreVendor
}
bp, err := buildContext.Import(path, srcDir, buildMode)
bp.ImportPath = importPath
@ -422,7 +420,7 @@ func vendoredImportPath(parent *Package, path string) (found string) {
continue
}
targ := filepath.Join(dir[:i], vpath)
if isDir(targ) {
if isDir(targ) && hasGoFiles(targ) {
// We started with parent's dir c:\gopath\src\foo\bar\baz\quux\xyzzy.
// We know the import path for parent's dir.
// We chopped off some number of path elements and
@ -445,6 +443,20 @@ func vendoredImportPath(parent *Package, path string) (found string) {
return path
}
// hasGoFiles reports whether dir contains any files with names ending in .go.
// For a vendor check we must exclude directories that contain no .go files.
// Otherwise it is not possible to vendor just a/b/c and still import the
// non-vendored a/b. See golang.org/issue/13832.
func hasGoFiles(dir string) bool {
fis, _ := ioutil.ReadDir(dir)
for _, fi := range fis {
if !fi.IsDir() && strings.HasSuffix(fi.Name(), ".go") {
return true
}
}
return false
}
// reusePackage reuses package p to satisfy the import at the top
// of the import stack stk. If this use causes an import loop,
// reusePackage updates p's error information to record the loop.
@ -504,7 +516,7 @@ func disallowInternal(srcDir string, p *Package, stk *importStack) *Package {
i-- // rewind over slash in ".../internal"
}
parent := p.Dir[:i+len(p.Dir)-len(p.ImportPath)]
if hasPathPrefix(filepath.ToSlash(srcDir), filepath.ToSlash(parent)) {
if hasFilePathPrefix(filepath.Clean(srcDir), filepath.Clean(parent)) {
return p
}
@ -601,7 +613,7 @@ func disallowVendorVisibility(srcDir string, p *Package, stk *importStack) *Pack
return p
}
parent := p.Dir[:truncateTo]
if hasPathPrefix(filepath.ToSlash(srcDir), filepath.ToSlash(parent)) {
if hasFilePathPrefix(filepath.Clean(srcDir), filepath.Clean(parent)) {
return p
}

5
src/cmd/go/testdata/src/run/bad.go vendored Normal file
View file

@ -0,0 +1,5 @@
package main
import _ "run/subdir/internal/private"
func main() {}

5
src/cmd/go/testdata/src/run/good.go vendored Normal file
View file

@ -0,0 +1,5 @@
package main
import _ "run/internal"
func main() {}

View file

@ -0,0 +1 @@
package internal

View file

@ -0,0 +1 @@
package private

View file

@ -0,0 +1 @@
package dir1

View file

@ -0,0 +1 @@
package dir2

View file

@ -3,3 +3,5 @@ package x
import _ "p"
import _ "q"
import _ "r"
import _ "vend/dir1" // not vendored
import _ "vend/dir1/dir2" // vendored

View file

@ -24,12 +24,14 @@ func TestVendorImports(t *testing.T) {
tg.run("list", "-f", "{{.ImportPath}} {{.Imports}}", "vend/...")
want := `
vend [vend/vendor/p r]
vend/dir1 []
vend/hello [fmt vend/vendor/strings]
vend/subdir [vend/vendor/p r]
vend/vendor/p []
vend/vendor/q []
vend/vendor/strings []
vend/x [vend/x/vendor/p vend/vendor/q vend/x/vendor/r]
vend/vendor/vend/dir1/dir2 []
vend/x [vend/x/vendor/p vend/vendor/q vend/x/vendor/r vend/dir1 vend/vendor/vend/dir1/dir2]
vend/x/invalid [vend/x/invalid/vendor/foo]
vend/x/vendor/p []
vend/x/vendor/p/p [notfound]
@ -45,6 +47,14 @@ func TestVendorImports(t *testing.T) {
}
}
func TestVendorBuild(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
tg.setenv("GO15VENDOREXPERIMENT", "1")
tg.run("build", "vend/x")
}
func TestVendorRun(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()

View file

@ -282,6 +282,7 @@ const (
AUSEFIELD
AVARDEF
AVARKILL
AVARLIVE
A_ARCHSPECIFIC
)
@ -609,6 +610,12 @@ type Link struct {
Version int
Textp *LSym
Etextp *LSym
// state for writing objects
Text *LSym
Data *LSym
Etext *LSym
Edata *LSym
}
// The smallest possible offset from the hardware stack pointer to a local

View file

@ -111,6 +111,11 @@ import (
// out a Go object file. The linker does not call this; the linker
// does not write out object files.
func Writeobjdirect(ctxt *Link, b *Biobuf) {
Flushplist(ctxt)
Writeobjfile(ctxt, b)
}
func Flushplist(ctxt *Link) {
var flag int
var s *LSym
var p *Prog
@ -119,13 +124,11 @@ func Writeobjdirect(ctxt *Link, b *Biobuf) {
// Build list of symbols, and assign instructions to lists.
// Ignore ctxt->plist boundaries. There are no guarantees there,
// and the C compilers and assemblers just use one big list.
var text *LSym
// and the assemblers just use one big list.
var curtext *LSym
var data *LSym
var text *LSym
var etext *LSym
var edata *LSym
for pl := ctxt.Plist; pl != nil; pl = pl.Link {
for p = pl.Firstpc; p != nil; p = plink {
if ctxt.Debugasm != 0 && ctxt.Debugvlog != 0 {
@ -174,10 +177,10 @@ func Writeobjdirect(ctxt *Link, b *Biobuf) {
log.Fatalf("symbol %s listed multiple times", s.Name)
}
s.Onlist = 1
if data == nil {
data = s
if ctxt.Data == nil {
ctxt.Data = s
} else {
edata.Next = s
ctxt.Edata.Next = s
}
s.Next = nil
s.Size = p.To.Offset
@ -195,7 +198,7 @@ func Writeobjdirect(ctxt *Link, b *Biobuf) {
} else if flag&TLSBSS != 0 {
s.Type = STLSBSS
}
edata = s
ctxt.Edata = s
continue
}
@ -298,6 +301,17 @@ func Writeobjdirect(ctxt *Link, b *Biobuf) {
linkpcln(ctxt, s)
}
// Add to running list in ctxt.
if ctxt.Etext == nil {
ctxt.Text = text
} else {
ctxt.Etext.Next = text
}
ctxt.Etext = etext
ctxt.Plist = nil
}
func Writeobjfile(ctxt *Link, b *Biobuf) {
// Emit header.
Bputc(b, 0)
@ -312,10 +326,10 @@ func Writeobjdirect(ctxt *Link, b *Biobuf) {
wrstring(b, "")
// Emit symbols.
for s := text; s != nil; s = s.Next {
for s := ctxt.Text; s != nil; s = s.Next {
writesym(ctxt, b, s)
}
for s := data; s != nil; s = s.Next {
for s := ctxt.Data; s != nil; s = s.Next {
writesym(ctxt, b, s)
}

View file

@ -611,7 +611,7 @@ func RegisterOpcode(lo int, Anames []string) {
}
func Aconv(a int) string {
if a < A_ARCHSPECIFIC {
if 0 <= a && a < len(Anames) {
return Anames[a]
}
for i := range aSpace {
@ -643,6 +643,7 @@ var Anames = []string{
"USEFIELD",
"VARDEF",
"VARKILL",
"VARLIVE",
}
func Bool2int(b bool) int {

View file

@ -181,6 +181,7 @@ const (
APAUSE
APOPAL
APOPAW
APOPCNT
APOPFL
APOPFW
APOPL
@ -500,6 +501,7 @@ const (
AXADDQ
AXCHGQ
AXORQ
AXGETBV
// media
AADDPD
@ -614,6 +616,9 @@ const (
APCMPGTL
APCMPGTW
APEXTRW
APEXTRB
APEXTRD
APEXTRQ
APFACC
APFADD
APFCMPEQ
@ -632,6 +637,7 @@ const (
APFSUB
APFSUBR
APINSRW
APINSRB
APINSRD
APINSRQ
APMADDWL

View file

@ -149,6 +149,7 @@ var Anames = []string{
"PAUSE",
"POPAL",
"POPAW",
"POPCNT",
"POPFL",
"POPFW",
"POPL",
@ -451,6 +452,7 @@ var Anames = []string{
"XADDQ",
"XCHGQ",
"XORQ",
"XGETBV",
"ADDPD",
"ADDPS",
"ADDSD",
@ -563,6 +565,9 @@ var Anames = []string{
"PCMPGTL",
"PCMPGTW",
"PEXTRW",
"PEXTRB",
"PEXTRD",
"PEXTRQ",
"PFACC",
"PFADD",
"PFCMPEQ",
@ -581,6 +586,7 @@ var Anames = []string{
"PFSUB",
"PFSUBR",
"PINSRW",
"PINSRB",
"PINSRD",
"PINSRQ",
"PMADDWL",

View file

@ -187,6 +187,7 @@ const (
Zm_r_xm_nr
Zr_m_xm_nr
Zibm_r /* mmx1,mmx2/mem64,imm8 */
Zibr_m
Zmb_r
Zaut_r
Zo_m
@ -219,6 +220,7 @@ const (
Pf2 = 0xf2 /* xmm escape 1: f2 0f */
Pf3 = 0xf3 /* xmm escape 2: f3 0f */
Pq3 = 0x67 /* xmm escape 3: 66 48 0f */
Pfw = 0xf4 /* Pf3 with Rex.w: f3 48 0f */
Pvex1 = 0xc5 /* 66.0f escape, vex encoding */
Pvex2 = 0xc6 /* f3.0f escape, vex encoding */
Pvex3 = 0xc7 /* 66.0f38 escape, vex encoding */
@ -720,6 +722,10 @@ var yextrw = []ytab{
{Yu8, Yxr, Yrl, Zibm_r, 2},
}
var yextr = []ytab{
{Yu8, Yxr, Ymm, Zibr_m, 3},
}
var yinsrw = []ytab{
{Yu8, Yml, Yxr, Zibm_r, 2},
}
@ -1162,6 +1168,9 @@ var optab =
{APCMPGTL, ymm, Py1, [23]uint8{0x66, Pe, 0x66}},
{APCMPGTW, ymm, Py1, [23]uint8{0x65, Pe, 0x65}},
{APEXTRW, yextrw, Pq, [23]uint8{0xc5, 00}},
{APEXTRB, yextr, Pq, [23]uint8{0x3a, 0x14, 00}},
{APEXTRD, yextr, Pq, [23]uint8{0x3a, 0x16, 00}},
{APEXTRQ, yextr, Pq3, [23]uint8{0x3a, 0x16, 00}},
{APF2IL, ymfp, Px, [23]uint8{0x1d}},
{APF2IW, ymfp, Px, [23]uint8{0x1c}},
{API2FL, ymfp, Px, [23]uint8{0x0d}},
@ -1183,6 +1192,7 @@ var optab =
{APFSUB, ymfp, Px, [23]uint8{0x9a}},
{APFSUBR, ymfp, Px, [23]uint8{0xaa}},
{APINSRW, yinsrw, Pq, [23]uint8{0xc4, 00}},
{APINSRB, yinsr, Pq, [23]uint8{0x3a, 0x20, 00}},
{APINSRD, yinsr, Pq, [23]uint8{0x3a, 0x22, 00}},
{APINSRQ, yinsr, Pq3, [23]uint8{0x3a, 0x22, 00}},
{APMADDWL, ymm, Py1, [23]uint8{0xf5, Pe, 0xf5}},
@ -1198,6 +1208,7 @@ var optab =
{APMULULQ, ymm, Py1, [23]uint8{0xf4, Pe, 0xf4}},
{APOPAL, ynone, P32, [23]uint8{0x61}},
{APOPAW, ynone, Pe, [23]uint8{0x61}},
{APOPCNT, yml_rl, Pfw, [23]uint8{0xb8}},
{APOPFL, ynone, P32, [23]uint8{0x9d}},
{APOPFQ, ynone, Py, [23]uint8{0x9d}},
{APOPFW, ynone, Pe, [23]uint8{0x9d}},
@ -1533,6 +1544,7 @@ var optab =
{AXABORT, yxabort, Px, [23]uint8{0xc6, 0xf8}},
{AXEND, ynone, Px, [23]uint8{0x0f, 01, 0xd5}},
{AXTEST, ynone, Px, [23]uint8{0x0f, 01, 0xd6}},
{AXGETBV, ynone, Pm, [23]uint8{01, 0xd0}},
{obj.AUSEFIELD, ynop, Px, [23]uint8{0, 0}},
{obj.ATYPE, nil, 0, [23]uint8{}},
{obj.AFUNCDATA, yfuncdata, Px, [23]uint8{0, 0}},
@ -3194,6 +3206,15 @@ func doasm(ctxt *obj.Link, p *obj.Prog) {
ctxt.Andptr[0] = Pm
ctxt.Andptr = ctxt.Andptr[1:]
case Pfw: /* first escape, Rex.w, and second escape */
ctxt.Andptr[0] = Pf3
ctxt.Andptr = ctxt.Andptr[1:]
ctxt.Andptr[0] = Pw
ctxt.Andptr = ctxt.Andptr[1:]
ctxt.Andptr[0] = Pm
ctxt.Andptr = ctxt.Andptr[1:]
case Pm: /* opcode escape */
ctxt.Andptr[0] = Pm
ctxt.Andptr = ctxt.Andptr[1:]
@ -3343,7 +3364,7 @@ func doasm(ctxt *obj.Link, p *obj.Prog) {
ctxt.Andptr[0] = byte(op)
ctxt.Andptr = ctxt.Andptr[1:]
case Zibm_r:
case Zibm_r, Zibr_m:
for {
tmp1 := z
z++
@ -3354,7 +3375,11 @@ func doasm(ctxt *obj.Link, p *obj.Prog) {
ctxt.Andptr[0] = byte(op)
ctxt.Andptr = ctxt.Andptr[1:]
}
if yt.zcase == Zibr_m {
asmand(ctxt, p, &p.To, p.From3)
} else {
asmand(ctxt, p, p.From3, &p.To)
}
ctxt.Andptr[0] = byte(p.From.Offset)
ctxt.Andptr = ctxt.Andptr[1:]

View file

@ -1068,7 +1068,7 @@ func hostlink() {
argv = append(argv, "-pie")
case BuildmodeCShared:
if HEADTYPE == obj.Hdarwin {
argv = append(argv, "-dynamiclib")
argv = append(argv, "-dynamiclib", "-Wl,-read_only_relocs,suppress")
} else {
// ELF.
argv = append(argv, "-Wl,-Bsymbolic")

View file

@ -566,6 +566,25 @@ func Asmbmacho() {
}
}
if Linkmode == LinkInternal {
// For lldb, must say LC_VERSION_MIN_MACOSX or else
// it won't know that this Mach-O binary is from OS X
// (could be iOS or WatchOS intead).
// Go on iOS uses linkmode=external, and linkmode=external
// adds this itself. So we only need this code for linkmode=internal
// and we can assume OS X.
//
// See golang.org/issues/12941.
const (
LC_VERSION_MIN_MACOSX = 0x24
LC_VERSION_MIN_IPHONEOS = 0x25
LC_VERSION_MIN_WATCHOS = 0x30
)
ml := newMachoLoad(LC_VERSION_MIN_MACOSX, 2)
ml.data[0] = 10<<16 | 7<<8 | 0<<0 // OS X version 10.7.0
ml.data[1] = 10<<16 | 7<<8 | 0<<0 // SDK 10.7.0
}
// TODO: dwarf headers go in ms too
if Debug['s'] == 0 {
dwarfaddmachoheaders(ms)

View file

@ -1217,12 +1217,17 @@ func Asmbpe() {
// larger size, as verified with VMMap.
// Go code would be OK with 64k stacks, but we need larger stacks for cgo.
// That default stack reserve size affects only the main thread,
// for other threads we specify stack size in runtime explicitly
//
// The default stack reserve size affects only the main
// thread, ctrlhandler thread, and profileloop thread. For
// these, it must be greater than the stack size assumed by
// externalthreadhandler.
//
// For other threads we specify stack size in runtime explicitly
// (runtime knows whether cgo is enabled or not).
// If you change stack reserve sizes here,
// change STACKSIZE in runtime/cgo/gcc_windows_{386,amd64}.c and correspondent
// CreateThread parameter in runtime.newosproc as well.
// For these, the reserve must match STACKSIZE in
// runtime/cgo/gcc_windows_{386,amd64}.c and the correspondent
// CreateThread parameter in runtime.newosproc.
if !iscgo {
oh64.SizeOfStackReserve = 0x00020000
oh.SizeOfStackReserve = 0x00020000

View file

@ -217,8 +217,6 @@ Lexp_dec_loop:
MOVUPS X0, 16(DX)
RET
#define PSHUFD_X0_X0_ BYTE $0x66; BYTE $0x0f; BYTE $0x70; BYTE $0xc0
#define PSHUFD_X1_X1_ BYTE $0x66; BYTE $0x0f; BYTE $0x70; BYTE $0xc9
TEXT _expand_key_128<>(SB),NOSPLIT,$0
PSHUFD $0xff, X1, X1
SHUFPS $0x10, X0, X4
@ -230,8 +228,6 @@ TEXT _expand_key_128<>(SB),NOSPLIT,$0
ADDQ $16, BX
RET
#define PSLLDQ_X5_ BYTE $0x66; BYTE $0x0f; BYTE $0x73; BYTE $0xfd
#define PSHUFD_X0_X3_ BYTE $0x66; BYTE $0x0f; BYTE $0x70; BYTE $0xd8
TEXT _expand_key_192a<>(SB),NOSPLIT,$0
PSHUFD $0x55, X1, X1
SHUFPS $0x10, X0, X4
@ -242,7 +238,7 @@ TEXT _expand_key_192a<>(SB),NOSPLIT,$0
MOVAPS X2, X5
MOVAPS X2, X6
PSLLDQ_X5_; BYTE $0x4
PSLLDQ $0x4, X5
PSHUFD $0xff, X0, X3
PXOR X3, X2
PXOR X5, X2
@ -264,7 +260,7 @@ TEXT _expand_key_192b<>(SB),NOSPLIT,$0
PXOR X1, X0
MOVAPS X2, X5
PSLLDQ_X5_; BYTE $0x4
PSLLDQ $0x4, X5
PSHUFD $0xff, X0, X3
PXOR X3, X2
PXOR X5, X2

View file

@ -345,7 +345,7 @@ TEXT ·gcmAesData(SB),NOSPLIT,$0
PXOR B0, B0
MOVQ (aut), B0
PINSRD $2, 8(aut), B0
BYTE $0x66; BYTE $0x0f; BYTE $0x3a; BYTE $0x20; BYTE $0x46; BYTE $0x0c; BYTE $0x0c //PINSRB $12, 12(aut), B0
PINSRB $12, 12(aut), B0
XORQ autLen, autLen
JMP dataMul
@ -404,7 +404,7 @@ dataEnd:
dataLoadLoop:
PSLLDQ $1, B0
BYTE $0x66; BYTE $0x0f; BYTE $0x3a; BYTE $0x20; BYTE $0x06; BYTE $0x00 //PINSRB $0, (aut), B0
PINSRB $0, (aut), B0
LEAQ -1(aut), aut
DECQ autLen
@ -892,7 +892,7 @@ encLast4:
PXOR B0, B0
ptxLoadLoop:
PSLLDQ $1, B0
BYTE $0x66; BYTE $0x0f; BYTE $0x3a; BYTE $0x20; BYTE $0x06; BYTE $0x00 //PINSRB $0, (ptx), B0
PINSRB $0, (ptx), B0
LEAQ -1(ptx), ptx
DECQ ptxLen
JNE ptxLoadLoop
@ -1264,7 +1264,7 @@ decLast3:
PXOR T1, B0
ptxStoreLoop:
BYTE $0x66; BYTE $0x0f; BYTE $0x3a; BYTE $0x14; BYTE $0x06; BYTE $0x00 // PEXTRB $0, B0, (ptx)
PEXTRB $0, B0, (ptx)
PSRLDQ $1, B0
LEAQ 1(ptx), ptx
DECQ ptxLen

View file

@ -38,6 +38,9 @@ type AEAD interface {
//
// The ciphertext and dst may alias exactly or not at all. To reuse
// ciphertext's storage for the decrypted output, use ciphertext[:0] as dst.
//
// Even if the function fails, the contents of dst, up to its capacity,
// may be overwritten.
Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error)
}
@ -168,11 +171,19 @@ func (g *gcm) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
var expectedTag [gcmTagSize]byte
g.auth(expectedTag[:], ciphertext, data, &tagMask)
ret, out := sliceForAppend(dst, len(ciphertext))
if subtle.ConstantTimeCompare(expectedTag[:], tag) != 1 {
// The AESNI code decrypts and authenticates concurrently, and
// so overwrites dst in the event of a tag mismatch. That
// behaviour is mimicked here in order to be consistent across
// platforms.
for i := range out {
out[i] = 0
}
return nil, errOpen
}
ret, out := sliceForAppend(dst, len(ciphertext))
g.counterCrypt(out, ciphertext, &counter)
return ret, nil

View file

@ -240,3 +240,37 @@ func TestAESGCM(t *testing.T) {
ct[0] ^= 0x80
}
}
func TestTagFailureOverwrite(t *testing.T) {
// The AESNI GCM code decrypts and authenticates concurrently and so
// overwrites the output buffer before checking the authentication tag.
// In order to be consistent across platforms, all implementations
// should do this and this test checks that.
key, _ := hex.DecodeString("ab72c77b97cb5fe9a382d9fe81ffdbed")
nonce, _ := hex.DecodeString("54cc7dc2c37ec006bcc6d1db")
ciphertext, _ := hex.DecodeString("0e1bde206a07a9c2c1b65300f8c649972b4401346697138c7a4891ee59867d0c")
aes, _ := aes.NewCipher(key)
aesgcm, _ := cipher.NewGCM(aes)
dst := make([]byte, len(ciphertext)-16)
for i := range dst {
dst[i] = 42
}
result, err := aesgcm.Open(dst[:0], nonce, ciphertext, nil)
if err == nil {
t.Fatal("Bad Open still resulted in nil error.")
}
if result != nil {
t.Fatal("Failed Open returned non-nil result.")
}
for i := range dst {
if dst[i] != 0 {
t.Fatal("Failed Open didn't zero dst buffer")
}
}
}

View file

@ -16,6 +16,7 @@ import (
"io"
"net"
"sync"
"sync/atomic"
"time"
)
@ -56,6 +57,11 @@ type Conn struct {
input *block // application data waiting to be read
hand bytes.Buffer // handshake data waiting to be read
// activeCall is an atomic int32; the low bit is whether Close has
// been called. the rest of the bits are the number of goroutines
// in Conn.Write.
activeCall int32
tmp [16]byte
}
@ -855,8 +861,22 @@ func (c *Conn) readHandshake() (interface{}, error) {
return m, nil
}
var errClosed = errors.New("crypto/tls: use of closed connection")
// Write writes data to the connection.
func (c *Conn) Write(b []byte) (int, error) {
// interlock with Close below
for {
x := atomic.LoadInt32(&c.activeCall)
if x&1 != 0 {
return 0, errClosed
}
if atomic.CompareAndSwapInt32(&c.activeCall, x, x+2) {
defer atomic.AddInt32(&c.activeCall, -2)
break
}
}
if err := c.Handshake(); err != nil {
return 0, err
}
@ -960,6 +980,27 @@ func (c *Conn) Read(b []byte) (n int, err error) {
// Close closes the connection.
func (c *Conn) Close() error {
// Interlock with Conn.Write above.
var x int32
for {
x = atomic.LoadInt32(&c.activeCall)
if x&1 != 0 {
return errClosed
}
if atomic.CompareAndSwapInt32(&c.activeCall, x, x|1) {
break
}
}
if x != 0 {
// io.Writer and io.Closer should not be used concurrently.
// If Close is called while a Write is currently in-flight,
// interpret that as a sign that this Close is really just
// being used to break the Write and/or clean up resources and
// avoid sending the alertCloseNotify, which may block
// waiting on handshakeMutex or the c.out mutex.
return c.conn.Close()
}
var alertErr error
c.handshakeMutex.Lock()

View file

@ -6,6 +6,7 @@ package tls
import (
"bytes"
"errors"
"fmt"
"internal/testenv"
"io"
@ -364,3 +365,104 @@ func TestVerifyHostnameResumed(t *testing.T) {
c.Close()
}
}
func TestConnCloseBreakingWrite(t *testing.T) {
ln := newLocalListener(t)
defer ln.Close()
srvCh := make(chan *Conn, 1)
var serr error
var sconn net.Conn
go func() {
var err error
sconn, err = ln.Accept()
if err != nil {
serr = err
srvCh <- nil
return
}
serverConfig := *testConfig
srv := Server(sconn, &serverConfig)
if err := srv.Handshake(); err != nil {
serr = fmt.Errorf("handshake: %v", err)
srvCh <- nil
return
}
srvCh <- srv
}()
cconn, err := net.Dial("tcp", ln.Addr().String())
if err != nil {
t.Fatal(err)
}
defer cconn.Close()
conn := &changeImplConn{
Conn: cconn,
}
clientConfig := *testConfig
tconn := Client(conn, &clientConfig)
if err := tconn.Handshake(); err != nil {
t.Fatal(err)
}
srv := <-srvCh
if srv == nil {
t.Fatal(serr)
}
defer sconn.Close()
connClosed := make(chan struct{})
conn.closeFunc = func() error {
close(connClosed)
return nil
}
inWrite := make(chan bool, 1)
var errConnClosed = errors.New("conn closed for test")
conn.writeFunc = func(p []byte) (n int, err error) {
inWrite <- true
<-connClosed
return 0, errConnClosed
}
closeReturned := make(chan bool, 1)
go func() {
<-inWrite
tconn.Close() // test that this doesn't block forever.
closeReturned <- true
}()
_, err = tconn.Write([]byte("foo"))
if err != errConnClosed {
t.Errorf("Write error = %v; want errConnClosed", err)
}
<-closeReturned
if err := tconn.Close(); err != errClosed {
t.Errorf("Close error = %v; want errClosed", err)
}
}
// changeImplConn is a net.Conn which can change its Write and Close
// methods.
type changeImplConn struct {
net.Conn
writeFunc func([]byte) (int, error)
closeFunc func() error
}
func (w *changeImplConn) Write(p []byte) (n int, err error) {
if w.writeFunc != nil {
return w.writeFunc(p)
}
return w.Conn.Write(p)
}
func (w *changeImplConn) Close() error {
if w.closeFunc != nil {
return w.closeFunc()
}
return w.Conn.Close()
}

View file

@ -200,10 +200,15 @@ func IsScanValue(v interface{}) bool {
// ValueConverter that's used when a Stmt doesn't implement
// ColumnConverter.
//
// DefaultParameterConverter returns the given value directly if
// IsValue(value). Otherwise integer type are converted to
// int64, floats to float64, and strings to []byte. Other types are
// an error.
// DefaultParameterConverter returns its argument directly if
// IsValue(arg). Otherwise, if the argument implements Valuer, its
// Value method is used to return a Value. As a fallback, the provided
// argument's underlying type is used to convert it to a Value:
// underlying integer types are converted to int64, floats to float64,
// and strings to []byte. If the argument is a nil pointer,
// ConvertValue returns a nil Value. If the argument is a non-nil
// pointer, it is dereferenced and ConvertValue is called
// recursively. Other types are an error.
var DefaultParameterConverter defaultConverter
type defaultConverter struct{}

View file

@ -33,6 +33,9 @@ var _ = log.Printf
// INSERT|<tablename>|col=val,col2=val2,col3=?
// SELECT|<tablename>|projectcol1,projectcol2|filtercol=?,filtercol2=?
//
// Any of these can be preceded by PANIC|<method>|, to cause the
// named method on fakeStmt to panic.
//
// When opening a fakeDriver's database, it starts empty with no
// tables. All tables and data are stored in memory only.
type fakeDriver struct {
@ -111,6 +114,7 @@ type fakeStmt struct {
cmd string
table string
panic string
closed bool
@ -499,9 +503,15 @@ func (c *fakeConn) Prepare(query string) (driver.Stmt, error) {
if len(parts) < 1 {
return nil, errf("empty query")
}
stmt := &fakeStmt{q: query, c: c}
if len(parts) >= 3 && parts[0] == "PANIC" {
stmt.panic = parts[1]
parts = parts[2:]
}
cmd := parts[0]
stmt.cmd = cmd
parts = parts[1:]
stmt := &fakeStmt{q: query, c: c, cmd: cmd}
c.incrStat(&c.stmtsMade)
switch cmd {
case "WIPE":
@ -524,6 +534,9 @@ func (c *fakeConn) Prepare(query string) (driver.Stmt, error) {
}
func (s *fakeStmt) ColumnConverter(idx int) driver.ValueConverter {
if s.panic == "ColumnConverter" {
panic(s.panic)
}
if len(s.placeholderConverter) == 0 {
return driver.DefaultParameterConverter
}
@ -531,6 +544,9 @@ func (s *fakeStmt) ColumnConverter(idx int) driver.ValueConverter {
}
func (s *fakeStmt) Close() error {
if s.panic == "Close" {
panic(s.panic)
}
if s.c == nil {
panic("nil conn in fakeStmt.Close")
}
@ -550,6 +566,9 @@ var errClosed = errors.New("fakedb: statement has been closed")
var hookExecBadConn func() bool
func (s *fakeStmt) Exec(args []driver.Value) (driver.Result, error) {
if s.panic == "Exec" {
panic(s.panic)
}
if s.closed {
return nil, errClosed
}
@ -634,6 +653,9 @@ func (s *fakeStmt) execInsert(args []driver.Value, doInsert bool) (driver.Result
var hookQueryBadConn func() bool
func (s *fakeStmt) Query(args []driver.Value) (driver.Rows, error) {
if s.panic == "Query" {
panic(s.panic)
}
if s.closed {
return nil, errClosed
}
@ -716,6 +738,9 @@ rows:
}
func (s *fakeStmt) NumInput() int {
if s.panic == "NumInput" {
panic(s.panic)
}
return s.placeholders
}

View file

@ -1477,10 +1477,14 @@ func (s *Stmt) Exec(args ...interface{}) (Result, error) {
return nil, driver.ErrBadConn
}
func resultFromStatement(ds driverStmt, args ...interface{}) (Result, error) {
func driverNumInput(ds driverStmt) int {
ds.Lock()
want := ds.si.NumInput()
ds.Unlock()
defer ds.Unlock() // in case NumInput panics
return ds.si.NumInput()
}
func resultFromStatement(ds driverStmt, args ...interface{}) (Result, error) {
want := driverNumInput(ds)
// -1 means the driver doesn't know how to count the number of
// placeholders, so we won't sanity check input here and instead let the
@ -1495,8 +1499,8 @@ func resultFromStatement(ds driverStmt, args ...interface{}) (Result, error) {
}
ds.Lock()
defer ds.Unlock()
resi, err := ds.si.Exec(dargs)
ds.Unlock()
if err != nil {
return nil, err
}
@ -1927,6 +1931,6 @@ func stack() string {
// withLock runs while holding lk.
func withLock(lk sync.Locker, fn func()) {
lk.Lock()
defer lk.Unlock() // in case fn panics
fn()
lk.Unlock()
}

View file

@ -68,6 +68,46 @@ func newTestDB(t testing.TB, name string) *DB {
return db
}
func TestDriverPanic(t *testing.T) {
// Test that if driver panics, database/sql does not deadlock.
db, err := Open("test", fakeDBName)
if err != nil {
t.Fatalf("Open: %v", err)
}
expectPanic := func(name string, f func()) {
defer func() {
err := recover()
if err == nil {
t.Fatalf("%s did not panic", name)
}
}()
f()
}
expectPanic("Exec Exec", func() { db.Exec("PANIC|Exec|WIPE") })
exec(t, db, "WIPE") // check not deadlocked
expectPanic("Exec NumInput", func() { db.Exec("PANIC|NumInput|WIPE") })
exec(t, db, "WIPE") // check not deadlocked
expectPanic("Exec Close", func() { db.Exec("PANIC|Close|WIPE") })
exec(t, db, "WIPE") // check not deadlocked
exec(t, db, "PANIC|Query|WIPE") // should run successfully: Exec does not call Query
exec(t, db, "WIPE") // check not deadlocked
exec(t, db, "CREATE|people|name=string,age=int32,photo=blob,dead=bool,bdate=datetime")
expectPanic("Query Query", func() { db.Query("PANIC|Query|SELECT|people|age,name|") })
expectPanic("Query NumInput", func() { db.Query("PANIC|NumInput|SELECT|people|age,name|") })
expectPanic("Query Close", func() {
rows, err := db.Query("PANIC|Close|SELECT|people|age,name|")
if err != nil {
t.Fatal(err)
}
rows.Close()
})
db.Query("PANIC|Exec|SELECT|people|age,name|") // should run successfully: Query does not call Exec
exec(t, db, "WIPE") // check not deadlocked
}
func exec(t testing.TB, db *DB, query string, args ...interface{}) {
_, err := db.Exec(query, args...)
if err != nil {

7
src/debug/dwarf/testdata/cycle.c vendored Normal file
View file

@ -0,0 +1,7 @@
typedef struct aaa *AAA;
typedef AAA BBB;
struct aaa { BBB val; };
AAA x(void) {
return (AAA)0;
}

BIN
src/debug/dwarf/testdata/cycle.elf vendored Normal file

Binary file not shown.

View file

@ -275,12 +275,14 @@ type typeReader interface {
// Type reads the type at off in the DWARF ``info'' section.
func (d *Data) Type(off Offset) (Type, error) {
return d.readType("info", d.Reader(), off, d.typeCache)
return d.readType("info", d.Reader(), off, d.typeCache, nil)
}
// readType reads a type from r at off of name using and updating a
// type cache.
func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Offset]Type) (Type, error) {
// readType reads a type from r at off of name. It adds types to the
// type cache, appends new typedef types to typedefs, and computes the
// sizes of types. Callers should pass nil for typedefs; this is used
// for internal recursion.
func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Offset]Type, typedefs *[]*TypedefType) (Type, error) {
if t, ok := typeCache[off]; ok {
return t, nil
}
@ -294,9 +296,24 @@ func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Off
return nil, DecodeError{name, off, "no type at offset"}
}
// If this is the root of the recursion, prepare to resolve
// typedef sizes once the recursion is done. This must be done
// after the type graph is constructed because it may need to
// resolve cycles in a different order than readType
// encounters them.
if typedefs == nil {
var typedefList []*TypedefType
defer func() {
for _, t := range typedefList {
t.Common().ByteSize = t.Type.Size()
}
}()
typedefs = &typedefList
}
// Parse type from Entry.
// Must always set typeCache[off] before calling
// d.Type recursively, to handle circular types correctly.
// d.readType recursively, to handle circular types correctly.
var typ Type
nextDepth := 0
@ -345,7 +362,7 @@ func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Off
var t Type
switch toff := tval.(type) {
case Offset:
if t, err = d.readType(name, r.clone(), toff, typeCache); err != nil {
if t, err = d.readType(name, r.clone(), toff, typeCache, typedefs); err != nil {
return nil
}
case uint64:
@ -674,7 +691,10 @@ func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Off
b = -1
switch t := typ.(type) {
case *TypedefType:
b = t.Type.Size()
// Record that we need to resolve this
// type's size once the type graph is
// constructed.
*typedefs = append(*typedefs, t)
case *PtrType:
b = int64(addressSize)
}

View file

@ -120,3 +120,37 @@ func testTypedefs(t *testing.T, d *Data, kind string) {
}
}
}
func TestTypedefCycle(t *testing.T) {
// See issue #13039: reading a typedef cycle starting from a
// different place than the size needed to be computed from
// used to crash.
//
// cycle.elf built with GCC 4.8.4:
// gcc -g -c -o cycle.elf cycle.c
d := elfData(t, "testdata/cycle.elf")
r := d.Reader()
offsets := []Offset{}
for {
e, err := r.Next()
if err != nil {
t.Fatal("r.Next:", err)
}
if e == nil {
break
}
switch e.Tag {
case TagBaseType, TagTypedef, TagPointerType, TagStructType:
offsets = append(offsets, e.Offset)
}
}
// Parse each type with a fresh type cache.
for _, offset := range offsets {
d := elfData(t, "testdata/cycle.elf")
_, err := d.Type(offset)
if err != nil {
t.Fatalf("d.Type(0x%x): %s", offset, err)
}
}
}

View file

@ -101,7 +101,7 @@ func (d *Data) sigToType(sig uint64) (Type, error) {
b := makeBuf(d, tu, tu.name, tu.off, tu.data)
r := &typeUnitReader{d: d, tu: tu, b: b}
t, err := d.readType(tu.name, r, Offset(tu.toff), make(map[Offset]Type))
t, err := d.readType(tu.name, r, Offset(tu.toff), make(map[Offset]Type), nil)
if err != nil {
return nil, err
}

View file

@ -347,6 +347,7 @@ func (enc *Encoding) DecodeString(s string) ([]byte, error) {
type decoder struct {
err error
readErr error // error from r.Read
enc *Encoding
r io.Reader
end bool // saw end of message
@ -357,10 +358,6 @@ type decoder struct {
}
func (d *decoder) Read(p []byte) (n int, err error) {
if d.err != nil {
return 0, d.err
}
// Use leftover decoded output from last read.
if len(d.out) > 0 {
n = copy(p, d.out)
@ -368,9 +365,14 @@ func (d *decoder) Read(p []byte) (n int, err error) {
return n, nil
}
if d.err != nil {
return 0, d.err
}
// This code assumes that d.r strips supported whitespace ('\r' and '\n').
// Read a chunk.
// Refill buffer.
for d.nbuf < 4 && d.readErr == nil {
nn := len(p) / 3 * 4
if nn < 4 {
nn = 4
@ -378,9 +380,31 @@ func (d *decoder) Read(p []byte) (n int, err error) {
if nn > len(d.buf) {
nn = len(d.buf)
}
nn, d.err = io.ReadAtLeast(d.r, d.buf[d.nbuf:nn], 4-d.nbuf)
nn, d.readErr = d.r.Read(d.buf[d.nbuf:nn])
d.nbuf += nn
if d.err != nil || d.nbuf < 4 {
}
if d.nbuf < 4 {
if d.enc.padChar == NoPadding && d.nbuf > 0 {
// Decode final fragment, without padding.
var nw int
nw, _, d.err = d.enc.decode(d.outbuf[:], d.buf[:d.nbuf])
d.nbuf = 0
d.end = true
d.out = d.outbuf[:nw]
n = copy(p, d.out)
d.out = d.out[n:]
if n > 0 || len(p) == 0 && len(d.out) > 0 {
return n, nil
}
if d.err != nil {
return 0, d.err
}
}
d.err = d.readErr
if d.err == io.EOF && d.nbuf > 0 {
d.err = io.ErrUnexpectedEOF
}
return 0, d.err
}
@ -396,13 +420,7 @@ func (d *decoder) Read(p []byte) (n int, err error) {
n, d.end, d.err = d.enc.decode(p, d.buf[:nr])
}
d.nbuf -= nr
for i := 0; i < d.nbuf; i++ {
d.buf[i] = d.buf[i+nr]
}
if d.err == nil {
d.err = err
}
copy(d.buf[:d.nbuf], d.buf[nr:])
return n, d.err
}

View file

@ -406,3 +406,28 @@ func BenchmarkDecodeString(b *testing.B) {
StdEncoding.DecodeString(data)
}
}
func TestDecoderRaw(t *testing.T) {
source := "AAAAAA"
want := []byte{0, 0, 0, 0}
// Direct.
dec1, err := RawURLEncoding.DecodeString(source)
if err != nil || !bytes.Equal(dec1, want) {
t.Errorf("RawURLEncoding.DecodeString(%q) = %x, %v, want %x, nil", source, dec1, err, want)
}
// Through reader. Used to fail.
r := NewDecoder(RawURLEncoding, bytes.NewReader([]byte(source)))
dec2, err := ioutil.ReadAll(io.LimitReader(r, 100))
if err != nil || !bytes.Equal(dec2, want) {
t.Errorf("reading NewDecoder(RawURLEncoding, %q) = %x, %v, want %x, nil", source, dec2, err, want)
}
// Should work with padding.
r = NewDecoder(URLEncoding, bytes.NewReader([]byte(source+"==")))
dec3, err := ioutil.ReadAll(r)
if err != nil || !bytes.Equal(dec3, want) {
t.Errorf("reading NewDecoder(URLEncoding, %q) = %x, %v, want %x, nil", source+"==", dec3, err, want)
}
}

View file

@ -37,6 +37,7 @@ import (
// To unmarshal JSON into a struct, Unmarshal matches incoming object
// keys to the keys used by Marshal (either the struct field name or its tag),
// preferring an exact match but also accepting a case-insensitive match.
// Unmarshal will only set exported fields of the struct.
//
// To unmarshal JSON into an interface value,
// Unmarshal stores one of these in the interface value:

View file

@ -344,18 +344,20 @@ const (
// See golang.org/s/go14customimport for more information.
ImportComment
// If AllowVendor is set, Import searches vendor directories
// By default, Import searches vendor directories
// that apply in the given source directory before searching
// the GOROOT and GOPATH roots.
// If an Import finds and returns a package using a vendor
// directory, the resulting ImportPath is the complete path
// to the package, including the path elements leading up
// to and including "vendor".
// For example, if Import("y", "x/subdir", AllowVendor) finds
// For example, if Import("y", "x/subdir", 0) finds
// "x/vendor/y", the returned package's ImportPath is "x/vendor/y",
// not plain "y".
// See golang.org/s/go15vendor for more information.
AllowVendor
//
// Setting IgnoreVendor ignores vendor directories.
IgnoreVendor
)
// A Package describes the Go package found in a directory.
@ -571,7 +573,7 @@ func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Packa
gopath := ctxt.gopath()
// Vendor directories get first chance to satisfy import.
if mode&AllowVendor != 0 && srcDir != "" {
if mode&IgnoreVendor == 0 && srcDir != "" {
searchVendor := func(root string, isGoroot bool) bool {
sub, ok := ctxt.hasSubdir(root, srcDir)
if !ok || !strings.HasPrefix(sub, "src/") || strings.Contains(sub, "/testdata/") {
@ -581,7 +583,7 @@ func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Packa
vendor := ctxt.joinPath(root, sub, "vendor")
if ctxt.isDir(vendor) {
dir := ctxt.joinPath(vendor, path)
if ctxt.isDir(dir) {
if ctxt.isDir(dir) && hasGoFiles(ctxt, dir) {
p.Dir = dir
p.ImportPath = strings.TrimPrefix(pathpkg.Join(sub, "vendor", path), "src/")
p.Goroot = isGoroot
@ -882,6 +884,20 @@ Found:
return p, pkgerr
}
// hasGoFiles reports whether dir contains any files with names ending in .go.
// For a vendor check we must exclude directories that contain no .go files.
// Otherwise it is not possible to vendor just a/b/c and still import the
// non-vendored a/b. See golang.org/issue/13832.
func hasGoFiles(ctxt *Context, dir string) bool {
ents, _ := ctxt.readDir(dir)
for _, ent := range ents {
if !ent.IsDir() && strings.HasSuffix(ent.Name(), ".go") {
return true
}
}
return false
}
func findImportComment(data []byte) (s string, line int) {
// expect keyword package
word, data := parseWord(data)

View file

@ -303,7 +303,7 @@ func TestImportVendor(t *testing.T) {
testenv.MustHaveGoBuild(t) // really must just have source
ctxt := Default
ctxt.GOPATH = ""
p, err := ctxt.Import("golang.org/x/net/http2/hpack", filepath.Join(ctxt.GOROOT, "src/net/http"), AllowVendor)
p, err := ctxt.Import("golang.org/x/net/http2/hpack", filepath.Join(ctxt.GOROOT, "src/net/http"), 0)
if err != nil {
t.Fatalf("cannot find vendored golang.org/x/net/http2/hpack from net/http directory: %v", err)
}
@ -317,7 +317,7 @@ func TestImportVendorFailure(t *testing.T) {
testenv.MustHaveGoBuild(t) // really must just have source
ctxt := Default
ctxt.GOPATH = ""
p, err := ctxt.Import("x.com/y/z", filepath.Join(ctxt.GOROOT, "src/net/http"), AllowVendor)
p, err := ctxt.Import("x.com/y/z", filepath.Join(ctxt.GOROOT, "src/net/http"), 0)
if err == nil {
t.Fatalf("found made-up package x.com/y/z in %s", p.Dir)
}
@ -327,3 +327,21 @@ func TestImportVendorFailure(t *testing.T) {
t.Fatalf("error on failed import does not mention GOROOT/src/vendor directory:\n%s", e)
}
}
func TestImportVendorParentFailure(t *testing.T) {
testenv.MustHaveGoBuild(t) // really must just have source
ctxt := Default
ctxt.GOPATH = ""
// This import should fail because the vendor/golang.org/x/net/http2 directory has no source code.
p, err := ctxt.Import("golang.org/x/net/http2", filepath.Join(ctxt.GOROOT, "src/net/http"), 0)
if err == nil {
t.Fatalf("found empty parent in %s", p.Dir)
}
if p != nil && p.Dir != "" {
t.Fatalf("decided to use %s", p.Dir)
}
e := err.Error()
if !strings.Contains(e, " (vendor tree)") {
t.Fatalf("error on failed import does not mention GOROOT/src/vendor directory:\n%s", e)
}
}

View file

@ -152,6 +152,7 @@ type reader struct {
// declarations
imports map[string]int
hasDotImp bool // if set, package contains a dot import
values []*Value // consts and vars
types map[string]*namedType
funcs methodSet
@ -471,6 +472,9 @@ func (r *reader) readFile(src *ast.File) {
if s, ok := spec.(*ast.ImportSpec); ok {
if import_, err := strconv.Unquote(s.Path.Value); err == nil {
r.imports[import_] = 1
if s.Name != nil && s.Name.Name == "." {
r.hasDotImp = true
}
}
}
}
@ -641,11 +645,12 @@ func (r *reader) computeMethodSets() {
func (r *reader) cleanupTypes() {
for _, t := range r.types {
visible := r.isVisible(t.name)
if t.decl == nil && (predeclaredTypes[t.name] || t.isEmbedded && visible) {
if t.decl == nil && (predeclaredTypes[t.name] || visible && (t.isEmbedded || r.hasDotImp)) {
// t.name is a predeclared type (and was not redeclared in this package),
// or it was embedded somewhere but its declaration is missing (because
// the AST is incomplete): move any associated values, funcs, and methods
// back to the top-level so that they are not lost.
// the AST is incomplete), or we have a dot-import (and all bets are off):
// move any associated values, funcs, and methods back to the top-level so
// that they are not lost.
// 1) move values
r.values = append(r.values, t.values...)
// 2) move factory functions

25
src/go/doc/testdata/issue13742.0.golden vendored Normal file
View file

@ -0,0 +1,25 @@
//
PACKAGE issue13742
IMPORTPATH
testdata/issue13742
IMPORTS
go/ast
FILENAMES
testdata/issue13742.go
FUNCTIONS
// Both F0 and G0 should appear as functions.
func F0(Node)
// Both F1 and G1 should appear as functions.
func F1(ast.Node)
//
func G0() Node
//
func G1() ast.Node

25
src/go/doc/testdata/issue13742.1.golden vendored Normal file
View file

@ -0,0 +1,25 @@
//
PACKAGE issue13742
IMPORTPATH
testdata/issue13742
IMPORTS
go/ast
FILENAMES
testdata/issue13742.go
FUNCTIONS
// Both F0 and G0 should appear as functions.
func F0(Node)
// Both F1 and G1 should appear as functions.
func F1(ast.Node)
//
func G0() Node
//
func G1() ast.Node

25
src/go/doc/testdata/issue13742.2.golden vendored Normal file
View file

@ -0,0 +1,25 @@
//
PACKAGE issue13742
IMPORTPATH
testdata/issue13742
IMPORTS
go/ast
FILENAMES
testdata/issue13742.go
FUNCTIONS
// Both F0 and G0 should appear as functions.
func F0(Node)
// Both F1 and G1 should appear as functions.
func F1(ast.Node)
//
func G0() Node
//
func G1() ast.Node

18
src/go/doc/testdata/issue13742.go vendored Normal file
View file

@ -0,0 +1,18 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package issue13742
import (
"go/ast"
. "go/ast"
)
// Both F0 and G0 should appear as functions.
func F0(Node) {}
func G0() Node { return nil }
// Both F1 and G1 should appear as functions.
func F1(ast.Node) {}
func G1() ast.Node { return nil }

View file

@ -20,15 +20,21 @@ type Lookup func(path string) (io.ReadCloser, error)
// For returns an Importer for the given compiler and lookup interface,
// or nil. Supported compilers are "gc", and "gccgo". If lookup is nil,
// the default package lookup mechanism for the given compiler is used.
// BUG(issue13847): For does not support non-nil lookup functions.
func For(compiler string, lookup Lookup) types.Importer {
switch compiler {
case "gc":
if lookup == nil {
return make(gcimports)
}
if lookup != nil {
panic("gc importer for custom import path lookup not yet implemented")
}
return make(gcimports)
case "gccgo":
if lookup == nil {
panic("gccgo importer for custom import path lookup not yet implemented")
}
var inst gccgoimporter.GccgoInstallation
if err := inst.InitFromDriver("gccgo"); err != nil {
return nil
@ -38,13 +44,13 @@ func For(compiler string, lookup Lookup) types.Importer {
importer: inst.GetImporter(nil, nil),
}
}
panic("gccgo importer for custom import path lookup not yet implemented")
}
// compiler not supported
return nil
}
// Default returns an Importer for the compiler that built the running binary.
// If available, the result implements types.ImporterFrom.
func Default() types.Importer {
return For(runtime.Compiler, nil)
}
@ -54,7 +60,14 @@ func Default() types.Importer {
type gcimports map[string]*types.Package
func (m gcimports) Import(path string) (*types.Package, error) {
return gcimporter.Import(m, path)
return m.ImportFrom(path, "" /* no vendoring */, 0)
}
func (m gcimports) ImportFrom(path, srcDir string, mode types.ImportMode) (*types.Package, error) {
if mode != 0 {
panic("mode must be 0")
}
return gcimporter.Import(m, path, srcDir)
}
// gccgo support
@ -65,5 +78,13 @@ type gccgoimports struct {
}
func (m *gccgoimports) Import(path string) (*types.Package, error) {
return m.ImportFrom(path, "" /* no vendoring */, 0)
}
func (m *gccgoimports) ImportFrom(path, srcDir string, mode types.ImportMode) (*types.Package, error) {
if mode != 0 {
panic("mode must be 0")
}
// TODO(gri) pass srcDir
return m.importer(m.packages, path)
}

View file

@ -72,7 +72,7 @@ func BImportData(imports map[string]*types.Package, data []byte, path string) (i
// read consts
for i := p.int(); i > 0; i-- {
name := p.string()
typ := p.typ()
typ := p.typ(nil)
val := p.value()
p.declare(types.NewConst(token.NoPos, pkg, name, typ, val))
}
@ -80,14 +80,14 @@ func BImportData(imports map[string]*types.Package, data []byte, path string) (i
// read vars
for i := p.int(); i > 0; i-- {
name := p.string()
typ := p.typ()
typ := p.typ(nil)
p.declare(types.NewVar(token.NoPos, pkg, name, typ))
}
// read funcs
for i := p.int(); i > 0; i-- {
name := p.string()
sig := p.typ().(*types.Signature)
sig := p.typ(nil).(*types.Signature)
p.int() // read and discard index of inlined function body
p.declare(types.NewFunc(token.NoPos, pkg, name, sig))
}
@ -97,7 +97,7 @@ func BImportData(imports map[string]*types.Package, data []byte, path string) (i
// name is parsed as part of named type and the
// type object is added to scope via respective
// named type
_ = p.typ().(*types.Named)
_ = p.typ(nil).(*types.Named)
}
// ignore compiler-specific import data
@ -190,7 +190,11 @@ type dddSlice struct {
func (t *dddSlice) Underlying() types.Type { return t }
func (t *dddSlice) String() string { return "..." + t.elem.String() }
func (p *importer) typ() types.Type {
// parent is the package which declared the type; parent == nil means
// the package currently imported. The parent package is needed for
// exported struct fields and interface methods which don't contain
// explicit package information in the export data.
func (p *importer) typ(parent *types.Package) types.Type {
// if the type was seen before, i is its index (>= 0)
i := p.tagOrIndex()
if i >= 0 {
@ -202,18 +206,18 @@ func (p *importer) typ() types.Type {
case namedTag:
// read type object
name := p.string()
tpkg := p.pkg()
scope := tpkg.Scope()
parent = p.pkg()
scope := parent.Scope()
obj := scope.Lookup(name)
// if the object doesn't exist yet, create and insert it
if obj == nil {
obj = types.NewTypeName(token.NoPos, tpkg, name, nil)
obj = types.NewTypeName(token.NoPos, parent, name, nil)
scope.Insert(obj)
}
if _, ok := obj.(*types.TypeName); !ok {
panic(fmt.Sprintf("pkg = %s, name = %s => %s", tpkg, name, obj))
panic(fmt.Sprintf("pkg = %s, name = %s => %s", parent, name, obj))
}
// associate new named type with obj if it doesn't exist yet
@ -224,7 +228,7 @@ func (p *importer) typ() types.Type {
p.record(t)
// read underlying type
t0.SetUnderlying(p.typ())
t0.SetUnderlying(p.typ(parent))
// interfaces don't have associated methods
if _, ok := t0.Underlying().(*types.Interface); ok {
@ -239,7 +243,7 @@ func (p *importer) typ() types.Type {
result, _ := p.paramList()
p.int() // read and discard index of inlined function body
sig := types.NewSignature(recv.At(0), params, result, isddd)
t0.AddMethod(types.NewFunc(token.NoPos, tpkg, name, sig))
t0.AddMethod(types.NewFunc(token.NoPos, parent, name, sig))
}
return t
@ -249,21 +253,21 @@ func (p *importer) typ() types.Type {
p.record(t)
n := p.int64()
*t = *types.NewArray(p.typ(), n)
*t = *types.NewArray(p.typ(parent), n)
return t
case sliceTag:
t := new(types.Slice)
p.record(t)
*t = *types.NewSlice(p.typ())
*t = *types.NewSlice(p.typ(parent))
return t
case dddTag:
t := new(dddSlice)
p.record(t)
t.elem = p.typ()
t.elem = p.typ(parent)
return t
case structTag:
@ -274,7 +278,7 @@ func (p *importer) typ() types.Type {
fields := make([]*types.Var, n)
tags := make([]string, n)
for i := range fields {
fields[i] = p.field()
fields[i] = p.field(parent)
tags[i] = p.string()
}
*t = *types.NewStruct(fields, tags)
@ -284,7 +288,7 @@ func (p *importer) typ() types.Type {
t := new(types.Pointer)
p.record(t)
*t = *types.NewPointer(p.typ())
*t = *types.NewPointer(p.typ(parent))
return t
case signatureTag:
@ -312,7 +316,7 @@ func (p *importer) typ() types.Type {
// read methods
methods := make([]*types.Func, p.int())
for i := range methods {
pkg, name := p.fieldName()
pkg, name := p.fieldName(parent)
params, isddd := p.paramList()
result, _ := p.paramList()
sig := types.NewSignature(nil, params, result, isddd)
@ -327,8 +331,8 @@ func (p *importer) typ() types.Type {
t := new(types.Map)
p.record(t)
key := p.typ()
val := p.typ()
key := p.typ(parent)
val := p.typ(parent)
*t = *types.NewMap(key, val)
return t
@ -348,7 +352,7 @@ func (p *importer) typ() types.Type {
default:
panic(fmt.Sprintf("unexpected channel dir %d", d))
}
val := p.typ()
val := p.typ(parent)
*t = *types.NewChan(dir, val)
return t
@ -357,18 +361,18 @@ func (p *importer) typ() types.Type {
}
}
func (p *importer) field() *types.Var {
pkg, name := p.fieldName()
typ := p.typ()
func (p *importer) field(parent *types.Package) *types.Var {
pkg, name := p.fieldName(parent)
typ := p.typ(parent)
anonymous := false
if name == "" {
// anonymous field - typ must be T or *T and T must be a type name
switch typ := deref(typ).(type) {
case *types.Basic: // basic types are named types
pkg = nil // // objects defined in Universe scope have no package
name = typ.Name()
case *types.Named:
pkg = p.pkgList[0]
name = typ.Obj().Name()
default:
panic("anonymous field expected")
@ -379,15 +383,20 @@ func (p *importer) field() *types.Var {
return types.NewField(token.NoPos, pkg, name, typ, anonymous)
}
func (p *importer) fieldName() (*types.Package, string) {
func (p *importer) fieldName(parent *types.Package) (*types.Package, string) {
pkg := parent
if pkg == nil {
// use the imported package instead
pkg = p.pkgList[0]
}
name := p.string()
if name == "" {
return nil, "" // anonymous field
return pkg, "" // anonymous
}
pkg := p.pkgList[0]
if name == "?" || name != "_" && !exported(name) {
// explicitly qualified field
if name == "?" {
name = ""
name = "" // anonymous
}
pkg = p.pkg()
}
@ -415,7 +424,7 @@ func (p *importer) paramList() (*types.Tuple, bool) {
}
func (p *importer) param(named bool) (*types.Var, bool) {
t := p.typ()
t := p.typ(nil)
td, isddd := t.(*dddSlice)
if isddd {
t = types.NewSlice(td.elem)

View file

@ -35,11 +35,10 @@ var pkgExts = [...]string{".a", ".o"}
// If no file was found, an empty filename is returned.
//
func FindPkg(path, srcDir string) (filename, id string) {
if len(path) == 0 {
if path == "" {
return
}
id = path
var noext string
switch {
default:
@ -50,6 +49,7 @@ func FindPkg(path, srcDir string) (filename, id string) {
return
}
noext = strings.TrimSuffix(bp.PkgObj, ".a")
id = bp.ImportPath
case build.IsLocalImport(path):
// "./x" -> "/this/directory/x.ext", "/this/directory/x"
@ -61,6 +61,13 @@ func FindPkg(path, srcDir string) (filename, id string) {
// does not support absolute imports
// "/x" -> "/x.ext", "/x"
noext = path
id = path
}
if false { // for debugging
if path != id {
fmt.Printf("%s -> %s\n", path, id)
}
}
// try extensions
@ -107,27 +114,16 @@ func ImportData(packages map[string]*types.Package, filename, id string, data io
return
}
// Import imports a gc-generated package given its import path, adds the
// corresponding package object to the packages map, and returns the object.
// Local import paths are interpreted relative to the current working directory.
// Import imports a gc-generated package given its import path and srcDir, adds
// the corresponding package object to the packages map, and returns the object.
// The packages map must contain all packages already imported.
//
func Import(packages map[string]*types.Package, path string) (pkg *types.Package, err error) {
// package "unsafe" is handled by the type checker
if path == "unsafe" {
panic(`gcimporter.Import called for package "unsafe"`)
}
srcDir := "."
if build.IsLocalImport(path) {
srcDir, err = os.Getwd()
if err != nil {
return
}
}
func Import(packages map[string]*types.Package, path, srcDir string) (pkg *types.Package, err error) {
filename, id := FindPkg(path, srcDir)
if filename == "" {
if path == "unsafe" {
return types.Unsafe, nil
}
err = fmt.Errorf("can't find import: %s", id)
return
}
@ -417,11 +413,11 @@ func (p *parser) parseBasicType() types.Type {
// ArrayType = "[" int_lit "]" Type .
//
func (p *parser) parseArrayType() types.Type {
func (p *parser) parseArrayType(parent *types.Package) types.Type {
// "[" already consumed and lookahead known not to be "]"
lit := p.expect(scanner.Int)
p.expect(']')
elem := p.parseType()
elem := p.parseType(parent)
n, err := strconv.ParseInt(lit, 10, 64)
if err != nil {
p.error(err)
@ -431,35 +427,43 @@ func (p *parser) parseArrayType() types.Type {
// MapType = "map" "[" Type "]" Type .
//
func (p *parser) parseMapType() types.Type {
func (p *parser) parseMapType(parent *types.Package) types.Type {
p.expectKeyword("map")
p.expect('[')
key := p.parseType()
key := p.parseType(parent)
p.expect(']')
elem := p.parseType()
elem := p.parseType(parent)
return types.NewMap(key, elem)
}
// Name = identifier | "?" | QualifiedName .
//
// For unqualified names, the returned package is the imported package.
// For unqualified and anonymous names, the returned package is the parent
// package unless parent == nil, in which case the returned package is the
// package being imported. (The parent package is not nil if the the name
// is an unqualified struct field or interface method name belonging to a
// type declared in another package.)
//
// For qualified names, the returned package is nil (and not created if
// it doesn't exist yet) unless materializePkg is set (which creates an
// unnamed package). In the latter case, a subequent import clause is
// expected to provide a name for the package.
// unnamed package with valid package path). In the latter case, a
// subequent import clause is expected to provide a name for the package.
//
func (p *parser) parseName(materializePkg bool) (pkg *types.Package, name string) {
func (p *parser) parseName(parent *types.Package, materializePkg bool) (pkg *types.Package, name string) {
pkg = parent
if pkg == nil {
pkg = p.sharedPkgs[p.id]
}
switch p.tok {
case scanner.Ident:
pkg = p.sharedPkgs[p.id]
name = p.lit
p.next()
case '?':
// anonymous
pkg = p.sharedPkgs[p.id]
p.next()
case '@':
// exported name prefixed with package path
pkg = nil
var id string
id, name = p.parseQualifiedName()
if materializePkg {
@ -480,15 +484,15 @@ func deref(typ types.Type) types.Type {
// Field = Name Type [ string_lit ] .
//
func (p *parser) parseField() (*types.Var, string) {
pkg, name := p.parseName(true)
typ := p.parseType()
func (p *parser) parseField(parent *types.Package) (*types.Var, string) {
pkg, name := p.parseName(parent, true)
typ := p.parseType(parent)
anonymous := false
if name == "" {
// anonymous field - typ must be T or *T and T must be a type name
switch typ := deref(typ).(type) {
case *types.Basic: // basic types are named types
pkg = nil
pkg = nil // objects defined in Universe scope have no package
name = typ.Name()
case *types.Named:
name = typ.Obj().Name()
@ -512,7 +516,7 @@ func (p *parser) parseField() (*types.Var, string) {
// StructType = "struct" "{" [ FieldList ] "}" .
// FieldList = Field { ";" Field } .
//
func (p *parser) parseStructType() types.Type {
func (p *parser) parseStructType(parent *types.Package) types.Type {
var fields []*types.Var
var tags []string
@ -522,7 +526,7 @@ func (p *parser) parseStructType() types.Type {
if i > 0 {
p.expect(';')
}
fld, tag := p.parseField()
fld, tag := p.parseField(parent)
if tag != "" && tags == nil {
tags = make([]string, i)
}
@ -539,7 +543,7 @@ func (p *parser) parseStructType() types.Type {
// Parameter = ( identifier | "?" ) [ "..." ] Type [ string_lit ] .
//
func (p *parser) parseParameter() (par *types.Var, isVariadic bool) {
_, name := p.parseName(false)
_, name := p.parseName(nil, false)
// remove gc-specific parameter numbering
if i := strings.Index(name, "·"); i >= 0 {
name = name[:i]
@ -548,7 +552,7 @@ func (p *parser) parseParameter() (par *types.Var, isVariadic bool) {
p.expectSpecial("...")
isVariadic = true
}
typ := p.parseType()
typ := p.parseType(nil)
if isVariadic {
typ = types.NewSlice(typ)
}
@ -611,7 +615,7 @@ func (p *parser) parseSignature(recv *types.Var) *types.Signature {
// by the compiler and thus embedded interfaces are never
// visible in the export data.
//
func (p *parser) parseInterfaceType() types.Type {
func (p *parser) parseInterfaceType(parent *types.Package) types.Type {
var methods []*types.Func
p.expectKeyword("interface")
@ -620,7 +624,7 @@ func (p *parser) parseInterfaceType() types.Type {
if i > 0 {
p.expect(';')
}
pkg, name := p.parseName(true)
pkg, name := p.parseName(parent, true)
sig := p.parseSignature(nil)
methods = append(methods, types.NewFunc(token.NoPos, pkg, name, sig))
}
@ -633,7 +637,7 @@ func (p *parser) parseInterfaceType() types.Type {
// ChanType = ( "chan" [ "<-" ] | "<-" "chan" ) Type .
//
func (p *parser) parseChanType() types.Type {
func (p *parser) parseChanType(parent *types.Package) types.Type {
dir := types.SendRecv
if p.tok == scanner.Ident {
p.expectKeyword("chan")
@ -646,7 +650,7 @@ func (p *parser) parseChanType() types.Type {
p.expectKeyword("chan")
dir = types.RecvOnly
}
elem := p.parseType()
elem := p.parseType(parent)
return types.NewChan(dir, elem)
}
@ -661,24 +665,24 @@ func (p *parser) parseChanType() types.Type {
// PointerType = "*" Type .
// FuncType = "func" Signature .
//
func (p *parser) parseType() types.Type {
func (p *parser) parseType(parent *types.Package) types.Type {
switch p.tok {
case scanner.Ident:
switch p.lit {
default:
return p.parseBasicType()
case "struct":
return p.parseStructType()
return p.parseStructType(parent)
case "func":
// FuncType
p.next()
return p.parseSignature(nil)
case "interface":
return p.parseInterfaceType()
return p.parseInterfaceType(parent)
case "map":
return p.parseMapType()
return p.parseMapType(parent)
case "chan":
return p.parseChanType()
return p.parseChanType(parent)
}
case '@':
// TypeName
@ -689,19 +693,19 @@ func (p *parser) parseType() types.Type {
if p.tok == ']' {
// SliceType
p.next()
return types.NewSlice(p.parseType())
return types.NewSlice(p.parseType(parent))
}
return p.parseArrayType()
return p.parseArrayType(parent)
case '*':
// PointerType
p.next()
return types.NewPointer(p.parseType())
return types.NewPointer(p.parseType(parent))
case '<':
return p.parseChanType()
return p.parseChanType(parent)
case '(':
// "(" Type ")"
p.next()
typ := p.parseType()
typ := p.parseType(parent)
p.expect(')')
return typ
}
@ -783,7 +787,8 @@ func (p *parser) parseConstDecl() {
var typ0 types.Type
if p.tok != '=' {
typ0 = p.parseType()
// constant types are never structured - no need for parent type
typ0 = p.parseType(nil)
}
p.expect('=')
@ -857,7 +862,7 @@ func (p *parser) parseTypeDecl() {
// structure, but throw it away if the object already has a type.
// This ensures that all imports refer to the same type object for
// a given type declaration.
typ := p.parseType()
typ := p.parseType(pkg)
if name := obj.Type().(*types.Named); name.Underlying() == nil {
name.SetUnderlying(typ)
@ -869,7 +874,7 @@ func (p *parser) parseTypeDecl() {
func (p *parser) parseVarDecl() {
p.expectKeyword("var")
pkg, name := p.parseExportedName()
typ := p.parseType()
typ := p.parseType(pkg)
pkg.Scope().Insert(types.NewVar(token.NoPos, pkg, name, typ))
}
@ -905,7 +910,7 @@ func (p *parser) parseMethodDecl() {
base := deref(recv.Type()).(*types.Named)
// parse method name, signature, and possibly inlined body
_, name := p.parseName(false)
_, name := p.parseName(nil, false)
sig := p.parseFunc(recv)
// methods always belong to the same package as the base type object

View file

@ -60,9 +60,9 @@ func compileNewExport(t *testing.T, dirname, filename string) string {
return filepath.Join(dirname, filename[:len(filename)-2]+"o")
}
func testPath(t *testing.T, path string) *types.Package {
func testPath(t *testing.T, path, srcDir string) *types.Package {
t0 := time.Now()
pkg, err := Import(make(map[string]*types.Package), path)
pkg, err := Import(make(map[string]*types.Package), path, srcDir)
if err != nil {
t.Errorf("testPath(%s): %s", path, err)
return nil
@ -90,7 +90,7 @@ func testDir(t *testing.T, dir string, endTime time.Time) (nimports int) {
for _, ext := range pkgExts {
if strings.HasSuffix(f.Name(), ext) {
name := f.Name()[0 : len(f.Name())-len(ext)] // remove extension
if testPath(t, filepath.Join(dir, name)) != nil {
if testPath(t, filepath.Join(dir, name), dir) != nil {
nimports++
}
}
@ -113,7 +113,7 @@ func TestImportTestdata(t *testing.T) {
defer os.Remove(outFn)
}
if pkg := testPath(t, "./testdata/exports"); pkg != nil {
if pkg := testPath(t, "./testdata/exports", "."); pkg != nil {
// The package's Imports list must include all packages
// explicitly imported by exports.go, plus all packages
// referenced indirectly via exported objects in exports.go.
@ -143,7 +143,7 @@ func TestImportTestdataNewExport(t *testing.T) {
defer os.Remove(outFn)
}
if pkg := testPath(t, "./testdata/exports"); pkg != nil {
if pkg := testPath(t, "./testdata/exports", "."); pkg != nil {
// The package's Imports list must include all packages
// explicitly imported by exports.go, plus all packages
// referenced indirectly via exported objects in exports.go.
@ -200,7 +200,7 @@ func TestImportedTypes(t *testing.T) {
importPath := s[0]
objName := s[1]
pkg, err := Import(make(map[string]*types.Package), importPath)
pkg, err := Import(make(map[string]*types.Package), importPath, ".")
if err != nil {
t.Error(err)
continue
@ -228,7 +228,7 @@ func TestIssue5815(t *testing.T) {
return
}
pkg, err := Import(make(map[string]*types.Package), "strings")
pkg, err := Import(make(map[string]*types.Package), "strings", ".")
if err != nil {
t.Fatal(err)
}
@ -262,7 +262,7 @@ func TestCorrectMethodPackage(t *testing.T) {
}
imports := make(map[string]*types.Package)
_, err := Import(imports, "net/http")
_, err := Import(imports, "net/http", ".")
if err != nil {
t.Fatal(err)
}
@ -299,7 +299,7 @@ func TestIssue13566(t *testing.T) {
}
// import must succeed (test for issue at hand)
pkg, err := Import(make(map[string]*types.Package), "./testdata/b")
pkg, err := Import(make(map[string]*types.Package), "./testdata/b", ".")
if err != nil {
t.Fatal(err)
}
@ -311,3 +311,53 @@ func TestIssue13566(t *testing.T) {
}
}
}
func TestIssue13898(t *testing.T) {
skipSpecialPlatforms(t)
// This package only handles gc export data.
if runtime.Compiler != "gc" {
t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
return
}
// import go/internal/gcimporter which imports go/types partially
imports := make(map[string]*types.Package)
_, err := Import(imports, "go/internal/gcimporter", ".")
if err != nil {
t.Fatal(err)
}
// look for go/types package
var goTypesPkg *types.Package
for path, pkg := range imports {
if path == "go/types" {
goTypesPkg = pkg
break
}
}
if goTypesPkg == nil {
t.Fatal("go/types not found")
}
// look for go/types.Object type
obj := goTypesPkg.Scope().Lookup("Object")
if obj == nil {
t.Fatal("go/types.Object not found")
}
typ, ok := obj.Type().(*types.Named)
if !ok {
t.Fatalf("go/types.Object type is %v; wanted named type", typ)
}
// lookup go/types.Object.Pkg method
m, _, _ := types.LookupFieldOrMethod(typ, false, nil, "Pkg")
if m == nil {
t.Fatal("go/types.Object.Pkg not found")
}
// the method must belong to go/types
if m.Pkg().Path() != "go/types" {
t.Fatalf("found %v; want go/types", m.Pkg())
}
}

View file

@ -51,16 +51,42 @@ func (err Error) Error() string {
return fmt.Sprintf("%s: %s", err.Fset.Position(err.Pos), err.Msg)
}
// An importer resolves import paths to Packages.
// See go/importer for existing implementations.
// An Importer resolves import paths to Packages.
//
// CAUTION: This interface does not support the import of locally
// vendored packages. See https://golang.org/s/go15vendor.
// If possible, external implementations should implement ImporterFrom.
type Importer interface {
// Import returns the imported package for the given import
// path, or an error if the package couldn't be imported.
// Import is responsible for returning the same package for
// matching import paths.
// Two calls to Import with the same path return the same
// package.
Import(path string) (*Package, error)
}
// ImportMode is reserved for future use.
type ImportMode int
// An ImporterFrom resolves import paths to packages; it
// supports vendoring per https://golang.org/s/go15vendor.
// Use go/importer to obtain an ImporterFrom implementation.
type ImporterFrom interface {
// Importer is present for backward-compatibility. Calling
// Import(path) is the same as calling ImportFrom(path, "", 0);
// i.e., locally vendored packages may not be found.
// The types package does not call Import if an ImporterFrom
// is present.
Importer
// ImportFrom returns the imported package for the given import
// path when imported by the package in srcDir, or an error
// if the package couldn't be imported. The mode value must
// be 0; it is reserved for future use.
// Two calls to ImportFrom with the same path and srcDir return
// the same package.
ImportFrom(path, srcDir string, mode ImportMode) (*Package, error)
}
// A Config specifies the configuration for type checking.
// The zero value for Config is a ready-to-use default configuration.
type Config struct {
@ -86,9 +112,12 @@ type Config struct {
// error found.
Error func(err error)
// Importer is called for each import declaration except when
// importing package "unsafe". An error is reported if an
// importer is needed but none was installed.
// An importer is used to import packages referred to from
// import declarations.
// If the installed importer implements ImporterFrom, the type
// checker calls ImportFrom instead of Import.
// The type checker reports an error if an importer is needed
// but none was installed.
Importer Importer
// If Sizes != nil, it provides the sizing functions for package unsafe.

View file

@ -11,6 +11,7 @@ import (
"go/ast"
"go/importer"
"go/parser"
"internal/testenv"
"sort"
"strings"
"testing"
@ -204,3 +205,90 @@ L7 uses var z int`
t.Errorf("Unexpected defs/uses\ngot:\n%s\nwant:\n%s", got, want)
}
}
// This tests that the package associated with the types.Object.Pkg method
// is the type's package independent of the order in which the imports are
// listed in the sources src1, src2 below.
// The actual issue is in go/internal/gcimporter which has a corresponding
// test; we leave this test here to verify correct behavior at the go/types
// level.
func TestIssue13898(t *testing.T) {
testenv.MustHaveGoBuild(t)
const src0 = `
package main
import "go/types"
func main() {
var info types.Info
for _, obj := range info.Uses {
_ = obj.Pkg()
}
}
`
// like src0, but also imports go/importer
const src1 = `
package main
import (
"go/types"
_ "go/importer"
)
func main() {
var info types.Info
for _, obj := range info.Uses {
_ = obj.Pkg()
}
}
`
// like src1 but with different import order
// (used to fail with this issue)
const src2 = `
package main
import (
_ "go/importer"
"go/types"
)
func main() {
var info types.Info
for _, obj := range info.Uses {
_ = obj.Pkg()
}
}
`
f := func(test, src string) {
f, err := parser.ParseFile(fset, "", src, 0)
if err != nil {
t.Fatal(err)
}
cfg := Config{Importer: importer.Default()}
info := Info{Uses: make(map[*ast.Ident]Object)}
_, err = cfg.Check("main", fset, []*ast.File{f}, &info)
if err != nil {
t.Fatal(err)
}
var pkg *Package
count := 0
for id, obj := range info.Uses {
if id.Name == "Pkg" {
pkg = obj.Pkg()
count++
}
}
if count != 1 {
t.Fatalf("%s: got %d entries named Pkg; want 1", test, count)
}
if pkg.Name() != "types" {
t.Fatalf("%s: got %v; want package types", test, pkg)
}
}
f("src0", src0)
f("src1", src1)
f("src2", src2)
}

View file

@ -9,7 +9,6 @@ import (
"go/ast"
"go/constant"
"go/token"
pathLib "path"
"strconv"
"strings"
"unicode"
@ -134,6 +133,20 @@ func (check *Checker) collectObjects() {
pkgImports[imp] = true
}
// srcDir is the directory used by the Importer to look up packages.
// The typechecker itself doesn't need this information so it is not
// explicitly provided. Instead, we extract it from position info of
// the source files as needed.
// This is the only place where the type-checker (just the importer)
// needs to know the actual source location of a file.
// TODO(gri) can we come up with a better API instead?
var srcDir string
if len(check.files) > 0 {
// FileName may be "" (typically for tests) in which case
// we get "." as the srcDir which is what we would want.
srcDir = dir(check.fset.Position(check.files[0].Name.Pos()).Filename)
}
for fileNo, file := range check.files {
// The package identifier denotes the current package,
// but there is no corresponding package object.
@ -170,17 +183,20 @@ func (check *Checker) collectObjects() {
// TODO(gri) shouldn't create a new one each time
imp = NewPackage("C", "C")
imp.fake = true
} else if path == "unsafe" {
// package "unsafe" is known to the language
imp = Unsafe
} else {
if importer := check.conf.Importer; importer != nil {
// ordinary import
if importer := check.conf.Importer; importer == nil {
err = fmt.Errorf("Config.Importer not installed")
} else if importerFrom, ok := importer.(ImporterFrom); ok {
imp, err = importerFrom.ImportFrom(path, srcDir, 0)
if imp == nil && err == nil {
err = fmt.Errorf("Config.Importer.ImportFrom(%s, %s, 0) returned nil but no error", path, pkg.path)
}
} else {
imp, err = importer.Import(path)
if imp == nil && err == nil {
err = fmt.Errorf("Config.Importer.Import(%s) returned nil but no error", path)
}
} else {
err = fmt.Errorf("Config.Importer not installed")
}
if err != nil {
check.errorf(s.Path.Pos(), "could not import %s (%s)", path, err)
@ -435,7 +451,7 @@ func (check *Checker) unusedImports() {
// since _ identifiers are not entered into scopes.
if !obj.used {
path := obj.imported.path
base := pathLib.Base(path)
base := pkgName(path)
if obj.name == base {
check.softErrorf(obj.pos, "%q imported but not used", path)
} else {
@ -453,3 +469,25 @@ func (check *Checker) unusedImports() {
}
}
}
// pkgName returns the package name (last element) of an import path.
func pkgName(path string) string {
if i := strings.LastIndex(path, "/"); i >= 0 {
path = path[i+1:]
}
return path
}
// dir makes a good-faith attempt to return the directory
// portion of path. If path is empty, the result is ".".
// (Per the go/build package dependency tests, we cannot import
// path/filepath and simply use filepath.Dir.)
func dir(path string) string {
if i := strings.LastIndexAny(path, "/\\"); i >= 0 {
path = path[:i]
}
if path == "" {
path = "."
}
return path
}

Some files were not shown because too many files have changed in this diff Show more